FormHelper.php 17.4 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
      '#options' => $this->getVariantSelectValues(),
      '#default_value' => $this->getVariantSelectValuesDefault($this->variant),
300 301 302 303 304 305 306 307
      '#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]],
      ],
308
      '#disabled' => $this->getEntityCategory() === 'instance',
309 310
    ];

Pawel G's avatar
Pawel G committed
311
    // Priority
312 313
    $form_fragment[$prefix . 'simple_sitemap_priority'] = [
      '#type' => 'select',
314
      '#title' => $this->t('Priority'),
315 316 317
      '#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
318
      '#default_value' => $settings['priority'],
319
      '#options' => $this->getPrioritySelectValues(),
320 321 322 323 324
      '#states' => [
        'visible' => !$multiple
          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
      ],
325
    ];
326

327 328
    if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['priority'])) {
      $form_fragment[$prefix . 'simple_sitemap_priority']['#options'][$this->formatPriority($this->bundleSettings['priority'])] .= ' (' . $this->t('default') . ')';
329
    }
330

Pawel G's avatar
Pawel G committed
331
    // Changefreq
332 333 334
    $form_fragment[$prefix . 'simple_sitemap_changefreq'] = [
      '#type' => 'select',
      '#title' => $this->t('Change frequency'),
335 336 337
      '#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
338
      '#default_value' => $settings['changefreq'],
339
      '#options' => $this->getChangefreqSelectValues(),
340 341 342 343 344
      '#states' => [
        'visible' => !$multiple
          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
      ],
345 346
    ];

347 348
    if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['changefreq'])) {
      $form_fragment[$prefix . 'simple_sitemap_changefreq']['#options'][$this->bundleSettings['changefreq']] .= ' (' . $this->t('default') . ')';
349 350
    }

Pawel G's avatar
Pawel G committed
351
    // Images
352 353 354 355
    $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
356 357
        ? $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.'),
358
      '#default_value' => (int) $settings['include_images'],
359
      '#options' => [0 => $this->t('No'), 1 => $this->t('Yes')],
360 361 362 363 364
      '#states' => [
        'visible' => !$multiple
          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
      ],
365 366
    ];

367 368
    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') . ')';
369 370
    }

Pawel G's avatar
Pawel G committed
371
    return $this;
372 373 374 375 376 377 378
  }

  /**
   * 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
379
   *   TRUE if this is a bundle or bundle instance form, FALSE otherwise.
380
   */
381
  protected function getEntityDataFromFormEntity() {
Pawel G's avatar
Pawel G committed
382
    if (!$form_entity = $this->getFormEntity()) {
Pawel G's avatar
Pawel G committed
383 384
      return FALSE;
    }
385

Pawel G's avatar
Pawel G committed
386 387 388 389 390 391
    $entity_type_id = $form_entity->getEntityTypeId();
    $sitemap_entity_types = $this->entityHelper->getSupportedEntityTypes();
    if (isset($sitemap_entity_types[$entity_type_id])) {
      $this->setEntityCategory('instance');
    }
    else {
392
      /** @var \Drupal\Core\Entity\EntityType $sitemap_entity_type */
Pawel G's avatar
Pawel G committed
393 394 395
      foreach ($sitemap_entity_types as $sitemap_entity_type) {
        if ($sitemap_entity_type->getBundleEntityType() === $entity_type_id) {
          $this->setEntityCategory('bundle');
396
          break;
Pawel G's avatar
Pawel G committed
397
        }
398
      }
399
    }
Pawel G's avatar
Pawel G committed
400 401

    // Menu fix.
Pawel G's avatar
Pawel G committed
402
    $this->setEntityCategory(NULL === $this->getEntityCategory() && $entity_type_id === 'menu' ? 'bundle' : $this->getEntityCategory());
Pawel G's avatar
Pawel G committed
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

    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;
422 423 424 425 426
  }

  /**
   * Gets the object entity of the form if available.
   *
Pawel G's avatar
Pawel G committed
427
   * @return \Drupal\Core\Entity\Entity|false
Pawel G's avatar
Pawel G committed
428 429
   *   Entity or FALSE if non-existent or if form operation is
   *   'delete'.
430
   */
431
  protected function getFormEntity() {
432
    $form_object = $this->formState->getFormObject();
433
    if (NULL !== $form_object
434
      && method_exists($form_object, 'getOperation')
435
      && method_exists($form_object, 'getEntity')
436
      && in_array($form_object->getOperation(), self::$allowedFormOperations)) {
437 438 439 440 441
      return $form_object->getEntity();
    }
    return FALSE;
  }

442 443 444 445 446
  /**
   * Removes gathered form information from service object.
   *
   * Needed because this service may contain form info from the previous
   * operation when revived from the container.
447 448
   *
   * @return $this
449
   */
450
  public function cleanUpFormInfo() {
451 452 453 454
    $this->entityCategory = NULL;
    $this->entityTypeId = NULL;
    $this->bundleName = NULL;
    $this->instanceId = NULL;
455 456
    $this->variant = NULL;
    $this->bundleSettings = NULL;
457 458

    return $this;
459 460
  }

461 462 463 464
  /**
   * Gets new entity Id after entity creation.
   * To be used in an entity form submit.
   *
Pawel G's avatar
Pawel G committed
465
   * @return int
Pawel G's avatar
Pawel G committed
466
   *   Entity ID.
467
   */
Pawel G's avatar
Pawel G committed
468 469
  public function getFormEntityId() {
    return $this->formState->getFormObject()->getEntity()->id();
470 471 472 473 474 475
  }

  /**
   * Checks if simple_sitemap values have been changed after submitting the form.
   * To be used in an entity form submit.
   *
476
   * @param $form
477
   * @param array $values
Pawel G's avatar
Pawel G committed
478
   *
479
   * @return bool
Pawel G's avatar
Pawel G committed
480
   *   TRUE if simple_sitemap form values have been altered by the user.
481
   */
482
  public function valuesChanged($form, array $values) {
Pawel G's avatar
Pawel G committed
483
    foreach (self::$valuesToCheck as $field_name) {
484 485
      if (!isset($form['simple_sitemap'][$field_name]['#default_value'])
        || (isset($values[$field_name]) && $values[$field_name] != $form['simple_sitemap'][$field_name]['#default_value'])) {
486 487 488
        return TRUE;
      }
    }
489

490 491
    return FALSE;
  }
Pawel G's avatar
Pawel G committed
492

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
  /**
   * Gets the values needed to display the variant dropdown setting.
   *
   * @return array
   */
  public function getVariantSelectValues() {
    return array_map(
      function($variant) { return $this->t($variant['label']); },
      $this->generator->getSitemapManager()->getSitemapVariants(NULL, FALSE)
    );
  }

  /**
   * Returns correct default value for variant select list.
   *
   * If only one variant is available, return it, otherwise check if a default
   * variant is provided and return it.
   *
   * @param string|null $default_value
   *  Actual default value from the database.
   *
   * @return string|null
   *  Value to be set on form.
   */
  public function getVariantSelectValuesDefault($default_value) {
    $options = $this->getVariantSelectValues();
    return NULL === $default_value
      ? (1 === count($options)
        ? array_keys($options)[0]
        : (!empty($default = $this->generator->getSetting('default_variant'))
          ? $default
          : $default_value
        )
      )
      : $default_value;
  }

Pawel G's avatar
Pawel G committed
530 531 532
  /**
   * Gets the values needed to display the priority dropdown setting.
   *
533
   * @return array
Pawel G's avatar
Pawel G committed
534
   */
535
  public function getPrioritySelectValues() {
Pawel G's avatar
Pawel G committed
536
    $options = [];
Pawel G's avatar
Pawel G committed
537
    foreach (range(0, self::PRIORITY_HIGHEST) as $value) {
538
      $value = $this->formatPriority($value / self::PRIORITY_DIVIDER);
539
      $options[$value] = $value;
Pawel G's avatar
Pawel G committed
540
    }
541

Pawel G's avatar
Pawel G committed
542 543
    return $options;
  }
544

545 546 547 548 549 550
  /**
   * Gets the values needed to display the changefreq dropdown setting.
   *
   * @return array
   */
  public function getChangefreqSelectValues() {
Pawel G's avatar
Pawel G committed
551
    $options = ['' => $this->t('- Not specified -')];
Pawel G's avatar
Pawel G committed
552
    foreach (self::getChangefreqOptions() as $setting) {
Pawel G's avatar
Pawel G committed
553
      $options[$setting] = $this->t($setting);
554
    }
555

556 557 558 559 560 561 562
    return $options;
  }

  /**
   * @return array
   */
  public static function getChangefreqOptions() {
563
    return self::$changefreqValues;
564 565
  }

566
  /**
567
   * @param string $priority
568 569 570
   * @return string
   */
  public function formatPriority($priority) {
Pawel G's avatar
Pawel G committed
571
    return number_format((float) $priority, 1, '.', '');
572 573
  }

574
  /**
575
   * @param string|int $priority
576 577
   * @return bool
   */
578
  public static function isValidPriority($priority) {
579
    return is_numeric($priority) && $priority >= 0 && $priority <= 1;
580
  }
581 582 583 584 585 586

  /**
   * @param string $changefreq
   * @return bool
   */
  public static function isValidChangefreq($changefreq) {
587
    return in_array($changefreq, self::$changefreqValues);
588
  }
589
}