3 namespace Drupal\advagg_validator\Form;
5 use Drupal\advagg\Form\AdvaggFormBase;
6 use Drupal\Component\Render\HtmlEscapedText;
9 * Base form for all advagg validator options.
11 abstract class BaseValidatorForm extends AdvaggFormBase {
16 protected function getEditableConfigNames() {
17 return ['advagg_validator.settings'];
21 * Generate a heirachical form sorted by path from asset files.
24 * The asset extension - usually 'css' or 'js'.
25 * @param bool $run_client_side
26 * Determines whether to assign submit functions to buttons.
31 public function generateForm($type, $run_client_side = TRUE) {
33 $files = $this->scanAllDirs($type);
35 foreach ($files as $file) {
36 $dir = dirname($file);
38 // Build the directory structure.
39 $levels = explode('/', $dir === '.' ? '{ROOT}' : $dir);
42 foreach ($levels as $key => $value) {
43 // Build directory structure.
45 $point = &$point[$value];
46 if (!is_array($point)) {
47 $form_api_dirname = str_replace(['/', '.'], ['__', '--'], $dir);
48 $wrapper = 'advagg-validator-' . $type . '-validator-ajax' . $form_api_dirname;
53 '#description' => '<strong>' . t('Directory:') . ' </strong>' . implode('/', $built),
56 if (!isset($point['check_all_levels']) && $value !== '{ROOT}' && count($levels) != $key + 1) {
57 $point['check_all_levels'] = [
59 '#value' => t('Check directory and all subdirectories'),
60 '#name' => implode('/', $built),
62 if (!$run_client_side) {
63 $point['check_all_levels'] += [
64 '#submit' => ['::submitCheckAll'],
66 'callback' => '::ajaxCheck',
67 'wrapper' => $wrapper,
72 $point['check_all_levels'] += [
74 'class' => ['advagg_validator_recursive_' . $type],
80 '#markup' => '<div></div>',
84 '#markup' => "<div id='" . $wrapper . "' class='results'></div>",
89 // Drop in button and info if we reached the point where a file lives.
90 if (count($levels) == $key + 1) {
91 $form_api_filename = str_replace(['/', '.'], ['__', '--'], $file);
93 if (!isset($point['check_this_level'])) {
94 $point['check_this_level'] = [
96 '#value' => t('Check directory'),
98 if (!$run_client_side) {
99 $point['check_this_level'] += [
100 '#submit' => ['::submitCheckDirectory'],
103 'callback' => '::ajaxCheck',
104 'wrapper' => $wrapper,
109 $point['check_this_level'] += [
111 'class' => ['advagg_validator_' . $type],
116 if (!isset($point['start'])) {
118 '#markup' => '<br /><strong>' . t('File:') . ' </strong><div class="filenames">',
123 '#markup' => '<br /><strong>' . t('Files:') . ' </strong><div class="filenames">',
126 $point[$form_api_filename] = [
127 '#markup' => $file . " </br>\n",
129 if (!isset($point['end'])) {
131 '#markup' => '</div>',
135 $point['hidden_' . $form_api_filename] = [
139 'class' => ['filenames'],
150 * Do not display info on a file if it is valid.
153 * Array containing info about validators and if the file is valid.
156 * $info array minus data if that file is valid.
158 protected function hideGoodFiles(array $info) {
160 foreach ($info as $filename => $validators) {
161 foreach ($validators as $v_name => $v_data) {
162 $output[$filename][$v_name] = ['#prefix' => '<em>' . $filename . ':</em> '];
163 if (!empty($v_data['validity'])) {
164 $output[$filename][$v_name]['#markup'] = t('valid');
166 elseif (isset($v_data['error'])) {
167 $output[$filename][$v_name]['error'] = $v_data['error'];
170 $output[$filename][$v_name]['options'] = [
171 '#markup' => '<em>' . t('Options:') . '</em><br/>',
173 foreach ($v_data['options'] as $option => $value) {
174 $output[$filename][$v_name]['options'][] = [
175 '#markup' => new HtmlEscapedText($option) . ': ' . new HtmlEscapedText($value),
176 '#suffix' => '</br>',
179 if (isset($v_data['errors'])) {
180 $output[$filename][$v_name]['errors'] = [
181 '#markup' => '<em>' . t('Errors:') . '</em>',
183 foreach ($v_data['errors'] as $error) {
184 $output[$filename][$v_name]['errors'][] = [
185 '#prefix' => '<pre>',
186 '#plain_text' => print_r($error, TRUE),
187 '#suffix' => '</pre>',
191 if (isset($v_data['warnings'])) {
192 $output[$filename][$v_name]['warnings'] = [
193 '#markup' => '<em>' . t('Warnings:') . '</em>',
195 foreach ($v_data['warnings'] as $warning) {
196 $output[$filename][$v_name]['warnings'][] = [
197 '#prefix' => '<pre>',
198 '#plain_text' => print_r($warning, TRUE),
199 '#suffix' => '</pre>',
210 * Recursively scan the drupal webroot for files matching the given extension.
218 protected function scanAllDirs($ext) {
220 'nodirmask' => '/(\.git|.*\/files*)/',
222 $output = $this->scanDirectory(\Drupal::root(), '/.*\.' . $ext . '$/', $options);
224 foreach ($output as $values) {
225 $files[] = str_replace(\Drupal::root() . '/', '', $values->uri);
231 * Finds all files that match a given mask in a given directory.
233 * Directories and files beginning with a period are excluded; this
234 * prevents hidden files and directories (such as SVN working directories)
235 * from being scanned.
238 * The base directory or URI to scan, without trailing slash.
239 * @param string $mask
240 * The preg_match() regular expression of the files to find.
241 * @param array $options
242 * An associative array of additional options, with the following elements:
243 * - 'nomask': The preg_match() regular expression of the files to ignore.
244 * Defaults to '/(\.\.?|CVS)$/'.
245 * - 'nomask': The preg_match() regular expression of the dirs to ignore.
246 * Defaults to '/(\.git)/'.
247 * - 'callback': The callback function to call for each match. There is no
249 * - 'recurse': When TRUE, the directory scan will recurse the entire tree
250 * starting at the provided directory. Defaults to TRUE.
251 * - 'key': The key to be used for the returned associative array of files.
252 * Possible values are 'uri', for the file's URI; 'filename', for the
253 * basename of the file; and 'name' for the name of the file without the
254 * extension. Defaults to 'uri'.
255 * - 'min_depth': Minimum depth of directories to return files from.
258 * Current depth of recursion. This parameter is only used internally and
259 * should not be passed in.
262 * An associative array (keyed on the chosen key) of objects with 'uri',
263 * 'filename', and 'name' members corresponding to the matching files.
265 protected function scanDirectory($dir, $mask, array $options = [], $depth = 0) {
266 // Merge in defaults.
268 'nomask' => '/(\.\.?|CVS)$/',
269 'nodirmask' => '/(\.git)/',
276 $options['key'] = in_array($options['key'], ['uri', 'filename', 'name']) ? $options['key'] : 'uri';
280 $handle = opendir($dir);
282 while (FALSE !== ($filename = readdir($handle))) {
283 // Skip if filename matches the nomask or is '.'.
284 if (preg_match($options['nomask'], $filename) || $filename[0] === '.') {
288 $uri = "$dir/$filename";
289 $uri = file_stream_wrapper_uri_normalize($uri);
290 if (is_dir($uri) && $options['recurse'] && !preg_match($options['nodirmask'], $uri)) {
291 // Give priority to files in this folder by merging them in after
292 // any subdirectory files.
293 $files = array_merge($this->scanDirectory($uri, $mask, $options, $depth + 1), $files);
295 elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) {
296 // Always use this match over anything already set in $files with
297 // the same $$options['key'].
298 $file = new \stdClass();
300 $file->filename = $filename;
301 $file->name = pathinfo($filename, PATHINFO_FILENAME);
302 $key = $options['key'];
303 $files[$file->$key] = $file;
304 if ($options['callback']) {
305 $options['callback']($uri);
318 * Perform server side test(s) on all given files.
320 * @param array $files
321 * An array of files to be tested.
322 * @param array $options
323 * (optional) An array of options to use in the test.
326 * An array of files with the result.
328 protected function testFiles(array $files, array $options = []) {
333 * Extract info from the DOMNode Object.
339 * Key Value pair from the DOM Node.
341 protected function domExtractor($dom) {
342 $node = $dom->firstChild;
345 $text = trim($node->nodeValue);
347 $key = str_replace('m:', '', $node->nodeName);
348 $output[$key] = $text;
350 } while ($node = $node->nextSibling);
355 * Get array element that corresponds to directory.
357 * @param array $array
358 * An associative array to check for the key. Usually a form array.
359 * @param array $keys_array
360 * An array of keys to check sequentially in a heirachical manner.
362 protected function getElement(array $array, array $keys_array) {
363 foreach ($keys_array as $key) {
364 if (!isset($key, $array)) {
367 $array = $array[$key];