FormHelper.php 17 KB
Newer Older
1 2
<?php

3
namespace Drupal\simple_sitemap\Form;
4

Pawel G's avatar
Pawel G committed
5
use Drupal\Core\Form\FormStateInterface;
6
use Drupal\Core\StringTranslation\StringTranslationTrait;
7
use Drupal\simple_sitemap\EntityHelper;
Pawel G's avatar
Pawel G committed
8 9
use Drupal\simple_sitemap\Simplesitemap;
use Drupal\Core\Session\AccountProxyInterface;
10

11
/**
Pawel G's avatar
Pawel G committed
12
 * Class FormHelper
13
 * @package Drupal\simple_sitemap\Form
14
 */
15
class FormHelper {
16
  use StringTranslationTrait;
17

Pawel G's avatar
Pawel G committed
18 19 20 21
  const PRIORITY_DEFAULT = 0.5;
  const PRIORITY_HIGHEST = 10;
  const PRIORITY_DIVIDER = 10;

Pawel G's avatar
Pawel G committed
22 23 24
  /**
   * @var \Drupal\simple_sitemap\Simplesitemap
   */
25
  protected $generator;
Pawel G's avatar
Pawel G committed
26 27 28 29

  /**
   * @var \Drupal\simple_sitemap\EntityHelper
   */
30
  protected $entityHelper;
Pawel G's avatar
Pawel G committed
31 32 33 34

  /**
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
35
  protected $currentUser;
Pawel G's avatar
Pawel G committed
36 37 38 39

  /**
   * @var \Drupal\Core\Form\FormState
   */
40
  protected $formState;
41

Pawel G's avatar
Pawel G committed
42 43 44
  /**
   * @var string|null
   */
Pawel G's avatar
Pawel G committed
45
  protected $entityCategory;
Pawel G's avatar
Pawel G committed
46 47 48 49

  /**
   * @var string
   */
50
  protected $entityTypeId;
Pawel G's avatar
Pawel G committed
51 52 53 54

  /**
   * @var string
   */
55
  protected $bundleName;
Pawel G's avatar
Pawel G committed
56 57 58 59

  /**
   * @var string
   */
60
  protected $instanceId;
61

62 63 64 65 66 67 68 69 70 71
  /**
   * @var string
   */
  protected $variant;

  /**
   * @var array
   */
  protected $bundleSettings;

72
  protected static $allowedFormOperations = [
73 74 75
    'default',
    'edit',
    'add',
Pawel G's avatar
Pawel G committed
76
    'register',
77 78
  ];

79
  protected static $changefreqValues = [
80 81 82 83 84 85 86 87 88
    'always',
    'hourly',
    'daily',
    'weekly',
    'monthly',
    'yearly',
    'never',
  ];

89
  protected static $valuesToCheck = [
90
    'simple_sitemap_variant',
91 92
    'simple_sitemap_index_content',
    'simple_sitemap_priority',
93
    'simple_sitemap_changefreq',
94
    'simple_sitemap_include_images',
Pawel G's avatar
Pawel G committed
95
    'simple_sitemap_regenerate_now',
96
  ];
97

98
  /**
99 100 101 102
   * FormHelper constructor.
   * @param \Drupal\simple_sitemap\Simplesitemap $generator
   * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
103
   */
Pawel G's avatar
Pawel G committed
104 105
  public function __construct(
    Simplesitemap $generator,
106
    EntityHelper $entityHelper,
Pawel G's avatar
Pawel G committed
107 108
    AccountProxyInterface $current_user
  ) {
109
    $this->generator = $generator;
110
    $this->entityHelper = $entityHelper;
111
    $this->currentUser = $current_user;
112
  }
Pawel G's avatar
Pawel G committed
113

114
  /**
Pawel G's avatar
Pawel G committed
115 116
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   * @return bool
117
   */
Pawel G's avatar
Pawel G committed
118
  public function processForm(FormStateInterface $form_state) {
Pawel G's avatar
Pawel G committed
119
    $this->formState = $form_state;
120
    $this->cleanUpFormInfo();
121
    $this->getEntityDataFromFormEntity();
122
    $this->negotiateVariant();
123

Pawel G's avatar
Pawel G committed
124
    return $this->supports();
125 126
  }

127
  /**
Pawel G's avatar
Pawel G committed
128
   * @param string $entity_category
129 130
   * @return $this
   */
131 132
  public function setEntityCategory($entity_category) {
    $this->entityCategory = $entity_category;
133
    return $this;
134 135
  }

136
  /**
137
   * @return null|string
138
   */
139 140 141 142 143 144 145 146
  public function getEntityCategory() {
    return $this->entityCategory;
  }

  /**
 * @param string $entity_type_id
 * @return $this
 */
147 148
  public function setEntityTypeId($entity_type_id) {
    $this->entityTypeId = $entity_type_id;
149
    return $this;
150 151
  }

152 153 154 155 156 157 158
  /**
   * @return string
   */
  public function getEntityTypeId() {
    return $this->entityTypeId;
  }

159
  /**
Pawel G's avatar
Pawel G committed
160
   * @param string $bundle_name
161 162
   * @return $this
   */
163 164
  public function setBundleName($bundle_name) {
    $this->bundleName = $bundle_name;
165
    return $this;
166 167
  }

168 169 170 171 172 173 174
  /**
   * @return string
   */
  public function getBundleName() {
    return $this->bundleName;
  }

175
  /**
Pawel G's avatar
Pawel G committed
176
   * @param string $instance_id
177 178
   * @return $this
   */
179 180
  public function setInstanceId($instance_id) {
    $this->instanceId = $instance_id;
181
    return $this;
182 183
  }

184 185 186 187 188 189 190
  /**
   * @return string
   */
  public function getInstanceId() {
    return $this->instanceId;
  }

Pawel G's avatar
Pawel G committed
191
  /**
Pawel G's avatar
Pawel G committed
192
   * @return bool
Pawel G's avatar
Pawel G committed
193
   */
Pawel G's avatar
Pawel G committed
194
  protected function supports() {
Pawel G's avatar
Pawel G committed
195 196

    // Do not alter the form if user lacks certain permissions.
Pawel G's avatar
Pawel G committed
197
    if (!$this->currentUser->hasPermission('administer sitemap settings')) {
Pawel G's avatar
Pawel G committed
198
      return FALSE;
Pawel G's avatar
Pawel G committed
199
    }
Pawel G's avatar
Pawel G committed
200 201

    // Do not alter the form if it is irrelevant to sitemap generation.
202
    elseif (empty($this->getEntityCategory())) {
Pawel G's avatar
Pawel G committed
203
      return FALSE;
Pawel G's avatar
Pawel G committed
204
    }
Pawel G's avatar
Pawel G committed
205 206

    // Do not alter the form if entity is not enabled in sitemap settings.
207
    elseif (!$this->generator->entityTypeIsEnabled($this->getEntityTypeId())) {
Pawel G's avatar
Pawel G committed
208
      return FALSE;
Pawel G's avatar
Pawel G committed
209
    }
Pawel G's avatar
Pawel G committed
210 211 212

    // Do not alter the form, if sitemap is disabled for the entity type of this
    // entity instance.
213 214 215 216 217 218
    elseif ($this->getEntityCategory() === 'instance') {
      if (NULL === $this->variant || !$this->generator
          ->setVariants($this->variant)
          ->bundleIsIndexed($this->getEntityTypeId(), $this->getBundleName())) {
        return FALSE;
      }
Pawel G's avatar
Pawel G committed
219
    }
Pawel G's avatar
Pawel G committed
220 221

    return TRUE;
Pawel G's avatar
Pawel G committed
222 223
  }

224
  /**
Pawel G's avatar
Pawel G committed
225
   * @param array $form_fragment
226
   * @return $this
227
   */
Pawel G's avatar
Pawel G committed
228
  public function displayRegenerateNow(&$form_fragment) {
Pawel G's avatar
Pawel G committed
229
    $form_fragment['simple_sitemap_regenerate_now'] = [
230
      '#type' => 'checkbox',
231
      '#title' => $this->t('Regenerate sitemap after hitting <em>Save</em>'),
Pawel G's avatar
Pawel G committed
232
      '#description' => $this->t('This setting will regenerate all sitemaps including the above changes.'),
233
      '#default_value' => FALSE,
Pawel G's avatar
Pawel G committed
234
    ];
Pawel G's avatar
Pawel G committed
235
    if ($this->generator->getSetting('cron_generate')) {
236
      $form_fragment['simple_sitemap_regenerate_now']['#description'] .= '<br/>' . $this->t('Otherwise the sitemap will be regenerated during a future cron run.');
237
    }
238 239

    return $this;
240
  }
241

242 243 244 245
  /**
   * @return $this
   */
  public function negotiateVariant() {
246 247 248 249 250 251
    $all_bundle_settings = $this->generator->setVariants(TRUE)
      ->getBundleSettings($this->getEntityTypeId(), $this->getBundleName(), FALSE, TRUE);
    $this->bundleSettings = NULL !== ($variant = key($all_bundle_settings))
      ? $all_bundle_settings[$variant]
      : [];
    $this->variant = $variant;
252 253

    return $this;
254 255
  }

256
  /**
257
   * @param array $form_fragment
258 259
   * @param bool $multiple
   * @return $this
260 261
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
262
   */
263
  public function displayEntitySettings(&$form_fragment, $multiple = FALSE) {
264
    $prefix = $multiple ? $this->getEntityTypeId() . '_' : '';
265

266 267 268
    $settings = $this->getEntityCategory() === 'instance' && NULL !== $this->variant && NULL !== $this->getInstanceId()
      ? $this->generator->setVariants($this->variant)->getEntityInstanceSettings($this->getEntityTypeId(), $this->getInstanceId())
      : $this->bundleSettings;
269
    Simplesitemap::supplementDefaultSettings('entity', $settings);
Pawel G's avatar
Pawel G committed
270

271
    $bundle_name = !empty($this->getBundleName()) ? $this->getBundleName() : $this->t('undefined');
272

Pawel G's avatar
Pawel G committed
273
    // Index
274 275 276
    if (!$multiple) {
      $form_fragment[$prefix . 'simple_sitemap_index_content'] = [
        '#type' => 'radios',
277
        '#default_value' => (int) $settings['index'],
278
        '#options' => [
Pawel G's avatar
Pawel G committed
279 280 281 282 283 284
          0 => $this->getEntityCategory() === 'instance'
            ? $this->t('Do not index this @bundle entity', ['@bundle' => $bundle_name])
            : $this->t('Do not index entities of this type'),
          1 => $this->getEntityCategory() === 'instance'
            ? $this->t('Index this @bundle entity', ['@bundle' => $bundle_name])
            : $this->t('Index entities of this type'),
Pawel G's avatar
Pawel G committed
285
        ],
286
      ];
Pawel G's avatar
Pawel G committed
287

288 289
      if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['index'])) {
        $form_fragment[$prefix . 'simple_sitemap_index_content']['#options'][(int) $this->bundleSettings['index']] .= ' <em>(' . $this->t('default') . ')</em>';
290 291 292
      }
    }

293 294 295 296 297
    // Variant
    $form_fragment[$prefix . 'simple_sitemap_variant'] = [
      '#type' => 'select',
      '#title' => $this->t('Sitemap variant'),
      '#description' => $this->t('The sitemap variant entities of this type are to be indexed in.'),
298 299 300 301
      '#options' => array_map(
        function($variant) { return $this->t($variant['label']); },
        $this->generator->getSitemapManager()->getSitemapVariants(NULL, FALSE)
      ),
302 303 304 305 306 307 308 309 310 311
      '#states' => [
        'visible' => !$multiple
          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
        'required' => !$multiple // todo Should implement server side validation on top of this.
          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
      ],
      '#disabled' => $this->getEntityCategory() === 'instance'
    ];
312 313 314 315 316 317 318 319 320 321 322
    // If only one variant is available, set it, otherwise check if a default
    // variant is provided and set it.
    $form_fragment[$prefix . 'simple_sitemap_variant']['#default_value'] = NULL === $this->variant
      ? (1 === count($form_fragment[$prefix . 'simple_sitemap_variant']['#options'])
        ? array_keys($form_fragment[$prefix . 'simple_sitemap_variant']['#options'])[0]
        : (!empty($default = $this->generator->getSetting('default_variant'))
            ? $default
            : $this->variant
        )
      )
      : $this->variant;
323

Pawel G's avatar
Pawel G committed
324
    // Priority
325 326
    $form_fragment[$prefix . 'simple_sitemap_priority'] = [
      '#type' => 'select',
327
      '#title' => $this->t('Priority'),
328 329 330
      '#description' => $this->getEntityCategory() === 'instance'
        ? $this->t('The priority this @bundle entity will have in the eyes of search engine bots.', ['@bundle' => $bundle_name])
        : $this->t('The priority entities of this type will have in the eyes of search engine bots.'),
Pawel G's avatar
Pawel G committed
331
      '#default_value' => $settings['priority'],
332
      '#options' => $this->getPrioritySelectValues(),
333 334 335 336 337
      '#states' => [
        'visible' => !$multiple
          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
      ],
338
    ];
339

340 341
    if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['priority'])) {
      $form_fragment[$prefix . 'simple_sitemap_priority']['#options'][$this->formatPriority($this->bundleSettings['priority'])] .= ' (' . $this->t('default') . ')';
342
    }
343

Pawel G's avatar
Pawel G committed
344
    // Changefreq
345 346 347
    $form_fragment[$prefix . 'simple_sitemap_changefreq'] = [
      '#type' => 'select',
      '#title' => $this->t('Change frequency'),
348 349 350
      '#description' => $this->getEntityCategory() === 'instance'
      ? $this->t('The frequency with which this @bundle entity changes. Search engine bots may take this as an indication of how often to index it.', ['@bundle' => $bundle_name])
      : $this->t('The frequency with which entities of this type change. Search engine bots may take this as an indication of how often to index them.'),
Pawel G's avatar
Pawel G committed
351
      '#default_value' => $settings['changefreq'],
352
      '#options' => $this->getChangefreqSelectValues(),
353 354 355 356 357
      '#states' => [
        'visible' => !$multiple
          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
      ],
358 359
    ];

360 361
    if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['changefreq'])) {
      $form_fragment[$prefix . 'simple_sitemap_changefreq']['#options'][$this->bundleSettings['changefreq']] .= ' (' . $this->t('default') . ')';
362 363
    }

Pawel G's avatar
Pawel G committed
364
    // Images
365 366 367 368
    $form_fragment[$prefix . 'simple_sitemap_include_images'] = [
      '#type' => 'select',
      '#title' => $this->t('Include images'),
      '#description' => $this->getEntityCategory() === 'instance'
Pawel G's avatar
Pawel G committed
369 370
        ? $this->t('Determines if images referenced by this @bundle entity should be included in the sitemap.', ['@bundle' => $bundle_name])
        : $this->t('Determines if images referenced by entities of this type should be included in the sitemap.'),
371
      '#default_value' => (int) $settings['include_images'],
372
      '#options' => [0 => $this->t('No'), 1 => $this->t('Yes')],
373 374 375 376 377
      '#states' => [
        'visible' => !$multiple
          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
      ],
378 379
    ];

380 381
    if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['include_images'])) {
      $form_fragment[$prefix . 'simple_sitemap_include_images']['#options'][(int) $this->bundleSettings['include_images']] .= ' (' . $this->t('default') . ')';
382 383
    }

Pawel G's avatar
Pawel G committed
384
    return $this;
385 386 387 388 389 390 391
  }

  /**
   * Checks if this particular form is a bundle form, or a bundle instance form
   * and gathers sitemap settings from the database.
   *
   * @return bool
Pawel G's avatar
Pawel G committed
392
   *   TRUE if this is a bundle or bundle instance form, FALSE otherwise.
393
   */
394
  protected function getEntityDataFromFormEntity() {
Pawel G's avatar
Pawel G committed
395
    if (!$form_entity = $this->getFormEntity()) {
Pawel G's avatar
Pawel G committed
396 397
      return FALSE;
    }
398

Pawel G's avatar
Pawel G committed
399 400 401 402 403 404
    $entity_type_id = $form_entity->getEntityTypeId();
    $sitemap_entity_types = $this->entityHelper->getSupportedEntityTypes();
    if (isset($sitemap_entity_types[$entity_type_id])) {
      $this->setEntityCategory('instance');
    }
    else {
405
      /** @var \Drupal\Core\Entity\EntityType $sitemap_entity_type */
Pawel G's avatar
Pawel G committed
406 407 408
      foreach ($sitemap_entity_types as $sitemap_entity_type) {
        if ($sitemap_entity_type->getBundleEntityType() === $entity_type_id) {
          $this->setEntityCategory('bundle');
409
          break;
Pawel G's avatar
Pawel G committed
410
        }
411
      }
412
    }
Pawel G's avatar
Pawel G committed
413 414

    // Menu fix.
Pawel G's avatar
Pawel G committed
415
    $this->setEntityCategory(NULL === $this->getEntityCategory() && $entity_type_id === 'menu' ? 'bundle' : $this->getEntityCategory());
Pawel G's avatar
Pawel G committed
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434

    switch ($this->getEntityCategory()) {
      case 'bundle':
        $this->setEntityTypeId($this->entityHelper->getBundleEntityTypeId($form_entity));
        $this->setBundleName($form_entity->id());
        $this->setInstanceId(NULL);
        break;

      case 'instance':
        $this->setEntityTypeId($entity_type_id);
        $this->setBundleName($this->entityHelper->getEntityInstanceBundleName($form_entity));
        // New menu link's id is '' instead of NULL, hence checking for empty.
        $this->setInstanceId(!empty($form_entity->id()) ? $form_entity->id() : NULL);
        break;

      default:
        return FALSE;
    }
    return TRUE;
435 436 437 438 439
  }

  /**
   * Gets the object entity of the form if available.
   *
Pawel G's avatar
Pawel G committed
440
   * @return \Drupal\Core\Entity\Entity|false
Pawel G's avatar
Pawel G committed
441 442
   *   Entity or FALSE if non-existent or if form operation is
   *   'delete'.
443
   */
444
  protected function getFormEntity() {
445
    $form_object = $this->formState->getFormObject();
446
    if (NULL !== $form_object
447
      && method_exists($form_object, 'getOperation')
448
      && method_exists($form_object, 'getEntity')
449
      && in_array($form_object->getOperation(), self::$allowedFormOperations)) {
450 451 452 453 454
      return $form_object->getEntity();
    }
    return FALSE;
  }

455 456 457 458 459
  /**
   * Removes gathered form information from service object.
   *
   * Needed because this service may contain form info from the previous
   * operation when revived from the container.
460 461
   *
   * @return $this
462
   */
463
  public function cleanUpFormInfo() {
464 465 466 467
    $this->entityCategory = NULL;
    $this->entityTypeId = NULL;
    $this->bundleName = NULL;
    $this->instanceId = NULL;
468 469
    $this->variant = NULL;
    $this->bundleSettings = NULL;
470 471

    return $this;
472 473
  }

474 475 476 477
  /**
   * Gets new entity Id after entity creation.
   * To be used in an entity form submit.
   *
Pawel G's avatar
Pawel G committed
478
   * @return int
Pawel G's avatar
Pawel G committed
479
   *   Entity ID.
480
   */
Pawel G's avatar
Pawel G committed
481 482
  public function getFormEntityId() {
    return $this->formState->getFormObject()->getEntity()->id();
483 484 485 486 487 488
  }

  /**
   * Checks if simple_sitemap values have been changed after submitting the form.
   * To be used in an entity form submit.
   *
489
   * @param $form
490
   * @param array $values
Pawel G's avatar
Pawel G committed
491
   *
492
   * @return bool
Pawel G's avatar
Pawel G committed
493
   *   TRUE if simple_sitemap form values have been altered by the user.
494
   */
495
  public function valuesChanged($form, array $values) {
Pawel G's avatar
Pawel G committed
496
    foreach (self::$valuesToCheck as $field_name) {
497 498
      if (!isset($form['simple_sitemap'][$field_name]['#default_value'])
        || (isset($values[$field_name]) && $values[$field_name] != $form['simple_sitemap'][$field_name]['#default_value'])) {
499 500 501
        return TRUE;
      }
    }
502

503 504
    return FALSE;
  }
Pawel G's avatar
Pawel G committed
505 506 507 508

  /**
   * Gets the values needed to display the priority dropdown setting.
   *
509
   * @return array
510
   *   Select options.
Pawel G's avatar
Pawel G committed
511
   */
512
  public function getPrioritySelectValues() {
Pawel G's avatar
Pawel G committed
513
    $options = [];
Pawel G's avatar
Pawel G committed
514
    foreach (range(0, self::PRIORITY_HIGHEST) as $value) {
515
      $value = $this->formatPriority($value / self::PRIORITY_DIVIDER);
516
      $options[$value] = $value;
Pawel G's avatar
Pawel G committed
517
    }
518

Pawel G's avatar
Pawel G committed
519 520
    return $options;
  }
521

522 523 524 525 526 527 528
  /**
   * Gets the values needed to display the changefreq dropdown setting.
   *
   * @return array
   *   Select options.
   */
  public function getChangefreqSelectValues() {
Pawel G's avatar
Pawel G committed
529
    $options = ['' => $this->t('- Not specified -')];
Pawel G's avatar
Pawel G committed
530
    foreach (self::getChangefreqOptions() as $setting) {
Pawel G's avatar
Pawel G committed
531
      $options[$setting] = $this->t($setting);
532
    }
533

534 535 536 537 538 539 540
    return $options;
  }

  /**
   * @return array
   */
  public static function getChangefreqOptions() {
541
    return self::$changefreqValues;
542 543
  }

544
  /**
545
   * @param string $priority
546 547 548
   * @return string
   */
  public function formatPriority($priority) {
Pawel G's avatar
Pawel G committed
549
    return number_format((float) $priority, 1, '.', '');
550 551
  }

552
  /**
553
   * @param string|int $priority
554 555
   * @return bool
   */
556
  public static function isValidPriority($priority) {
557
    return is_numeric($priority) && $priority >= 0 && $priority <= 1;
558
  }
559 560 561 562 563 564

  /**
   * @param string $changefreq
   * @return bool
   */
  public static function isValidChangefreq($changefreq) {
565
    return in_array($changefreq, self::$changefreqValues);
566
  }
567
}