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

73
    $form['simple_sitemap_settings']['#prefix'] = $this->getDonationText();
74

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

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

    $form['simple_sitemap_settings']['status']['actions']['regenerate_submit'] = [
88
      '#type' => 'submit',
89
      '#value' => $this->t('Generate from queue'),
90
      '#submit' => ['::generateSitemap'],
gbyte.co's avatar
gbyte.co committed
91
      '#validate' => [],
92
    ];
93

94
//    $form['simple_sitemap_settings']['status']['actions']['regenerate_backend_submit'] = [
95 96 97 98 99 100
//      '#type' => 'submit',
//      '#value' => $this->t('Generate from queue (background)'),
//      '#submit' => ['::generateSitemapBackend'],
//      '#validate' => [],
//    ];

101
    $form['simple_sitemap_settings']['status']['actions']['rebuild_queue_submit'] = [
102 103 104 105 106 107
      '#type' => 'submit',
      '#value' => $this->t('Rebuild queue'),
      '#submit' => ['::rebuildQueue'],
      '#validate' => [],
    ];

108
    $form['simple_sitemap_settings']['status']['progress'] = [
109
      '#prefix' => '<div class="clearfix">',
110 111 112
      '#suffix' => '</div>',
    ];

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

115
    $queue_worker = $this->generator->getQueueWorker();
116
    $total_count = $queue_worker->getInitialElementCount();
117 118 119 120 121 122 123 124 125 126 127 128
    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,
        '#message' => t('@indexed out of @total items have been processed.', ['@indexed' => $indexed_count, '@total' => $total_count]),
      ];
gbyte.co's avatar
gbyte.co committed
129
      $form['simple_sitemap_settings']['status']['progress']['bar']['#markup'] = render($index_progress);
130
    }
131
    else {
gbyte.co's avatar
gbyte.co committed
132
      $form['simple_sitemap_settings']['status']['progress']['bar']['#markup'] = '<div class="description">' . $this->t('There are no items to be indexed.') . '</div>';
133 134 135 136 137 138 139 140 141 142 143
    }

    $sitemap_manager = $this->generator->getSitemapManager();
    $sitemap_statuses = $this->fetchSitemapInstanceStatuses();

    foreach ($sitemap_manager->getSitemapTypes() as $type_name => $type_definition) {
      if (!empty($variants = $sitemap_manager->getSitemapVariants($type_name, FALSE))) {
        $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,
144
          '#description' => !empty($type_definition['description']) ? '<div class="description">' . $type_definition['description'] . '</div>' : '',
145 146 147 148
        ];
        $form['simple_sitemap_settings']['status']['types'][$type_name]['table'] = [
          '#type' => 'table',
          '#header' => [$this->t('Variant'), $this->t('Status'), /*$this->t('Actions')*/],
149
          '#attributes' => ['class' => ['form-item', 'clearfix']],
150 151 152
        ];
        foreach ($variants as $variant_name => $variant_definition) {
          $row = [];
gbyte.co's avatar
gbyte.co committed
153
          $row['name']['data']['#markup'] = '<span title="' . $variant_name . '">' . $variant_definition['label'] . '</span>';
154 155 156 157 158 159 160 161 162 163 164 165 166
          if (!isset($sitemap_statuses[$variant_name])) {
            $row['status'] = $this->t('pending');
          }
          else {
            $url = $GLOBALS['base_url'] . '/' . $variant_name . '/sitemap.xml';
            switch ($sitemap_statuses[$variant_name]) {
              case 0:
                $row['status'] = $this->t('generating');
                break;
              case 1:
                $row['status']['data']['#markup'] = $this->t('<a href="@url" target="_blank">published</a>', ['@url' => $url]);
                break;
              case 2:
167
                $row['status'] = $this->t('<a href="@url" target="_blank">published</a>, regenerating', ['@url' => $url]);
168 169 170 171 172 173 174 175 176 177
                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
178 179 180
    if (empty($form['simple_sitemap_settings']['status']['types'])) {
      $form['simple_sitemap_settings']['status']['types']['#markup'] = $this->t('No variants have been defined');
    }
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

/*    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' => '',
        ];
      }
    }*/
201

202
    $form['simple_sitemap_settings']['settings'] = [
203
      '#type' => 'fieldset',
204
      '#title' => $this->t('Settings'),
205
    ];
206

207
    $form['simple_sitemap_settings']['settings']['cron_generate'] = [
gbyte.co's avatar
gbyte.co committed
208
      '#type' => 'checkbox',
209 210
      '#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.'),
211
      '#default_value' => $this->generator->getSetting('cron_generate', TRUE),
212
    ];
gbyte.co's avatar
gbyte.co committed
213

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    $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),
      '#options' => [
        0 => $this->t('On every cron run'),
        1 => $this->t('Once an hour'),
        3 => $this->t('Once every @hours hours', ['@hours' => 3]),
        6 => $this->t('Once every @hours hours', ['@hours' => 6]),
        12 => $this->t('Once every @hours hours', ['@hours' => 12]),
        24 => $this->t('Once a day'),
        48 => $this->t('Once every @days days', ['@days' => 48/24]),
        72 => $this->t('Once every @days days', ['@days' => 72/24]),
        96 => $this->t('Once every @days days', ['@days' => 96/24]),
        120 => $this->t('Once every @days days', ['@days' => 120/24]),
        144 => $this->t('Once every @days days', ['@days' => 144/24]),
        168 => $this->t('Once a week'),
      ],
      '#states' => [
        'visible' => [
          ':input[name="cron_generate"]' => ['checked' => TRUE],
        ],
      ],
    ];

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
    $form['simple_sitemap_settings']['settings']['languages'] = [
      '#type' => 'details',
      '#title' => $this->t('Language settings'),
      '#open' => FALSE,
    ];

    $language_options = [];
    foreach ($this->languageManager->getLanguages() as $language) {
      if (!$language->isDefault()) {
        $language_options[$language->getId()] = $language->getName();
      }
    }

    $form['simple_sitemap_settings']['settings']['languages']['skip_untranslated'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Skip non-existent translations'),
      '#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.'),
      '#default_value' => $this->generator->getSetting('skip_untranslated', FALSE),
    ];

    $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', []),
    ];

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

276
    $variants = $this->generator->getSitemapManager()->getSitemapVariants(NULL, FALSE);
277
    $default_variant = $this->generator->getSetting('default_variant');
278 279 280 281 282
    $form['simple_sitemap_settings']['advanced']['default_variant'] = [
      '#type' => 'select',
      '#title' => $this->t('Default sitemap variant'),
      '#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']),
      '#default_value' => isset($variants[$default_variant]) ? $default_variant : '',
283
      '#options' => ['' => $this->t('- None -')] + array_map(function($variant) { return $this->t($variant['label']); }, $variants),
284 285
      ];

286 287 288 289 290
    $form['simple_sitemap_settings']['advanced']['base_url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Default base URL'),
      '#default_value' => $this->generator->getSetting('base_url', ''),
      '#size' => 30,
291
      '#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']]),
292 293
    ];

294
    $form['simple_sitemap_settings']['advanced']['remove_duplicates'] = [
gbyte.co's avatar
gbyte.co committed
295
      '#type' => 'checkbox',
296
      '#title' => $this->t('Exclude duplicate links'),
297
      '#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).'),
298
      '#default_value' => $this->generator->getSetting('remove_duplicates', TRUE),
299 300
    ];

301
    $form['simple_sitemap_settings']['advanced']['max_links'] = [
302
      '#type' => 'number',
303
      '#title' => $this->t('Maximum links in a sitemap'),
304
      '#min' => 1,
305 306
      '#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.'),
      '#default_value' => $this->generator->getSetting('max_links'),
307
    ];
308

309
    $form['simple_sitemap_settings']['advanced']['generate_duration'] = [
310
      '#type' => 'number',
311
      '#title' => $this->t('Sitemap generation max duration'),
312
      '#min' => 1,
gbyte.co's avatar
gbyte.co committed
313
      '#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.'),
314
      '#default_value' => $this->generator->getSetting('generate_duration', 10000) / 1000,
gbyte.co's avatar
gbyte.co committed
315
      '#required' => TRUE,
316
    ];
317

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

320 321 322
    return parent::buildForm($form, $form_state);
  }

323 324 325 326 327 328 329 330 331
  /**
   * @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() {
332
    $results = $this->db
333 334 335 336 337 338 339 340 341 342 343 344 345
      ->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;
  }

346 347 348
  /**
   * {@inheritdoc}
   */
349 350 351
  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
352
    if ($base_url !== '' && !UrlHelper::isValid($base_url, TRUE)) {
353 354 355
      $form_state->setErrorByName('base_url', t('The base URL is invalid.'));
    }
  }
356 357 358 359 360

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

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

    // Regenerate sitemaps according to user setting.
    if ($form_state->getValue('simple_sitemap_regenerate_now')) {
gbyte.co's avatar
gbyte.co committed
377
      $this->generator->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
   */
  public function generateSitemapBackend (array &$form, FormStateInterface $form_state) {
gbyte.co's avatar
gbyte.co committed
396
    $this->generator->generateSitemap('backend');
397 398 399 400 401 402
  }


  /**
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
gbyte.co's avatar
gbyte.co committed
403
   * @throws \Drupal\Component\Plugin\Exception\PluginException
404 405 406
   */
  public function rebuildQueue(array &$form, FormStateInterface $form_state) {
    $this->generator->rebuildQueue();
407
  }
gbyte.co's avatar
gbyte.co committed
408

409
}