3 namespace Drupal\advagg\Form;
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\Component\Utility\Xss;
7 use Drupal\Core\Config\ConfigFactoryInterface;
8 use Drupal\Core\Datetime\DateFormatterInterface;
9 use Drupal\Core\Form\ConfigFormBase;
10 use Drupal\Core\Form\FormBuilderInterface;
11 use Drupal\Core\Form\FormStateInterface;
12 use Drupal\Core\State\StateInterface;
13 use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
14 use Drupal\Core\Theme\Registry;
15 use Symfony\Component\DependencyInjection\ContainerInterface;
16 use Symfony\Component\HttpFoundation\RequestStack;
19 * View AdvAgg information for this site.
21 class InfoForm extends ConfigFormBase {
23 * The theme registry service.
25 * @var \Drupal\Core\Theme\Registry
27 protected $themeRegistry;
30 * The AdvAgg file status state information storage service.
32 * @var \Drupal\Core\State\StateInterface
34 protected $advaggFiles;
37 * The AdvAgg aggregates state information storage service.
39 * @var \Drupal\Core\State\StateInterface
41 protected $advaggAggregates;
46 * @var \Symfony\Component\HttpFoundation\RequestStack
48 protected $requestStack;
51 * The date formatter service.
53 * @var \Drupal\Core\Datetime\DateFormatterInterface
55 protected $dateFormatter;
58 * The string translation service.
60 * @var \Drupal\Core\StringTranslation\Translator\TranslatorInterface
62 protected $translation;
65 * Constructs a SettingsForm object.
67 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
68 * The factory for configuration objects.
69 * @param \Drupal\Core\Theme\Registry $theme_registry
70 * The theme registry service.
71 * @param \Drupal\Core\State\StateInterface $advagg_files
72 * The AdvAgg file status state information storage service.
73 * @param \Drupal\Core\State\StateInterface $advagg_aggregates
74 * The AdvAgg aggregate state information storage service.
75 * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
77 * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
78 * The Date formatter service.
79 * @param \Drupal\Core\StringTranslation\Translator\TranslatorInterface $string_translation
80 * The string translation service.
82 public function __construct(ConfigFactoryInterface $config_factory, Registry $theme_registry, StateInterface $advagg_files, StateInterface $advagg_aggregates, RequestStack $request_stack, DateFormatterInterface $date_formatter, TranslatorInterface $string_translation) {
83 parent::__construct($config_factory);
85 $this->themeRegistry = $theme_registry;
86 $this->advaggFiles = $advagg_files;
87 $this->advaggAggregates = $advagg_aggregates;
88 $this->requestStack = $request_stack;
89 $this->dateFormatter = $date_formatter;
90 $this->translation = $string_translation;
96 public static function create(ContainerInterface $container) {
98 $container->get('config.factory'),
99 $container->get('theme.registry'),
100 $container->get('state.advagg.files'),
101 $container->get('state.advagg.aggregates'),
102 $container->get('request_stack'),
103 $container->get('date.formatter'),
104 $container->get('string_translation')
111 public function getFormId() {
112 return 'advagg_info';
118 protected function getEditableConfigNames() {
125 public function buildForm(array $form, FormStateInterface $form_state) {
128 '#markup' => '<p>' . $this->t('This page provides debugging information. There are no configuration options here.') . '</p>',
131 // Get all hooks and variables.
132 $core_hooks = $this->themeRegistry->get();
133 $advagg_hooks = advagg_hooks_implemented();
135 // Output html preprocess functions hooks.
136 $form['theme_info'] = [
137 '#type' => 'details',
138 '#title' => $this->t('Hook Theme Info'),
140 $data = implode("\n", $core_hooks['html']['preprocess functions']);
141 $form['theme_info']['advagg_theme_info'] = [
142 '#markup' => '<p>preprocess functions on html.</p><pre>' . $data . '</pre>',
145 $file_data = $this->advaggFiles->getAll();
147 // Get all parent css and js files.
148 $types = ['css', 'js'];
149 foreach ($types as $type) {
151 '#type' => 'details',
152 '#title' => $this->t('@type files', ['@type' => Unicode::strtoupper($type)]),
155 foreach ($file_data as $name => $info) {
156 if (!in_array($info['fileext'], $types)) {
159 $form[$info['fileext']][$info['filename_hash']] = [
160 '#markup' => '<details><summary>' . $this->translation->formatPlural($info['changes'], 'changed 1 time - %file<br />', 'changed %changes times - %file<br />', [
161 '%changes' => $info['changes'],
163 ]) . '</summary><div class="details-wrapper"><pre>' . print_r($info, TRUE) . '</pre></div></details>',
167 // Display as module -> hook instead of hook -> module.
168 ksort($advagg_hooks);
170 foreach ($advagg_hooks as $hook => $values) {
171 if (!empty($values)) {
172 foreach ($values as $module_name) {
173 if (!isset($module_hooks[$module_name])) {
174 $module_hooks[$module_name] = [];
176 $module_hooks[$module_name][] = $hook;
180 $module_hooks['not in use'][] = $hook;
183 ksort($module_hooks);
185 $form['modules_implementing_advagg'] = [
186 '#type' => 'details',
187 '#title' => $this->t('Modules implementing aggregate hooks'),
189 $form['hooks_implemented'] = [
190 '#type' => 'details',
191 '#title' => $this->t('AdvAgg CSS/JS hooks implemented by modules'),
194 // Output all advagg hooks implemented.
195 foreach ($module_hooks as $hook => $values) {
196 if (empty($values)) {
197 $form['modules_implementing_advagg'][$hook] = [
198 '#markup' => '<div><strong>' . $hook . ':</strong> 0</div>',
202 $form['modules_implementing_advagg'][$hook] = [
203 '#markup' => '<div><strong>' . $hook . ':</strong> ' . count($values) . $this->formatList($values) . '</div>',
208 // Output all advagg hooks implemented.
209 foreach ($advagg_hooks as $hook => $values) {
210 if (empty($values)) {
211 $form['hooks_implemented'][$hook] = [
212 '#markup' => '<div><strong>' . $hook . ':</strong> 0</div>',
216 $form['hooks_implemented'][$hook] = [
217 '#markup' => '<div><strong>' . $hook . ':</strong> ' . count($values) . $this->formatList($values) . '</div>',
222 // Output what is used inside of advagg_get_current_hooks_hash().
223 $form['hooks_variables_hash'] = [
224 '#type' => 'details',
225 '#title' => $this->t('Hooks And Variables Used In Hash'),
227 $form['hooks_variables_hash']['description'] = [
228 '#markup' => $this->t('Current Value: %value. Below is the listing of variables and hooks used to generate the 3rd hash of an aggregates filename.', ['%value' => advagg_get_current_hooks_hash()]),
230 $form['hooks_variables_hash']['output'] = [
231 // @ignore production_php
232 '#markup' => '<pre>' . print_r(advagg_current_hooks_hash_array(), TRUE) . '</pre>',
234 // Get info about a file.
235 $form['get_info_about_agg'] = [
236 '#type' => 'details',
238 '#title' => $this->t('Get detailed info about an aggregate file'),
240 $form['get_info_about_agg']['filename'] = [
241 '#type' => 'textfield',
244 '#default_value' => '',
245 '#title' => $this->t('Filename'),
247 $form['get_info_about_agg']['submit'] = [
249 '#value' => $this->t('Lookup Details'),
250 '#submit' => ['::getFileInfoSubmit'],
251 '#validate' => ['::getFileInfoValidate'],
253 'callback' => '::getFileInfoAjax',
254 'wrapper' => 'advagg-file-info-ajax',
258 $form['get_info_about_agg']['tip'] = [
259 '#markup' => '<p>' . $this->t('Takes input like "@css_file" or a full aggregate name like "@advagg_js"', [
260 '@css_file' => $this->advaggFiles->getRandomKey(),
261 '@advagg_js' => $this->advaggAggregates->getRandom()['uid'],
264 $form['get_info_about_agg']['wrapper'] = [
265 '#prefix' => "<div id='advagg-file-info-ajax'>",
266 '#suffix' => "</div>",
268 $form = parent::buildForm($form, $form_state);
269 unset($form['actions']);
274 * Format an indented list from array.
277 * The array to convert to a string.
279 * (optional) Depth multiplier for indentation.
282 * The imploded and spaced array.
284 private function formatList(array $list, $depth = 1) {
285 $spacer = '<br />' . str_repeat(' ', 2 * $depth);
286 $output = $spacer . Xss::filter(implode($spacer, $list), ['br']);
291 * Display file info in a drupal message.
294 * An associative array containing the structure of the form.
295 * @param \Drupal\Core\Form\FormStateInterface $form_state
296 * The current state of the form.
298 public function getFileInfoSubmit(array &$form, FormStateInterface $form_state) {
299 $info = $this->getFileInfo($form_state->getValue('filename'));
300 $output = '<pre>' . print_r($info, TRUE) . '</pre>';
301 if (!$this->isAjax()) {
302 drupal_set_message($output);
307 * Display file info via ajax callback.
310 * An associative array containing the structure of the form.
311 * @param \Drupal\Core\Form\FormStateInterface $form_state
312 * The current state of the form.
314 public function getFileInfoAjax(array &$form, FormStateInterface $form_state) {
315 $element = $form['get_info_about_agg']['wrapper'];
316 if ($form_state->hasAnyErrors()) {
319 $info = $this->getFileInfo($form_state->getValue('filename'));
321 $form_state->setErrorByName('filename', $this->t('Please input a valid aggregate filename.'));
325 $element['#markup'] = '<pre>' . print_r($info, TRUE) . '</pre>';
331 * Verify that the filename is correct.
334 * An associative array containing the structure of the form.
335 * @param \Drupal\Core\Form\FormStateInterface $form_state
336 * The current state of the form.
338 public function getFileInfoValidate(array &$form, FormStateInterface $form_state) {
339 if (empty($form_state->getValue('filename'))) {
340 $form_state->setErrorByName('filename', $this->t('Please input an aggregate filename.'));
345 * Get detailed info about the given filename.
347 * @param string $filename
348 * Name of file to lookup.
351 * Returns an array of detailed info about this file.
353 private function getFileInfo($filename) {
354 // Strip quotes and trim.
355 $filename = trim(str_replace(['"', "'"], '', $filename));
356 if (substr_compare($filename, 'css_', 0) > 0 || substr_compare($filename, 'js_', 0) > 0) {
357 $results = array_column($this->advaggAggregates->getAll(), NULL, 'uid');
358 if (isset($results[$filename])) {
359 return $results[$filename];
362 return "Aggregate name unrecognized, confirm spelling, otherwise likely a very old aggregate that has been expunged.";
365 elseif ($data = $this->advaggFiles->get($filename)) {
366 $data['File modification date'] = $this->dateFormatter->format($data['mtime'], 'html_datetime');
367 $data['Information last update'] = $this->dateFormatter->format($data['updated'], 'html_datetime');
371 return "File not found and AdvAgg has no record of it. Confirm spelling of the path.";
376 * Checks if the form was submitted by AJAX.
379 * TRUE if the form was submitted via AJAX, otherwise FALSE.
381 private function isAjax() {
382 $request = $this->requestStack->getCurrentRequest();
383 if ($request->query->has(FormBuilderInterface::AJAX_FORM_REQUEST)) {