Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / path / src / Form / PathFormBase.php
1 <?php
2
3 namespace Drupal\path\Form;
4
5 use Drupal\Core\Form\FormBase;
6 use Drupal\Core\Form\FormStateInterface;
7 use Drupal\Core\Language\LanguageInterface;
8 use Drupal\Core\Path\AliasManagerInterface;
9 use Drupal\Core\Path\AliasStorageInterface;
10 use Drupal\Core\Path\PathValidatorInterface;
11 use Drupal\Core\Routing\RequestContext;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13
14 /**
15  * Provides a base class for path add/edit forms.
16  */
17 abstract class PathFormBase extends FormBase {
18
19   /**
20    * An array containing the path ID, source, alias, and language code.
21    *
22    * @var array
23    */
24   protected $path;
25
26   /**
27    * The path alias storage.
28    *
29    * @var \Drupal\Core\Path\AliasStorageInterface
30    */
31   protected $aliasStorage;
32
33   /**
34    * The path alias manager.
35    *
36    * @var \Drupal\Core\Path\AliasManagerInterface
37    */
38   protected $aliasManager;
39
40   /**
41    * The path validator.
42    *
43    * @var \Drupal\Core\Path\PathValidatorInterface
44    */
45   protected $pathValidator;
46
47   /**
48    * The request context.
49    *
50    * @var \Drupal\Core\Routing\RequestContext
51    */
52   protected $requestContext;
53
54   /**
55    * Constructs a new PathController.
56    *
57    * @param \Drupal\Core\Path\AliasStorageInterface $alias_storage
58    *   The path alias storage.
59    * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
60    *   The path alias manager.
61    * @param \Drupal\Core\Path\PathValidatorInterface $path_validator
62    *   The path validator.
63    * @param \Drupal\Core\Routing\RequestContext $request_context
64    *   The request context.
65    */
66   public function __construct(AliasStorageInterface $alias_storage, AliasManagerInterface $alias_manager, PathValidatorInterface $path_validator, RequestContext $request_context) {
67     $this->aliasStorage = $alias_storage;
68     $this->aliasManager = $alias_manager;
69     $this->pathValidator = $path_validator;
70     $this->requestContext = $request_context;
71   }
72
73   /**
74    * {@inheritdoc}
75    */
76   public static function create(ContainerInterface $container) {
77     return new static(
78       $container->get('path.alias_storage'),
79       $container->get('path.alias_manager'),
80       $container->get('path.validator'),
81       $container->get('router.request_context')
82     );
83   }
84
85   /**
86    * Builds the path used by the form.
87    *
88    * @param int|null $pid
89    *   Either the unique path ID, or NULL if a new one is being created.
90    */
91   abstract protected function buildPath($pid);
92
93   /**
94    * {@inheritdoc}
95    */
96   public function buildForm(array $form, FormStateInterface $form_state, $pid = NULL) {
97     $this->path = $this->buildPath($pid);
98     $form['source'] = [
99       '#type' => 'textfield',
100       '#title' => $this->t('Existing system path'),
101       '#default_value' => $this->path['source'],
102       '#maxlength' => 255,
103       '#size' => 45,
104       '#description' => $this->t('Specify the existing path you wish to alias. For example: /node/28, /forum/1, /taxonomy/term/1.'),
105       '#field_prefix' => $this->requestContext->getCompleteBaseUrl(),
106       '#required' => TRUE,
107     ];
108     $form['alias'] = [
109       '#type' => 'textfield',
110       '#title' => $this->t('Path alias'),
111       '#default_value' => $this->path['alias'],
112       '#maxlength' => 255,
113       '#size' => 45,
114       '#description' => $this->t('Specify an alternative path by which this data can be accessed. For example, type "/about" when writing an about page.'),
115       '#field_prefix' => $this->requestContext->getCompleteBaseUrl(),
116       '#required' => TRUE,
117     ];
118
119     // A hidden value unless language.module is enabled.
120     if (\Drupal::moduleHandler()->moduleExists('language')) {
121       $languages = \Drupal::languageManager()->getLanguages();
122       $language_options = [];
123       foreach ($languages as $langcode => $language) {
124         $language_options[$langcode] = $language->getName();
125       }
126
127       $form['langcode'] = [
128         '#type' => 'select',
129         '#title' => $this->t('Language'),
130         '#options' => $language_options,
131         '#empty_value' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
132         '#empty_option' => $this->t('- None -'),
133         '#default_value' => $this->path['langcode'],
134         '#weight' => -10,
135         '#description' => $this->t('A path alias set for a specific language will always be used when displaying this page in that language, and takes precedence over path aliases set as <em>- None -</em>.'),
136       ];
137     }
138     else {
139       $form['langcode'] = [
140         '#type' => 'value',
141         '#value' => $this->path['langcode']
142       ];
143     }
144
145     $form['actions'] = ['#type' => 'actions'];
146     $form['actions']['submit'] = [
147       '#type' => 'submit',
148       '#value' => $this->t('Save'),
149       '#button_type' => 'primary',
150     ];
151
152     return $form;
153   }
154
155   /**
156    * {@inheritdoc}
157    */
158   public function validateForm(array &$form, FormStateInterface $form_state) {
159     $source = &$form_state->getValue('source');
160     $source = $this->aliasManager->getPathByAlias($source);
161     $alias = &$form_state->getValue('alias');
162
163     // Trim the submitted value of whitespace and slashes. Ensure to not trim
164     // the slash on the left side.
165     $alias = rtrim(trim(trim($alias), ''), "\\/");
166
167     if ($source[0] !== '/') {
168       $form_state->setErrorByName('source', 'The source path has to start with a slash.');
169     }
170     if ($alias[0] !== '/') {
171       $form_state->setErrorByName('alias', 'The alias path has to start with a slash.');
172     }
173
174     // Language is only set if language.module is enabled, otherwise save for all
175     // languages.
176     $langcode = $form_state->getValue('langcode', LanguageInterface::LANGCODE_NOT_SPECIFIED);
177
178     if ($this->aliasStorage->aliasExists($alias, $langcode, $this->path['source'])) {
179       $stored_alias = $this->aliasStorage->load(['alias' => $alias, 'langcode' => $langcode]);
180       if ($stored_alias['alias'] !== $alias) {
181         // The alias already exists with different capitalization as the default
182         // implementation of AliasStorageInterface::aliasExists is
183         // case-insensitive.
184         $form_state->setErrorByName('alias', t('The alias %alias could not be added because it is already in use in this language with different capitalization: %stored_alias.', [
185           '%alias' => $alias,
186           '%stored_alias' => $stored_alias['alias'],
187         ]));
188       }
189       else {
190         $form_state->setErrorByName('alias', t('The alias %alias is already in use in this language.', ['%alias' => $alias]));
191       }
192     }
193
194     if (!$this->pathValidator->isValid(trim($source, '/'))) {
195       $form_state->setErrorByName('source', t("The path '@link_path' is either invalid or you do not have access to it.", ['@link_path' => $source]));
196     }
197   }
198
199   /**
200    * {@inheritdoc}
201    */
202   public function submitForm(array &$form, FormStateInterface $form_state) {
203     // Remove unnecessary values.
204     $form_state->cleanValues();
205
206     $pid = $form_state->getValue('pid', 0);
207     $source = $form_state->getValue('source');
208     $alias = $form_state->getValue('alias');
209     // Language is only set if language.module is enabled, otherwise save for all
210     // languages.
211     $langcode = $form_state->getValue('langcode', LanguageInterface::LANGCODE_NOT_SPECIFIED);
212
213     $this->aliasStorage->save($source, $alias, $langcode, $pid);
214
215     drupal_set_message($this->t('The alias has been saved.'));
216     $form_state->setRedirect('path.admin_overview');
217   }
218
219 }