3 namespace Drupal\layouter\Form;
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;
12 use Drupal\file\Entity\File;
13 use Drupal\file\FileInterface;
16 * Provides multistep ajax form for an layout choice.
18 class LayouterForm extends FormBase {
21 * The steps count of multiform.
28 * All layouter templates invoked by hook_layouter_templates_info.
37 public function __construct() {
38 $this->templates = \Drupal::moduleHandler()
39 ->invokeAll('layouter_templates_info');
45 public function getFormId() {
46 return 'layouter_multistep_form';
52 public function buildForm(array $form, FormStateInterface $form_state, $textarea_id = NULL) {
53 $step = $form_state->get('step');
56 $form_state->set('step', $step);
59 $form['#prefix'] = '<div id="layouter-form-wrapper" class="layouter-form">';
60 $form['#sufix'] = '</div>';
66 foreach ($this->templates as $id => $params) {
67 $options[$id] = $params['title']->render();
70 $form['data']['type'] = [
71 '#title' => $this->t('Choose the layout'),
73 '#options' => $options,
75 '#after_build' => ['::processLayoutTypeRadios'],
78 $button_label = $this->t('Next');
81 $this->buildLayouterFields($form, $form_state);
82 $button_label = $this->t('Submit');
85 $form['actions']['submit'] = [
87 '#value' => $button_label,
88 '#name' => 'submit_button',
90 'class' => ['use-ajax-submit'],
93 'callback' => '::ajaxResponse',
96 $form['actions']['cancel'] = [
98 '#value' => $this->t('Cancel'),
99 '#name' => 'cancel_button',
100 '#submit' => ['::cancelForm'],
101 '#limit_validation_errors' => [],
103 'class' => ['use-ajax-submit'],
113 public function submitForm(array &$form, FormStateInterface $form_state) {
114 $step = $form_state->get('step');
115 if ($step < $this->steps) {
116 $form_state->setRebuild();
118 $form_state->set('type', $form_state->getValue('type'));
122 $form_state->set('step', $step);
126 * Ajax callback prints rebuilded form.
129 * @param \Drupal\Core\Form\FormStateInterface $form_state
131 * @return \Drupal\Core\Ajax\AjaxResponse
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 />';
144 $form_state->clearErrors();
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);
153 $command = new CloseModalDialogCommand();
154 $response->addCommand($command);
155 $command = new InvokeCommand(
157 'layouterAddContent',
158 [$textarea_id, $content]
160 $response->addCommand($command);
163 $command = new ReplaceCommand('#layouter-form-wrapper', $form);
164 $response->addCommand($command);
170 * Form submit handler triggered by 'cancel' button. Closes popup form.
173 * @param \Drupal\Core\Form\FormStateInterface $form_state
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();
187 $response = new AjaxResponse();
188 $response->addCommand(new CloseModalDialogCommand());
189 $form_state->setResponse($response);
193 * Wraps each radio button item in the 'radios' set into additional container.
194 * After-build callback.
197 * $element - form element.
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>';
213 * Returns the 'textarea' form item.
215 * @param string $name
217 * @param array $params
218 * Additional parameters for field from layouter template.
221 * Renderable array for textfield.
223 private function textContentHandler($name, array $params) {
225 '#type' => 'textarea',
226 '#title' => $params['title'],
227 '#description' => $params['description'],
235 * Returns the form item with actual settings, to upload image.
237 * @param string $name
239 * @param array $params
240 * Additional parameters for field from layouter template.
243 * Renderable array for file field.
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'];
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])]
259 $image_field_description .= '<br />' .
261 'Allowed file types: @extensions.',
262 ['@extensions' => $allowed_extensions[0]]
264 if (!empty($params['description'])) {
265 $image_field_description .= '<br />' . $params['description'];
267 $location_scheme = \Drupal::config('layouter.settings')->get('uri_scheme');
269 // Add managed_file field and textfield for image alternative text.
271 '#type' => 'managed_file',
272 '#title' => $this->t('Image'),
273 '#field_name' => 'layouter_image',
274 '#description' => $image_field_description,
276 '#upload_location' => $location_scheme . '://layouter_images',
277 '#upload_validators' => [
278 'file_validate_extensions' => $allowed_extensions,
279 'file_validate_size' => [$max_upload_size],
282 $result[$name . '_alt'] = [
283 '#type' => 'textfield',
284 '#title' => $this->t('Alternative text'),
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;
297 $admin_image_style_description = '';
299 $image_styles = \Drupal::config('layouter.settings')->get('image_styles');
300 $image_styles_options['none'] = 'none';
301 foreach ($image_styles as $k => $v) {
303 $image_styles_options[$k] = $v;
307 // Add image style field to result.
308 $result[$name . '_style'] = [
310 '#title' => $this->t('Image style'),
312 '#options' => $image_styles_options,
313 '#description' => $admin_image_style_description,
316 $fieldset[$fieldset_name] = $result;
321 * Builds HTML that will be added to textarea.
323 * @param \Drupal\Core\Form\FormStateInterface $form_state
325 * Content for output.
327 private function buildResponseHtml(FormStateInterface $form_state) {
329 '#theme' => $this->templates[$form_state->get('type')]['theme'],
331 $fields = $form_state->get('fields');
332 foreach ($fields as $field_name => $fiels_params) {
333 switch ($fiels_params['type']) {
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') {
347 '#theme' => 'image_style',
348 '#style_name' => $image_style,
351 $image_content['#uri'] = $image->getFileUri();
352 $image_content['#alt'] = $form_state->getValue($field_name . '_alt');
353 $content['#' . $field_name] = render($image_content);
357 $content['#' . $field_name] = $form_state->getValue($field_name);
362 return render($content);
366 * Sets up and builds fields from selected layouter template.
369 * @param \Drupal\Core\Form\FormStateInterface $form_state
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);
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);
383 if ($params['type'] == 'text') {
384 $params['title'] = ($params['title']) ?: $this->t('Text');
385 $form['data'] += $this->textContentHandler($name, $params);