SimplesitemapSettingsForm.php 16.8 KB
Newer Older
1 2
<?php

gbyte.co's avatar
gbyte.co committed
3
namespace Drupal\simple_sitemap\Form;
4

5
use Symfony\Component\DependencyInjection\ContainerInterface;
6
use Drupal\Core\Form\FormStateInterface;
7
use Drupal\simple_sitemap\Simplesitemap;
8
use Drupal\Component\Utility\UrlHelper;
9 10
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Database\Connection;
11 12

/**
gbyte.co's avatar
gbyte.co committed
13
 * Class SimplesitemapSettingsForm
gbyte.co's avatar
gbyte.co committed
14
 * @package Drupal\simple_sitemap\Form
15
 */
gbyte.co's avatar
gbyte.co committed
16
class SimplesitemapSettingsForm extends SimplesitemapFormBase {
17

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
  /**
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * @var \Drupal\Core\Database\Connection
   */
  protected $db;

  /**
   * SimplesitemapSettingsForm constructor.
   * @param \Drupal\simple_sitemap\Simplesitemap $generator
   * @param \Drupal\simple_sitemap\Form\FormHelper $form_helper
   * @param \Drupal\Core\Language\LanguageManager $language_manager
   * @param \Drupal\Core\Database\Connection $database
   */
  public function __construct(
    Simplesitemap $generator,
    FormHelper $form_helper,
    LanguageManager $language_manager,
    Connection $database
  ) {
    parent::__construct(
      $generator,
      $form_helper
    );
    $this->languageManager = $language_manager;
    $this->db = $database;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('simple_sitemap.generator'),
      $container->get('simple_sitemap.form_helper'),
      $container->get('language_manager'),
      $container->get('database')
    );
  }

61 62 63
  /**
   * {@inheritdoc}
   */
gbyte.co's avatar
gbyte.co committed
64
  public function getFormId() {
gbyte.co's avatar
gbyte.co committed
65
    return 'simple_sitemap_settings_form';
66 67 68 69 70 71
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
72

gbyte.co's avatar
gbyte.co committed
73
    $form['simple_sitemap_settings']['#prefix'] = FormHelper::getDonationText();
74
    $form['simple_sitemap_settings']['#attached']['library'][] = 'simple_sitemap/sitemapSettings';
75
    $queue_worker = $this->generator->getQueueWorker();
76

77
    $form['simple_sitemap_settings']['status'] = [
78
      '#type' => 'fieldset',
79
      '#title' => $this->t('Sitemap status'),
gbyte.co's avatar
gbyte.co committed
80
      '#markup' => '<div class="description">' . $this->t('Sitemaps can be regenerated on demand here.') . '</div>',
81
      '#description' => $this->t('Variants can be configured <a href="@url">here</a>.', ['@url' => $GLOBALS['base_url'] . '/admin/config/search/simplesitemap/variants']),
82
    ];
83

84 85 86 87 88
    $form['simple_sitemap_settings']['status']['actions'] = [
      '#prefix' => '<div class="clearfix"><div class="form-item">',
      '#suffix' => '</div></div>',
    ];

89
    $form['simple_sitemap_settings']['status']['actions']['rebuild_queue_submit'] = [
90
      '#type' => 'submit',
91 92
      '#value' => $this->t('Rebuild queue'),
      '#submit' => ['::rebuildQueue'],
gbyte.co's avatar
gbyte.co committed
93
      '#validate' => [],
94
    ];
95

96
    $form['simple_sitemap_settings']['status']['actions']['regenerate_submit'] = [
97
      '#type' => 'submit',
98 99 100 101
      '#value' => $queue_worker->generationInProgress()
        ? $this->t('Resume generation')
        : $this->t('Rebuild queue & generate'),
      '#submit' => ['::generateSitemap'],
102 103 104
      '#validate' => [],
    ];

105
    $form['simple_sitemap_settings']['status']['progress'] = [
106
      '#prefix' => '<div class="clearfix">',
107 108 109
      '#suffix' => '</div>',
    ];

gbyte.co's avatar
gbyte.co committed
110 111
    $form['simple_sitemap_settings']['status']['progress']['title']['#markup'] = $this->t('Progress of sitemap regeneration');

112
    $total_count = $queue_worker->getInitialElementCount();
113 114 115 116 117 118 119 120 121 122
    if (!empty($total_count)) {
      $indexed_count = $queue_worker->getProcessedElementCount();
      $percent = round(100 * $indexed_count / $total_count);

      // With all results processed, there still may be some stashed results to be indexed.
      $percent = $percent === 100 && $queue_worker->generationInProgress() ? 99 : $percent;

      $index_progress = [
        '#theme' => 'progress_bar',
        '#percent' => $percent,
123
        '#message' => $this->t('@indexed out of @total items have been processed.<br>Each sitemap variant is published after all of its items have been processed.', ['@indexed' => $indexed_count, '@total' => $total_count]),
124
      ];
gbyte.co's avatar
gbyte.co committed
125
      $form['simple_sitemap_settings']['status']['progress']['bar']['#markup'] = render($index_progress);
126
    }
127
    else {
gbyte.co's avatar
gbyte.co committed
128
      $form['simple_sitemap_settings']['status']['progress']['bar']['#markup'] = '<div class="description">' . $this->t('There are no items to be indexed.') . '</div>';
129 130 131
    }

    $sitemap_manager = $this->generator->getSitemapManager();
132 133 134 135
    $sitemap_settings = [
      'base_url' => $this->generator->getSetting('base_url', ''),
      'default_variant' => $this->generator->getSetting('default_variant', NULL),
    ];
136 137 138
    $sitemap_statuses = $this->fetchSitemapInstanceStatuses();
    foreach ($sitemap_manager->getSitemapTypes() as $type_name => $type_definition) {
      if (!empty($variants = $sitemap_manager->getSitemapVariants($type_name, FALSE))) {
139 140 141 142
        $sitemap_generator = $sitemap_manager
          ->getSitemapGenerator($type_definition['sitemapGenerator'])
          ->setSettings($sitemap_settings);

143 144 145 146
        $form['simple_sitemap_settings']['status']['types'][$type_name] = [
          '#type' => 'details',
          '#title' => '<em>' . $type_definition['label'] . '</em> ' . $this->t('sitemaps'),
          '#open' => !empty($variants) && count($variants) <= 5,
147
          '#description' => !empty($type_definition['description']) ? '<div class="description">' . $type_definition['description'] . '</div>' : '',
148 149 150 151
        ];
        $form['simple_sitemap_settings']['status']['types'][$type_name]['table'] = [
          '#type' => 'table',
          '#header' => [$this->t('Variant'), $this->t('Status'), /*$this->t('Actions')*/],
152
          '#attributes' => ['class' => ['form-item', 'clearfix']],
153 154 155
        ];
        foreach ($variants as $variant_name => $variant_definition) {
          $row = [];
156
          $row['name']['data']['#markup'] = '<span title="' . $variant_name . '">' . $this->t($variant_definition['label']) . '</span>';
157 158 159 160 161 162 163 164 165
          if (!isset($sitemap_statuses[$variant_name])) {
            $row['status'] = $this->t('pending');
          }
          else {
            switch ($sitemap_statuses[$variant_name]) {
              case 0:
                $row['status'] = $this->t('generating');
                break;
              case 1:
166 167 168
                $row['status']['data']['#markup'] = $this->t('<a href="@url" target="_blank">published</a>',
                  ['@url' => $sitemap_generator->setSitemapVariant($variant_name)->getSitemapUrl()]
                );
169 170
                break;
              case 2:
171 172 173
                $row['status'] = $this->t('<a href="@url" target="_blank">published</a>, regenerating',
                  ['@url' => $sitemap_generator->setSitemapVariant($variant_name)->getSitemapUrl()]
                );
174 175 176 177 178 179 180 181 182 183
                break;
            }
          }

//          $row['actions'] = '';
          $form['simple_sitemap_settings']['status']['types'][$type_name]['table']['#rows'][$variant_name] = $row;
          unset($sitemap_statuses[$variant_name]);
        }
      }
    }
gbyte.co's avatar
gbyte.co committed
184 185 186
    if (empty($form['simple_sitemap_settings']['status']['types'])) {
      $form['simple_sitemap_settings']['status']['types']['#markup'] = $this->t('No variants have been defined');
    }
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206

/*    if (!empty($sitemap_statuses)) {
      $form['simple_sitemap_settings']['status']['types']['&orphans'] = [
        '#type' => 'details',
        '#title' => $this->t('Orphans'),
        '#open' => TRUE,
      ];

      $form['simple_sitemap_settings']['status']['types']['&orphans']['table'] = [
        '#type' => 'table',
        '#header' => [$this->t('Variant'), $this->t('Status'), $this->t('Actions')],
      ];
      foreach ($sitemap_statuses as $orphan_name => $orphan_info) {
        $form['simple_sitemap_settings']['status']['types']['&orphans']['table']['#rows'][$orphan_name] = [
          'name' => $orphan_name,
          'status' => $this->t('orphaned'),
          'actions' => '',
        ];
      }
    }*/
207

208
    $form['simple_sitemap_settings']['settings'] = [
209
      '#type' => 'fieldset',
210
      '#title' => $this->t('Settings'),
211
    ];
212

213
    $form['simple_sitemap_settings']['settings']['cron_generate'] = [
gbyte.co's avatar
gbyte.co committed
214
      '#type' => 'checkbox',
215 216
      '#title' => $this->t('Regenerate the sitemaps during cron runs'),
      '#description' => $this->t('Uncheck this if you intend to only regenerate the sitemaps manually or via drush.'),
217
      '#default_value' => $this->generator->getSetting('cron_generate', TRUE),
218
    ];
gbyte.co's avatar
gbyte.co committed
219

220 221 222 223 224
    $form['simple_sitemap_settings']['settings']['cron_generate_interval'] = [
      '#type' => 'select',
      '#title' => $this->t('Sitemap generation interval'),
      '#description' => $this->t('The sitemap will be generated according to this interval.'),
      '#default_value' => $this->generator->getSetting('cron_generate_interval', 0),
gbyte.co's avatar
gbyte.co committed
225
      '#options' => FormHelper::getCronIntervalOptions(),
226
      '#states' => [
gbyte.co's avatar
gbyte.co committed
227
        'visible' => [':input[name="cron_generate"]' => ['checked' => TRUE]],
228 229 230
      ],
    ];

231 232
    $form['simple_sitemap_settings']['settings']['xsl'] = [
      '#type' => 'checkbox',
233 234
      '#title' => $this->t('Add styling and sorting to sitemaps'),
      '#description' => $this->t('If checked, sitemaps will be displayed as tables with sortable entries and thus become much friendlier towards human visitors. Search engines will not care.'),
235 236 237
      '#default_value' => $this->generator->getSetting('xsl', TRUE),
    ];

238 239 240 241 242 243 244 245 246
    $form['simple_sitemap_settings']['settings']['languages'] = [
      '#type' => 'details',
      '#title' => $this->t('Language settings'),
      '#open' => FALSE,
    ];

    $form['simple_sitemap_settings']['settings']['languages']['skip_untranslated'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Skip non-existent translations'),
247
      '#description' => $this->t('If checked, entity links are generated exclusively for languages the entity has been translated to as long as the language is not excluded below.<br>Otherwise entity links are generated for every language installed on the site apart from languages excluded below.<br>Bear in mind that non-entity paths like homepage will always be generated for every non-excluded language.'),
248 249 250
      '#default_value' => $this->generator->getSetting('skip_untranslated', FALSE),
    ];

251 252 253 254 255 256
    $language_options = [];
    foreach ($this->languageManager->getLanguages() as $language) {
      if (!$language->isDefault()) {
        $language_options[$language->getId()] = $language->getName();
      }
    }
257 258 259 260 261 262 263 264 265 266
    $form['simple_sitemap_settings']['settings']['languages']['excluded_languages'] = [
      '#title' => $this->t('Exclude languages'),
      '#type' => 'checkboxes',
      '#options' => $language_options,
      '#description' => !empty($language_options)
        ? $this->t('There will be no links generated for languages checked here.')
        : $this->t('There are no languages other than the default language <a href="@url">available</a>.', ['@url' => $GLOBALS['base_url'] . '/admin/config/regional/language']),
      '#default_value' => $this->generator->getSetting('excluded_languages', []),
    ];

267
    $form['simple_sitemap_settings']['advanced'] = [
268
      '#type' => 'details',
269
      '#title' => $this->t('Advanced settings'),
270
      '#open' => TRUE,
271
    ];
gbyte.co's avatar
gbyte.co committed
272

273
    $variants = $this->generator->getSitemapManager()->getSitemapVariants(NULL, FALSE);
274
    $default_variant = $this->generator->getSetting('default_variant');
275 276 277
    $form['simple_sitemap_settings']['advanced']['default_variant'] = [
      '#type' => 'select',
      '#title' => $this->t('Default sitemap variant'),
278
      '#description' => $this->t('This sitemap variant will be available under <em>/sitemap.xml</em> in addition to its default path <em>/variant-name/sitemap.xml</em>.<br>Variants can be configured <a href="@url">here</a>.', ['@url' => $GLOBALS['base_url'] . '/admin/config/search/simplesitemap/variants']),
279
      '#default_value' => isset($variants[$default_variant]) ? $default_variant : '',
280
      '#options' => ['' => $this->t('- None -')] + array_map(function($variant) { return $this->t($variant['label']); }, $variants),
281 282
      ];

283 284 285 286 287
    $form['simple_sitemap_settings']['advanced']['base_url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Default base URL'),
      '#default_value' => $this->generator->getSetting('base_url', ''),
      '#size' => 30,
288
      '#description' => $this->t('On some hosting providers it is impossible to pass parameters to cron to tell Drupal which URL to bootstrap with. In this case the base URL of sitemap links can be overridden here.<br>Example: <em>@url</em>', ['@url' => $GLOBALS['base_url']]),
289 290
    ];

291
    $form['simple_sitemap_settings']['advanced']['remove_duplicates'] = [
gbyte.co's avatar
gbyte.co committed
292
      '#type' => 'checkbox',
293
      '#title' => $this->t('Exclude duplicate links'),
294
      '#description' => $this->t('Prevent per-sitemap variant duplicate links.<br>Uncheck this to significantly speed up the sitemap generation process on a huge site (more than 20 000 indexed entities).'),
295
      '#default_value' => $this->generator->getSetting('remove_duplicates', TRUE),
296 297
    ];

298
    $form['simple_sitemap_settings']['advanced']['max_links'] = [
299
      '#type' => 'number',
300
      '#title' => $this->t('Maximum links in a sitemap'),
301
      '#min' => 1,
302
      '#description' => $this->t('The maximum number of links one sitemap can hold. If more links are generated than set here, a sitemap index will be created and the links split into several sub-sitemaps.<br>50 000 links is the maximum Google will parse per sitemap, but an equally important consideration is generation performance: Splitting sitemaps into chunks <em>greatly</em> increases it.<br>If left blank, all links will be shown on a single sitemap.'),
303
      '#default_value' => $this->generator->getSetting('max_links'),
304
    ];
305

306
    $form['simple_sitemap_settings']['advanced']['generate_duration'] = [
307
      '#type' => 'number',
308
      '#title' => $this->t('Sitemap generation max duration'),
309
      '#min' => 1,
310
      '#description' => $this->t('The maximum duration <strong>in seconds</strong> the generation task can run during a single cron run or during one batch process iteration.<br>The higher the number, the quicker the generation process, but higher the risk of PHP timeout errors.'),
311
      '#default_value' => $this->generator->getSetting('generate_duration', 10000) / 1000,
gbyte.co's avatar
gbyte.co committed
312
      '#required' => TRUE,
313
    ];
314

315
    $this->formHelper->displayRegenerateNow($form['simple_sitemap_settings']);
gbyte.co's avatar
gbyte.co committed
316

317 318 319
    return parent::buildForm($form, $form_state);
  }

320 321 322 323 324 325 326 327 328
  /**
   * @return array
   *  Array of sitemap statuses keyed by variant name.
   *  Status values:
   *  0: Instance is unpublished
   *  1: Instance is published
   *  2: Instance is published but is being regenerated
   */
  protected function fetchSitemapInstanceStatuses() {
329
    $results = $this->db
330 331 332 333 334 335 336 337 338 339 340 341 342
      ->query('SELECT type, status FROM {simple_sitemap} GROUP BY type, status')
      ->fetchAll();

    $instances = [];
    foreach ($results as $i => $result) {
      $instances[$result->type] = isset($instances[$result->type])
        ? $result->status + 1
        : (int) $result->status;
    }

    return $instances;
  }

343 344 345
  /**
   * {@inheritdoc}
   */
346 347 348
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $base_url = $form_state->getValue('base_url');
    $form_state->setValue('base_url', rtrim($base_url, '/'));
gbyte.co's avatar
gbyte.co committed
349
    if ($base_url !== '' && !UrlHelper::isValid($base_url, TRUE)) {
gbyte.co's avatar
gbyte.co committed
350
      $form_state->setErrorByName('base_url', $this->t('The base URL is invalid.'));
351 352
    }
  }
353 354 355 356 357

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
358 359 360 361 362
    foreach (['max_links',
               'cron_generate',
               'cron_generate_interval',
               'remove_duplicates',
               'skip_untranslated',
363
               'xsl',
364 365
               'base_url',
               'default_variant'] as $setting_name) {
366
      $this->generator->saveSetting($setting_name, $form_state->getValue($setting_name));
gbyte.co's avatar
gbyte.co committed
367
    }
368
    $this->generator->saveSetting('excluded_languages', array_filter($form_state->getValue('excluded_languages')));
369
    $this->generator->saveSetting('generate_duration', $form_state->getValue('generate_duration') * 1000);
370

371
    parent::submitForm($form, $form_state);
gbyte.co's avatar
gbyte.co committed
372 373 374

    // Regenerate sitemaps according to user setting.
    if ($form_state->getValue('simple_sitemap_regenerate_now')) {
375 376 377
      $this->generator->setVariants(TRUE)
        ->rebuildQueue()
        ->generateSitemap();
gbyte.co's avatar
gbyte.co committed
378
    }
379 380
  }

gbyte.co's avatar
gbyte.co committed
381 382 383
  /**
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
gbyte.co's avatar
gbyte.co committed
384
   * @throws \Drupal\Component\Plugin\Exception\PluginException
gbyte.co's avatar
gbyte.co committed
385
   */
386
  public function generateSitemap(array &$form, FormStateInterface $form_state) {
gbyte.co's avatar
gbyte.co committed
387
    $this->generator->generateSitemap();
388 389 390 391 392
  }

  /**
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
gbyte.co's avatar
gbyte.co committed
393
   * @throws \Drupal\Component\Plugin\Exception\PluginException
394 395 396
   */
  public function rebuildQueue(array &$form, FormStateInterface $form_state) {
    $this->generator->rebuildQueue();
397
  }
gbyte.co's avatar
gbyte.co committed
398

399
}