3 namespace Drupal\advagg\Form;
5 use Drupal\Core\Asset\AssetCollectionOptimizerInterface;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\Config\ConfigFactoryInterface;
8 use Drupal\Core\Datetime\DateFormatterInterface;
9 use Drupal\Core\Form\ConfigFormBase;
10 use Drupal\Core\Form\FormStateInterface;
11 use Drupal\Core\PrivateKey;
12 use Drupal\Core\State\StateInterface;
13 use Drupal\Component\Utility\Crypt;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
17 * Configure advagg settings for this site.
19 class OperationsForm extends ConfigFormBase {
22 * The private key service.
24 * @var \Drupal\Core\PrivateKey
26 protected $privateKey;
29 * The CSS asset collection optimizer service.
31 * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
33 protected $cssCollectionOptimizer;
36 * The JavaScript asset collection optimizer service.
38 * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
40 protected $jsCollectionOptimizer;
43 * The date formatter service.
45 * @var \Drupal\Core\Datetime\DateFormatterInterface
47 protected $dateFormatter;
50 * A state information store for the AdvAgg generated aggregates.
52 * @var \Drupal\Core\State\StateInterface
54 protected $advaggAggregates;
57 * A state information store for the AdvAgg scanned files.
59 * @var \Drupal\Core\State\StateInterface
61 protected $advaggFiles;
64 * Constructs the OperationsForm object.
66 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
67 * The factory for configuration objects.
68 * @param \Drupal\Core\PrivateKey $private_key
69 * The private key service.
70 * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
71 * The Date formatter service.
72 * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $css_collection_optimizer
73 * The CSS asset collection optimizer service.
74 * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $js_collection_optimizer
75 * The JavaScript asset collection optimizer service.
76 * @param \Drupal\Core\State\StateInterface $advagg_files
77 * A state information store for the AdvAgg scanned files.
78 * @param \Drupal\Core\State\StateInterface $advagg_aggregates
79 * A state information store for the AdvAgg generated aggregates.
81 public function __construct(ConfigFactoryInterface $config_factory, PrivateKey $private_key, DateFormatterInterface $date_formatter, AssetCollectionOptimizerInterface $css_collection_optimizer, AssetCollectionOptimizerInterface $js_collection_optimizer, StateInterface $advagg_files, StateInterface $advagg_aggregates) {
82 parent::__construct($config_factory);
83 $this->privateKey = $private_key;
84 $this->dateFormatter = $date_formatter;
85 $this->cssCollectionOptimizer = $css_collection_optimizer;
86 $this->jsCollectionOptimizer = $js_collection_optimizer;
87 $this->advaggFiles = $advagg_files;
88 $this->advaggAggregates = $advagg_aggregates;
94 public static function create(ContainerInterface $container) {
96 $container->get('config.factory'),
97 $container->get('private_key'),
98 $container->get('date.formatter'),
99 $container->get('asset.css.collection_optimizer'),
100 $container->get('asset.js.collection_optimizer'),
101 $container->get('state.advagg.files'),
102 $container->get('state.advagg.aggregates')
109 public function getFormId() {
110 return 'advagg_operations';
116 protected function getEditableConfigNames() {
117 return ['advagg.settings'];
123 public function buildForm(array $form, FormStateInterface $form_state) {
125 // Explain what can be done on this page.
127 '#markup' => '<p>' . $this->t('This is a collection of commands to control the cache and to manage testing of this module. In general this page is useful when troubleshooting some aggregation issues. For normal operations, you do not need to do anything on this page below the Smart Cache Flush. There are no configuration options here.') . '</p>',
130 '#prefix' => "<div id='operations-wrapper'>",
131 '#suffix' => "</div>",
134 // Buttons to do stuff.
135 // AdvAgg smart cache flushing.
136 $form['smart_flush'] = [
137 '#type' => 'fieldset',
138 '#title' => $this->t('Smart Cache Flush'),
139 '#description' => $this->t('Scan all files referenced in aggregated files. If any of them have changed, clear that cache so the changes will go out.'),
141 $form['smart_flush']['advagg_flush'] = [
143 '#value' => $this->t('Flush AdvAgg Cache'),
144 '#submit' => ['::flushCache'],
146 'callback' => '::tasksAjax',
147 'wrapper' => 'operations-wrapper',
151 // Set/Remove Bypass Cookie.
153 '#type' => 'fieldset',
154 '#title' => $this->t('Aggregation Bypass Cookie'),
155 '#description' => $this->t('This will set or remove a cookie that disables aggregation for a set period of time.'),
157 $form['bypass']['timespan'] = [
159 '#title' => 'Bypass length',
161 21600 => $this->t('6 hours'),
162 43200 => $this->t('12 hours'),
163 86400 => $this->t('1 day'),
164 172800 => $this->t('2 days'),
165 604800 => $this->t('1 week'),
166 2592000 => $this->t('1 month'),
167 31536000 => $this->t('1 year'),
170 $form['bypass']['submit'] = [
172 '#value' => $this->t('Toggle The "aggregation bypass cookie" For This Browser'),
174 'onclick' => 'javascript:return advagg_toggle_cookie()',
176 '#submit' => ['::toggleBypassCookie'],
178 // Add in aggregation bypass cookie javascript.
179 $form['#attached']['drupalSettings']['advagg'] = [
180 'key' => Crypt::hashBase64($this->privateKey->get()),
182 $form['#attached']['library'][] = 'advagg/admin.operations';
184 // Tasks run by cron.
186 '#type' => 'fieldset',
187 '#title' => $this->t('Cron Maintenance Tasks'),
189 '#markup' => $this->t('The following 3 operations are ran on cron but you can run them manually here.'),
192 $form['cron']['wrapper'] = [
193 '#prefix' => "<div id='cron-wrapper'>",
194 '#suffix' => "</div>",
196 $form['cron']['smart_file_flush'] = [
197 '#type' => 'details',
198 '#title' => $this->t('Clear All Stale Files'),
199 '#description' => $this->t('Remove all stale files. Scan all files in the advagg_css/js directories and remove the ones that have not been accessed in the last 30 days.'),
201 $form['cron']['smart_file_flush']['advagg_flush_stale_files'] = [
203 '#value' => $this->t('Remove All Stale Files'),
204 '#submit' => ['::clearStaleAggregates'],
206 'callback' => '::cronTasksAjax',
207 'wrapper' => 'cron-wrapper',
210 $form['cron']['remove_missing_files'] = [
211 '#type' => 'details',
212 '#title' => $this->t('Clear Missing Files From Database'),
213 '#description' => $this->t('Scan for missing files and remove the associated entries from the database.'),
215 $form['cron']['remove_missing_files']['advagg_remove_missing_files_from_db'] = [
217 '#value' => $this->t('Clear Missing Files From Database'),
218 '#submit' => ['::clearMissingFiles'],
220 'callback' => '::cronTasksAjax',
221 'wrapper' => 'cron-wrapper',
224 $form['cron']['remove_old_aggregates'] = [
225 '#type' => 'details',
226 '#title' => $this->t('Delete Unused Aggregates From Database'),
227 '#description' => $this->t('Delete aggregates that have not been accessed in the last 6 weeks.'),
229 $form['cron']['remove_old_aggregates']['advagg_remove_old_unused_aggregates'] = [
231 '#value' => $this->t('Delete Unused Aggregates From Database'),
232 '#submit' => ['::clearOldUnusedAggregates'],
234 'callback' => '::cronTasksAjax',
235 'wrapper' => 'cron-wrapper',
239 // Hide drastic measures as they should not be done unless really needed.
240 $form['drastic_measures'] = [
241 '#type' => 'details',
242 '#title' => $this->t('Drastic Measures'),
243 '#description' => $this->t('The options below should normally never need to be done.'),
245 $form['drastic_measures']['wrapper'] = [
246 '#prefix' => "<div id='drastic-measures-wrapper'>",
247 '#suffix' => "</div>",
249 $form['drastic_measures']['dumb_cache_flush'] = [
250 '#type' => 'details',
251 '#title' => $this->t('Clear All Caches'),
252 '#description' => $this->t('Remove all entries from the advagg cache and file information stores. Useful if you suspect a cache is not getting cleared.'),
254 $form['drastic_measures']['dumb_cache_flush']['advagg_flush_all_caches'] = [
256 '#value' => $this->t('Clear All Caches & File Information'),
257 '#submit' => ['::clearAll'],
259 'callback' => '::drasticTasksAjax',
260 'wrapper' => 'drastic-measures-wrapper',
263 $form['drastic_measures']['dumb_file_flush'] = [
264 '#type' => 'details',
265 '#title' => $this->t('Clear All Files'),
266 '#description' => $this->t('Remove all generated files. Useful if you think some of the generated files got corrupted and thus need to be deleted.'),
268 $form['drastic_measures']['dumb_file_flush']['advagg_flush_all_files'] = [
270 '#value' => $this->t('Remove All Generated Files'),
271 '#submit' => ['::clearAggregates'],
273 'callback' => '::drasticTasksAjax',
274 'wrapper' => 'drastic-measures-wrapper',
277 $form['drastic_measures']['force_change'] = [
278 '#type' => 'details',
279 '#title' => $this->t('Force new aggregates'),
280 '#description' => $this->t('Force the creation of all new aggregates by incrementing a global counter. Current value of counter: %value. This is useful if a CDN has cached an aggregate incorrectly as it will force new ones to be used even if nothing else has changed.', [
281 '%value' => advagg_get_global_counter(),
284 $form['drastic_measures']['force_change']['increment_global_counter'] = [
286 '#value' => $this->t('Increment Global Counter'),
287 '#submit' => ['::incrementCounter'],
289 'callback' => '::drasticTasksAjax',
290 'wrapper' => 'drastic-measures-wrapper',
293 return parent::buildForm($form, $form_state);
297 * Perform a smart flush.
299 public function flushCache() {
300 Cache::invalidateTags(['library_info', 'advagg_css', 'advagg_js']);
302 if ($this->config('advagg.settings')->get('cache_level') >= 0) {
303 // Display a simple message if not in Development mode.
304 drupal_set_message($this->t('Advagg Caches Cleared'));
309 * Report results via Ajax.
312 * An associative array containing the structure of the form.
314 public function tasksAjax(array &$form) {
315 return $form['wrapper'];
319 * Clear out all advagg cache bins and clear out all advagg aggregated files.
321 public function clearAggregates() {
322 // Clear out the cache.
323 Cache::invalidateTags(['library_info', 'advagg_css', 'advagg_js']);
325 $css_files = $this->cssCollectionOptimizer->deleteAllReal();
326 $js_files = $this->jsCollectionOptimizer->deleteAllReal();
328 // Report back the results.
329 drupal_set_message($this->t('All AdvAgg aggregates have been deleted. %css_count CSS files and %js_count JS files have been removed.', [
330 '%css_count' => count($css_files),
331 '%js_count' => count($js_files),
336 * Clear ALL saved information and aggregates.
338 public function clearAll() {
339 $this->clearAggregates();
340 $this->advaggAggregates->deleteAll();
341 $this->advaggFiles->deleteAll();
342 drupal_set_message($this->t('All AdvAgg cached information and aggregates deleted.'));
346 * Clear out all stale advagg aggregated files.
348 public function clearStaleAggregates() {
350 $css_count = count($this->cssCollectionOptimizer->deleteStale());
351 $js_count = count($this->jsCollectionOptimizer->deleteStale());
353 // Report back the results.
354 if ($css_count || $js_count) {
355 drupal_set_message($this->t('All stale aggregates have been deleted. %css_count CSS files and %js_count JS files have been removed.', [
356 '%css_count' => $css_count,
357 '%js_count' => $js_count,
361 drupal_set_message($this->t('No stale aggregates found. Nothing was deleted.'));
366 * Clear out all advagg cache bins and increment the counter.
368 public function incrementCounter() {
369 // Clear out the cache and delete aggregates.
370 $this->clearAggregates();
372 // Increment counter.
373 $new_value = $this->config('advagg.settings')->get('global_counter') + 1;
374 $this->config('advagg.settings')
375 ->set('global_counter', $new_value)
377 drupal_set_message($this->t('Global counter is now set to %new_value', [
378 '%new_value' => $new_value,
383 * Report results from the drastic measure tasks via Ajax.
386 * An associative array containing the structure of the form.
388 public function drasticTasksAjax(array &$form) {
389 return $form['drastic_measures']['wrapper'];
393 * Scan for missing files and remove the associated entries in the database.
395 public function clearMissingFiles() {
396 $deleted = $this->advaggFiles->clearMissingFiles();
397 $this->advaggAggregates->clearMissingFiles();
398 if (empty($deleted)) {
399 drupal_set_message($this->t('No missing files found or they could not be safely cleared out of the database.'));
402 drupal_set_message($this->t('Some missing files were found and could be safely cleared out of the database. <pre> @raw </pre>', [
403 '@raw' => print_r($deleted, TRUE),
409 * Report results from the cron tasks via Ajax.
412 * An associative array containing the structure of the form.
414 public function cronTasksAjax(array &$form) {
415 return $form['cron']['wrapper'];
419 * Delete aggregates that have not been accessed in the last 6 weeks.
421 public function clearOldUnusedAggregates() {
422 // Remove unused aggregates.
423 $count = count($this->cssCollectionOptimizer->deleteOld());
424 $count += count($this->jsCollectionOptimizer->deleteOld());
426 drupal_set_message($this->t('No old and unused aggregates found. Nothing was deleted.'));
429 drupal_set_message($this->t('Some old and unused aggregates were found. A total of %count database entries were removed.', [
436 * Set or remove the AdvAggDisabled cookie.
439 * An associative array containing the structure of the form.
440 * @param \Drupal\Core\Form\FormStateInterface $form_state
441 * The current state of the form.
443 public function toggleBypassCookie(array &$form, FormStateInterface $form_state) {
444 $cookie_name = 'AdvAggDisabled';
445 $key = Crypt::hashBase64($this->privateKey->get());
447 // If the cookie does exist then remove it.
448 if (!empty($_COOKIE[$cookie_name]) && $_COOKIE[$cookie_name] == $key) {
449 setcookie($cookie_name, '', -1, $GLOBALS['base_path'], '.' . $_SERVER['HTTP_HOST']);
450 unset($_COOKIE[$cookie_name]);
451 drupal_set_message($this->t('AdvAgg Bypass Cookie Removed.'));
453 // If the cookie does not exist then set it.
455 setcookie($cookie_name, $key, REQUEST_TIME + $form_state->getValue('timespan'), $GLOBALS['base_path'], '.' . $_SERVER['HTTP_HOST']);
456 $_COOKIE[$cookie_name] = $key;
457 drupal_set_message($this->t('AdvAgg Bypass Cookie Set for %time.', [
458 '%time' => $this->dateFormatter->formatInterval($form_state->getValue('timespan')),