scanAllDirs($type); rsort($files); foreach ($files as $file) { $dir = dirname($file); // Build the directory structure. $levels = explode('/', $dir === '.' ? '{ROOT}' : $dir); $point = &$form; $built = []; foreach ($levels as $key => $value) { // Build directory structure. $built[] = $value; $point = &$point[$value]; if (!is_array($point)) { $form_api_dirname = str_replace(['/', '.'], ['__', '--'], $dir); $wrapper = 'advagg-validator-' . $type . '-validator-ajax' . $form_api_dirname; $point = [ '#type' => 'details', '#title' => $value, '#description' => '' . t('Directory:') . ' ' . implode('/', $built), '#weight' => 100, ]; if (!isset($point['check_all_levels']) && $value !== '{ROOT}' && count($levels) != $key + 1) { $point['check_all_levels'] = [ '#type' => 'submit', '#value' => t('Check directory and all subdirectories'), '#name' => implode('/', $built), ]; if (!$run_client_side) { $point['check_all_levels'] += [ '#submit' => ['::submitCheckAll'], '#ajax' => [ 'callback' => '::ajaxCheck', 'wrapper' => $wrapper, ], ]; } else { $point['check_all_levels'] += [ '#attributes' => [ 'class' => ['advagg_validator_recursive_' . $type], ], ]; } } $point['break'] = [ '#markup' => '
', ]; $point['wrapper'] = [ '#markup' => "
", '#weight' => 90, ]; } // Drop in button and info if we reached the point where a file lives. if (count($levels) == $key + 1) { $form_api_filename = str_replace(['/', '.'], ['__', '--'], $file); if (!isset($point['check_this_level'])) { $point['check_this_level'] = [ '#type' => 'submit', '#value' => t('Check directory'), ]; if (!$run_client_side) { $point['check_this_level'] += [ '#submit' => ['::submitCheckDirectory'], '#name' => $dir, '#ajax' => [ 'callback' => '::ajaxCheck', 'wrapper' => $wrapper, ], ]; } else { $point['check_this_level'] += [ '#attributes' => [ 'class' => ['advagg_validator_' . $type], ], ]; } } if (!isset($point['start'])) { $point['start'] = [ '#markup' => '
' . t('File:') . '
', ]; } else { $point['start'] = [ '#markup' => '
' . t('Files:') . '
', ]; } $point[$form_api_filename] = [ '#markup' => $file . "
\n", ]; if (!isset($point['end'])) { $point['end'] = [ '#markup' => '
', ]; } $point['hidden_' . $form_api_filename] = [ '#type' => 'hidden', '#value' => $file, '#attributes' => [ 'class' => ['filenames'], ], ]; } } } return $form; } /** * Do not display info on a file if it is valid. * * @param array $info * Array containing info about validators and if the file is valid. * * @return array * $info array minus data if that file is valid. */ protected function hideGoodFiles(array $info) { $output = []; foreach ($info as $filename => $validators) { foreach ($validators as $v_name => $v_data) { $output[$filename][$v_name] = ['#prefix' => '' . $filename . ': ']; if (!empty($v_data['validity'])) { $output[$filename][$v_name]['#markup'] = t('valid'); } elseif (isset($v_data['error'])) { $output[$filename][$v_name]['error'] = $v_data['error']; } else { $output[$filename][$v_name]['options'] = [ '#markup' => '' . t('Options:') . '
', ]; foreach ($v_data['options'] as $option => $value) { $output[$filename][$v_name]['options'][] = [ '#markup' => new HtmlEscapedText($option) . ': ' . new HtmlEscapedText($value), '#suffix' => '
', ]; } if (isset($v_data['errors'])) { $output[$filename][$v_name]['errors'] = [ '#markup' => '' . t('Errors:') . '', ]; foreach ($v_data['errors'] as $error) { $output[$filename][$v_name]['errors'][] = [ '#prefix' => '
',
                '#plain_text' => print_r($error, TRUE),
                '#suffix' => '
', ]; } } if (isset($v_data['warnings'])) { $output[$filename][$v_name]['warnings'] = [ '#markup' => '' . t('Warnings:') . '', ]; foreach ($v_data['warnings'] as $warning) { $output[$filename][$v_name]['warnings'][] = [ '#prefix' => '
',
                '#plain_text' => print_r($warning, TRUE),
                '#suffix' => '
', ]; } } } } } return $output; } /** * Recursively scan the drupal webroot for files matching the given extension. * * @param string $ext * Usually css or js. * * @return array * An array of files. */ protected function scanAllDirs($ext) { $options = [ 'nodirmask' => '/(\.git|.*\/files*)/', ]; $output = $this->scanDirectory(\Drupal::root(), '/.*\.' . $ext . '$/', $options); $files = []; foreach ($output as $values) { $files[] = str_replace(\Drupal::root() . '/', '', $values->uri); } return $files; } /** * Finds all files that match a given mask in a given directory. * * Directories and files beginning with a period are excluded; this * prevents hidden files and directories (such as SVN working directories) * from being scanned. * * @param string $dir * The base directory or URI to scan, without trailing slash. * @param string $mask * The preg_match() regular expression of the files to find. * @param array $options * An associative array of additional options, with the following elements: * - 'nomask': The preg_match() regular expression of the files to ignore. * Defaults to '/(\.\.?|CVS)$/'. * - 'nomask': The preg_match() regular expression of the dirs to ignore. * Defaults to '/(\.git)/'. * - 'callback': The callback function to call for each match. There is no * default callback. * - 'recurse': When TRUE, the directory scan will recurse the entire tree * starting at the provided directory. Defaults to TRUE. * - 'key': The key to be used for the returned associative array of files. * Possible values are 'uri', for the file's URI; 'filename', for the * basename of the file; and 'name' for the name of the file without the * extension. Defaults to 'uri'. * - 'min_depth': Minimum depth of directories to return files from. * Defaults to 0. * @param int $depth * Current depth of recursion. This parameter is only used internally and * should not be passed in. * * @return array * An associative array (keyed on the chosen key) of objects with 'uri', * 'filename', and 'name' members corresponding to the matching files. */ protected function scanDirectory($dir, $mask, array $options = [], $depth = 0) { // Merge in defaults. $options += [ 'nomask' => '/(\.\.?|CVS)$/', 'nodirmask' => '/(\.git)/', 'callback' => 0, 'recurse' => TRUE, 'key' => 'uri', 'min_depth' => 0, ]; $options['key'] = in_array($options['key'], ['uri', 'filename', 'name']) ? $options['key'] : 'uri'; $files = []; if (is_dir($dir)) { $handle = opendir($dir); if ($handle) { while (FALSE !== ($filename = readdir($handle))) { // Skip if filename matches the nomask or is '.'. if (preg_match($options['nomask'], $filename) || $filename[0] === '.') { continue; } $uri = "$dir/$filename"; $uri = file_stream_wrapper_uri_normalize($uri); if (is_dir($uri) && $options['recurse'] && !preg_match($options['nodirmask'], $uri)) { // Give priority to files in this folder by merging them in after // any subdirectory files. $files = array_merge($this->scanDirectory($uri, $mask, $options, $depth + 1), $files); } elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) { // Always use this match over anything already set in $files with // the same $$options['key']. $file = new \stdClass(); $file->uri = $uri; $file->filename = $filename; $file->name = pathinfo($filename, PATHINFO_FILENAME); $key = $options['key']; $files[$file->$key] = $file; if ($options['callback']) { $options['callback']($uri); } } } closedir($handle); } } return $files; } /** * Perform server side test(s) on all given files. * * @param array $files * An array of files to be tested. * @param array $options * (optional) An array of options to use in the test. * * @return array * An array of files with the result. */ protected function testFiles(array $files, array $options = []) { return $files; } /** * Extract info from the DOMNode Object. * * @param object $dom * DOMNode Class. * * @return array * Key Value pair from the DOM Node. */ protected function domExtractor($dom) { $node = $dom->firstChild; $output = []; do { $text = trim($node->nodeValue); if (!empty($text)) { $key = str_replace('m:', '', $node->nodeName); $output[$key] = $text; } } while ($node = $node->nextSibling); return $output; } /** * Get array element that corresponds to directory. * * @param array $array * An associative array to check for the key. Usually a form array. * @param array $keys_array * An array of keys to check sequentially in a heirachical manner. */ protected function getElement(array $array, array $keys_array) { foreach ($keys_array as $key) { if (!isset($key, $array)) { return FALSE; } $array = $array[$key]; } return $array; } }