Version 1
[yaffs-website] / web / modules / contrib / layouter / src / Form / LayouterForm.php
1 <?php
2
3 namespace Drupal\layouter\Form;
4
5 use Drupal\Core\Ajax\AjaxResponse;
6 use Drupal\Core\Ajax\CloseModalDialogCommand;
7 use Drupal\Core\Ajax\InvokeCommand;
8 use Drupal\Core\Ajax\ReplaceCommand;
9 use Drupal\Core\Form\FormBase;
10 use Drupal\Core\Form\FormStateInterface;
11 use Drupal\Core\Url;
12 use Drupal\file\Entity\File;
13 use Drupal\file\FileInterface;
14
15 /**
16  * Provides multistep ajax form for an layout choice.
17  */
18 class LayouterForm extends FormBase {
19
20   /**
21    * The steps count of multiform.
22    *
23    * @var integer
24    */
25   protected $steps = 2;
26
27   /**
28    * All layouter templates invoked by hook_layouter_templates_info.
29    *
30    * @var array
31    */
32   protected $templates;
33
34   /**
35    * {@inheritdoc}
36    */
37   public function __construct() {
38     $this->templates = \Drupal::moduleHandler()
39       ->invokeAll('layouter_templates_info');
40   }
41
42   /**
43    * {@inheritdoc}
44    */
45   public function getFormId() {
46     return 'layouter_multistep_form';
47   }
48
49   /**
50    * {@inheritdoc}
51    */
52   public function buildForm(array $form, FormStateInterface $form_state, $textarea_id = NULL) {
53     $step = $form_state->get('step');
54     if (!$step) {
55       $step = 1;
56       $form_state->set('step', $step);
57     }
58
59     $form['#prefix'] = '<div id="layouter-form-wrapper" class="layouter-form">';
60     $form['#sufix'] = '</div>';
61     $form['errors'] = [];
62
63     $button_label = '';
64     if ($step == 1) {
65       $options = [];
66       foreach ($this->templates as $id => $params) {
67         $options[$id] = $params['title']->render();
68       }
69
70       $form['data']['type'] = [
71         '#title' => $this->t('Choose the layout'),
72         '#type' => 'radios',
73         '#options' => $options,
74         '#required' => TRUE,
75         '#after_build' => ['::processLayoutTypeRadios'],
76       ];
77
78       $button_label = $this->t('Next');
79     }
80     if ($step == 2) {
81       $this->buildLayouterFields($form, $form_state);
82       $button_label = $this->t('Submit');
83     }
84
85     $form['actions']['submit'] = [
86       '#type' => 'submit',
87       '#value' => $button_label,
88       '#name' => 'submit_button',
89       '#attributes' => [
90         'class' => ['use-ajax-submit'],
91       ],
92       '#ajax' => [
93         'callback' => '::ajaxResponse',
94       ],
95     ];
96     $form['actions']['cancel'] = [
97       '#type' => 'submit',
98       '#value' => $this->t('Cancel'),
99       '#name' => 'cancel_button',
100       '#submit' => ['::cancelForm'],
101       '#limit_validation_errors' => [],
102       '#attributes' => [
103         'class' => ['use-ajax-submit'],
104       ],
105     ];
106
107     return $form;
108   }
109
110   /**
111    * {@inheritdoc}
112    */
113   public function submitForm(array &$form, FormStateInterface $form_state) {
114     $step = $form_state->get('step');
115     if ($step < $this->steps) {
116       $form_state->setRebuild();
117       if ($step == 1) {
118         $form_state->set('type', $form_state->getValue('type'));
119       }
120     }
121     $step++;
122     $form_state->set('step', $step);
123   }
124
125   /**
126    * Ajax callback prints rebuilded form.
127    *
128    * @param array $form
129    * @param \Drupal\Core\Form\FormStateInterface $form_state
130    *
131    * @return \Drupal\Core\Ajax\AjaxResponse
132    */
133   public function ajaxResponse(array &$form, FormStateInterface $form_state) {
134     if ($form_state->hasAnyErrors()) {
135       $form['errors']['#prefix'] = '<div class="messages messages--error">';
136       $form['errors']['#suffix'] = '</div>';
137       $form['errors']['#markup'] = '';
138       $mess = drupal_get_messages('error');
139       foreach ($mess as $errors) {
140         foreach ($errors as $error) {
141           $form['errors']['#markup'] .= $error . '<br />';
142         }
143       }
144       $form_state->clearErrors();
145     }
146
147     $step = $form_state->get('step');
148     $response = new AjaxResponse();
149     if ($step == $this->steps + 1 && $form_state->isExecuted()) {
150       $textarea_id = $form_state->getBuildInfo()['args'][0];
151       $content = $this->buildResponseHtml($form_state);
152
153       $command = new CloseModalDialogCommand();
154       $response->addCommand($command);
155       $command = new InvokeCommand(
156         NULL,
157         'layouterAddContent',
158         [$textarea_id, $content]
159       );
160       $response->addCommand($command);
161     }
162     else {
163       $command = new ReplaceCommand('#layouter-form-wrapper', $form);
164       $response->addCommand($command);
165     }
166     return $response;
167   }
168
169   /**
170    * Form submit handler triggered by 'cancel' button. Closes popup form.
171    *
172    * @param array $form
173    * @param \Drupal\Core\Form\FormStateInterface $form_state
174    */
175   public function cancelForm(array &$form, FormStateInterface $form_state) {
176     // Delete loaded files.
177     if ($form_state->hasFileElement()) {
178       foreach ($form_state->get('fields') as $name => $params) {
179         if ($params['type'] == 'image') {
180           $input = $form_state->getUserInput();
181           if (!empty($input[$name]['fids'])) {
182             File::load($input[$name]['fids'])->delete();
183           }
184         }
185       }
186     }
187     $response = new AjaxResponse();
188     $response->addCommand(new CloseModalDialogCommand());
189     $form_state->setResponse($response);
190   }
191
192   /**
193    * Wraps each radio button item in the 'radios' set into additional container.
194    * After-build callback.
195    *
196    * @param array
197    *   $element - form element.
198    *
199    * @return array
200    */
201   public function processLayoutTypeRadios($element) {
202     foreach ($this->templates as $id => $params) {
203       if (!empty($element[$id])) {
204         $element[$id]['#prefix'] = '<div class="layouter-radio-wrapper '
205           . $id . '" title="' . $params['title'] . '">';
206         $element[$id]['#suffix'] = '</div>';
207       }
208     }
209     return $element;
210   }
211
212   /**
213    * Returns the 'textarea' form item.
214    *
215    * @param string $name
216    *   Field name.
217    * @param array $params
218    *   Additional parameters for field from layouter template.
219    *
220    * @return array
221    *   Renderable array for textfield.
222    */
223   private function textContentHandler($name, array $params) {
224     $result[$name] = [
225       '#type' => 'textarea',
226       '#title' => $params['title'],
227       '#description' => $params['description'],
228       '#rows' => 10,
229       '#required' => 1,
230     ];
231     return $result;
232   }
233
234   /**
235    * Returns the form item with actual settings, to upload image.
236    *
237    * @param string $name
238    *   Field name.
239    * @param array $params
240    *   Additional parameters for field from layouter template.
241    *
242    * @return array
243    *   Renderable array for file field.
244    */
245   private function imageContentHandler($name, array $params) {
246     $fieldset_name = 'image_' . $name;
247     // Fieldset for image fields.
248     $result['#type'] = 'fieldset';
249     $result['#title'] = $params['title'];
250
251     // Prepare managed_file field.
252     $allowed_extensions = ['png gif jpeg jpg'];
253     $max_upload_size_mb = (int) ini_get('upload_max_filesize');
254     $max_upload_size = [$max_upload_size_mb * 1024 * 1024];
255     $image_field_description = $this->t(
256       'Files must be less than @size.',
257       ['@size' => format_size($max_upload_size[0])]
258     );
259     $image_field_description .= '<br />' .
260       $this->t(
261         'Allowed file types: @extensions.',
262         ['@extensions' => $allowed_extensions[0]]
263       );
264     if (!empty($params['description'])) {
265       $image_field_description .= '<br />' . $params['description'];
266     }
267     $location_scheme = \Drupal::config('layouter.settings')->get('uri_scheme');
268
269     // Add managed_file field and textfield for image alternative text.
270     $result[$name] = [
271       '#type' => 'managed_file',
272       '#title' => $this->t('Image'),
273       '#field_name' => 'layouter_image',
274       '#description' => $image_field_description,
275       '#required' => 1,
276       '#upload_location' => $location_scheme . '://layouter_images',
277       '#upload_validators' => [
278         'file_validate_extensions' => $allowed_extensions,
279         'file_validate_size' => [$max_upload_size],
280       ],
281     ];
282     $result[$name . '_alt'] = [
283       '#type' => 'textfield',
284       '#title' => $this->t('Alternative text'),
285     ];
286
287     // Prepare list field with allowed image styles.
288     if (\Drupal::currentUser()->hasPermission('administer image styles')) {
289       $url = Url::fromRoute('entity.image_style.collection')->getInternalPath();
290       $description = $this->t('You can also')
291         . ' <a href="/' . $url . '" target="_blank">'
292         . $this->t('add your own image style') . '</a> '
293         . $this->t('if you need to.');
294       $admin_image_style_description = $description;
295     }
296     else {
297       $admin_image_style_description = '';
298     }
299     $image_styles = \Drupal::config('layouter.settings')->get('image_styles');
300     $image_styles_options['none'] = 'none';
301     foreach ($image_styles as $k => $v) {
302       if ($v != '0') {
303         $image_styles_options[$k] = $v;
304       }
305     }
306
307     // Add image style field to result.
308     $result[$name . '_style'] = [
309       '#type' => 'select',
310       '#title' => $this->t('Image style'),
311       '#required' => TRUE,
312       '#options' => $image_styles_options,
313       '#description' => $admin_image_style_description,
314     ];
315
316     $fieldset[$fieldset_name] = $result;
317     return $fieldset;
318   }
319
320   /**
321    * Builds HTML that will be added to textarea.
322    *
323    * @param \Drupal\Core\Form\FormStateInterface $form_state
324    * @return null
325    *   Content for output.
326    */
327   private function buildResponseHtml(FormStateInterface $form_state) {
328     $content = [
329       '#theme' => $this->templates[$form_state->get('type')]['theme'],
330     ];
331     $fields = $form_state->get('fields');
332     foreach ($fields as $field_name => $fiels_params) {
333       switch ($fiels_params['type']) {
334         case 'image':
335           $image_fid = $form_state->getValue($field_name)[0];
336           $image = File::load($image_fid);
337           /** @var FileInterface $image */
338           $image->setPermanent();
339           $image_style = $form_state->getValue($field_name . '_style');
340           if ($image_style == 'none') {
341             $image_content = [
342               '#theme' => 'image',
343             ];
344           }
345           else {
346             $image_content = [
347               '#theme' => 'image_style',
348               '#style_name' => $image_style,
349             ];
350           }
351           $image_content['#uri'] = $image->getFileUri();
352           $image_content['#alt'] = $form_state->getValue($field_name . '_alt');
353           $content['#' . $field_name] = render($image_content);
354           break;
355
356         case 'text':
357           $content['#' . $field_name] = $form_state->getValue($field_name);
358           break;
359
360       }
361     }
362     return render($content);
363   }
364
365   /**
366    * Sets up and builds fields from selected layouter template.
367    *
368    * @param array $form
369    * @param \Drupal\Core\Form\FormStateInterface $form_state
370    */
371   private function buildLayouterFields(array &$form, FormStateInterface $form_state) {
372     $type = $form_state->get('type');
373     $fields = $this->templates[$type]['fields'];
374     if (!is_null($fields)) {
375       $form_state->set('fields', $fields);
376       $form['data'] = [];
377       foreach ($fields as $name => $params) {
378         $params['description'] = ($params['description']) ?: '';
379         if ($params['type'] == 'image') {
380           $params['title'] = ($params['title']) ?: $this->t('Image settings');
381           $form['data'] += $this->imageContentHandler($name, $params);
382         }
383         if ($params['type'] == 'text') {
384           $params['title'] = ($params['title']) ?: $this->t('Text');
385           $form['data'] += $this->textContentHandler($name, $params);
386         }
387       }
388     }
389   }
390
391 }