3c46472a2713a8098957962de5d92ccc7fa004a4
[yaffs-website] / web / modules / contrib / advagg / src / Form / OperationsForm.php
1 <?php
2
3 namespace Drupal\advagg\Form;
4
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;
15
16 /**
17  * Configure advagg settings for this site.
18  */
19 class OperationsForm extends ConfigFormBase {
20
21   /**
22    * The private key service.
23    *
24    * @var \Drupal\Core\PrivateKey
25    */
26   protected $privateKey;
27
28   /**
29    * The CSS asset collection optimizer service.
30    *
31    * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
32    */
33   protected $cssCollectionOptimizer;
34
35   /**
36    * The JavaScript asset collection optimizer service.
37    *
38    * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
39    */
40   protected $jsCollectionOptimizer;
41
42   /**
43    * The date formatter service.
44    *
45    * @var \Drupal\Core\Datetime\DateFormatterInterface
46    */
47   protected $dateFormatter;
48
49   /**
50    * A state information store for the AdvAgg generated aggregates.
51    *
52    * @var \Drupal\Core\State\StateInterface
53    */
54   protected $advaggAggregates;
55
56   /**
57    * A state information store for the AdvAgg scanned files.
58    *
59    * @var \Drupal\Core\State\StateInterface
60    */
61   protected $advaggFiles;
62
63   /**
64    * Constructs the OperationsForm object.
65    *
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.
80    */
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;
89   }
90
91   /**
92    * {@inheritdoc}
93    */
94   public static function create(ContainerInterface $container) {
95     return new static(
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')
103     );
104   }
105
106   /**
107    * {@inheritdoc}
108    */
109   public function getFormId() {
110     return 'advagg_operations';
111   }
112
113   /**
114    * {@inheritdoc}
115    */
116   protected function getEditableConfigNames() {
117     return ['advagg.settings'];
118   }
119
120   /**
121    * {@inheritdoc}
122    */
123   public function buildForm(array $form, FormStateInterface $form_state) {
124     $form = [];
125     // Explain what can be done on this page.
126     $form['tip'] = [
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>',
128     ];
129     $form['wrapper'] = [
130       '#prefix' => "<div id='operations-wrapper'>",
131       '#suffix' => "</div>",
132     ];
133
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.'),
140     ];
141     $form['smart_flush']['advagg_flush'] = [
142       '#type' => 'submit',
143       '#value' => $this->t('Flush AdvAgg Cache'),
144       '#submit' => ['::flushCache'],
145       '#ajax' => [
146         'callback' => '::tasksAjax',
147         'wrapper' => 'operations-wrapper',
148       ],
149     ];
150
151     // Set/Remove Bypass Cookie.
152     $form['bypass'] = [
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.'),
156     ];
157     $form['bypass']['timespan'] = [
158       '#type' => 'select',
159       '#title' => 'Bypass length',
160       '#options' => [
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'),
168       ],
169     ];
170     $form['bypass']['submit'] = [
171       '#type' => 'submit',
172       '#value' => $this->t('Toggle The "aggregation bypass cookie" For This Browser'),
173       '#attributes' => [
174         'onclick' => 'javascript:return advagg_toggle_cookie()',
175       ],
176       '#submit' => ['::toggleBypassCookie'],
177     ];
178     // Add in aggregation bypass cookie javascript.
179     $form['#attached']['drupalSettings']['advagg'] = [
180       'key' => Crypt::hashBase64($this->privateKey->get()),
181     ];
182     $form['#attached']['library'][] = 'advagg/admin.operations';
183
184     // Tasks run by cron.
185     $form['cron'] = [
186       '#type' => 'fieldset',
187       '#title' => $this->t('Cron Maintenance Tasks'),
188       'description' => [
189         '#markup' => $this->t('The following 3 operations are ran on cron but you can run them manually here.'),
190       ],
191     ];
192     $form['cron']['wrapper'] = [
193       '#prefix' => "<div id='cron-wrapper'>",
194       '#suffix' => "</div>",
195     ];
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.'),
200     ];
201     $form['cron']['smart_file_flush']['advagg_flush_stale_files'] = [
202       '#type' => 'submit',
203       '#value' => $this->t('Remove All Stale Files'),
204       '#submit' => ['::clearStaleAggregates'],
205       '#ajax' => [
206         'callback' => '::cronTasksAjax',
207         'wrapper' => 'cron-wrapper',
208       ],
209     ];
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.'),
214     ];
215     $form['cron']['remove_missing_files']['advagg_remove_missing_files_from_db'] = [
216       '#type' => 'submit',
217       '#value' => $this->t('Clear Missing Files From Database'),
218       '#submit' => ['::clearMissingFiles'],
219       '#ajax' => [
220         'callback' => '::cronTasksAjax',
221         'wrapper' => 'cron-wrapper',
222       ],
223     ];
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.'),
228     ];
229     $form['cron']['remove_old_aggregates']['advagg_remove_old_unused_aggregates'] = [
230       '#type' => 'submit',
231       '#value' => $this->t('Delete Unused Aggregates From Database'),
232       '#submit' => ['::clearOldUnusedAggregates'],
233       '#ajax' => [
234         'callback' => '::cronTasksAjax',
235         'wrapper' => 'cron-wrapper',
236       ],
237     ];
238
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.'),
244     ];
245     $form['drastic_measures']['wrapper'] = [
246       '#prefix' => "<div id='drastic-measures-wrapper'>",
247       '#suffix' => "</div>",
248     ];
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.'),
253     ];
254     $form['drastic_measures']['dumb_cache_flush']['advagg_flush_all_caches'] = [
255       '#type' => 'submit',
256       '#value' => $this->t('Clear All Caches & File Information'),
257       '#submit' => ['::clearAll'],
258       '#ajax' => [
259         'callback' => '::drasticTasksAjax',
260         'wrapper' => 'drastic-measures-wrapper',
261       ],
262     ];
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.'),
267     ];
268     $form['drastic_measures']['dumb_file_flush']['advagg_flush_all_files'] = [
269       '#type' => 'submit',
270       '#value' => $this->t('Remove All Generated Files'),
271       '#submit' => ['::clearAggregates'],
272       '#ajax' => [
273         'callback' => '::drasticTasksAjax',
274         'wrapper' => 'drastic-measures-wrapper',
275       ],
276     ];
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(),
282       ]),
283     ];
284     $form['drastic_measures']['force_change']['increment_global_counter'] = [
285       '#type' => 'submit',
286       '#value' => $this->t('Increment Global Counter'),
287       '#submit' => ['::incrementCounter'],
288       '#ajax' => [
289         'callback' => '::drasticTasksAjax',
290         'wrapper' => 'drastic-measures-wrapper',
291       ],
292     ];
293     return parent::buildForm($form, $form_state);
294   }
295
296   /**
297    * Perform a smart flush.
298    */
299   public function flushCache() {
300     Cache::invalidateTags(['library_info', 'advagg_css', 'advagg_js']);
301
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'));
305     }
306   }
307
308   /**
309    * Report results via Ajax.
310    *
311    * @param array $form
312    *   An associative array containing the structure of the form.
313    */
314   public function tasksAjax(array &$form) {
315     return $form['wrapper'];
316   }
317
318   /**
319    * Clear out all advagg cache bins and clear out all advagg aggregated files.
320    */
321   public function clearAggregates() {
322     // Clear out the cache.
323     Cache::invalidateTags(['library_info', 'advagg_css', 'advagg_js']);
324
325     $css_files = $this->cssCollectionOptimizer->deleteAllReal();
326     $js_files = $this->jsCollectionOptimizer->deleteAllReal();
327
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),
332     ]));
333   }
334
335   /**
336    * Clear ALL saved information and aggregates.
337    */
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.'));
343   }
344
345   /**
346    * Clear out all stale advagg aggregated files.
347    */
348   public function clearStaleAggregates() {
349     // Run the command.
350     $css_count = count($this->cssCollectionOptimizer->deleteStale());
351     $js_count = count($this->jsCollectionOptimizer->deleteStale());
352
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,
358       ]));
359     }
360     else {
361       drupal_set_message($this->t('No stale aggregates found. Nothing was deleted.'));
362     }
363   }
364
365   /**
366    * Clear out all advagg cache bins and increment the counter.
367    */
368   public function incrementCounter() {
369     // Clear out the cache and delete aggregates.
370     $this->clearAggregates();
371
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)
376       ->save();
377     drupal_set_message($this->t('Global counter is now set to %new_value', [
378       '%new_value' => $new_value,
379     ]));
380   }
381
382   /**
383    * Report results from the drastic measure tasks via Ajax.
384    *
385    * @param array $form
386    *   An associative array containing the structure of the form.
387    */
388   public function drasticTasksAjax(array &$form) {
389     return $form['drastic_measures']['wrapper'];
390   }
391
392   /**
393    * Scan for missing files and remove the associated entries in the database.
394    */
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.'));
400     }
401     else {
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),
404       ]));
405     }
406   }
407
408   /**
409    * Report results from the cron tasks via Ajax.
410    *
411    * @param array $form
412    *   An associative array containing the structure of the form.
413    */
414   public function cronTasksAjax(array &$form) {
415     return $form['cron']['wrapper'];
416   }
417
418   /**
419    * Delete aggregates that have not been accessed in the last 6 weeks.
420    */
421   public function clearOldUnusedAggregates() {
422     // Remove unused aggregates.
423     $count = count($this->cssCollectionOptimizer->deleteOld());
424     $count += count($this->jsCollectionOptimizer->deleteOld());
425     if (empty($count)) {
426       drupal_set_message($this->t('No old and unused aggregates found. Nothing was deleted.'));
427     }
428     else {
429       drupal_set_message($this->t('Some old and unused aggregates were found. A total of %count database entries were removed.', [
430         '%count' => $count,
431       ]));
432     }
433   }
434
435   /**
436    * Set or remove the AdvAggDisabled cookie.
437    *
438    * @param array $form
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.
442    */
443   public function toggleBypassCookie(array &$form, FormStateInterface $form_state) {
444     $cookie_name = 'AdvAggDisabled';
445     $key = Crypt::hashBase64($this->privateKey->get());
446
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.'));
452     }
453     // If the cookie does not exist then set it.
454     else {
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')),
459       ]));
460     }
461   }
462
463 }