3 namespace Drupal\advagg_validator\Form;
6 use Drupal\Core\Config\ConfigFactoryInterface;
7 use Drupal\Core\Form\FormStateInterface;
8 use Drupal\Core\Render\RendererInterface;
9 use Drupal\Core\State\StateInterface;
10 use Drupal\Component\Utility\Crypt;
11 use GuzzleHttp\Client;
12 use GuzzleHttp\Exception\RequestException;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14 use Symfony\Component\HttpFoundation\RequestStack;
17 * Configure form for W3C validation of CSS files.
19 class CssW3Form extends BaseValidatorForm {
22 * The Guzzle HTTP Client.
24 * @var \GuzzleHttp\Client
26 protected $httpClient;
29 * The Drupal renderer.
31 * @var \Drupal\Core\Render\RendererInterface
38 * @param \GuzzleHttp\Client $http_client
39 * The Guzzle HTTP Client.
40 * @param \Drupal\Core\Render\RendererInterface $renderer
41 * The Drupal renderer.
43 public function __construct(ConfigFactoryInterface $config_factory, StateInterface $advagg_files, StateInterface $advagg_aggregates, RequestStack $request_stack, Client $http_client, RendererInterface $renderer) {
44 parent::__construct($config_factory, $advagg_files, $advagg_aggregates, $request_stack);
45 $this->requestStack = $request_stack;
46 $this->httpClient = $http_client;
47 $this->renderer = $renderer;
53 public static function create(ContainerInterface $container) {
55 $container->get('config.factory'),
56 $container->get('state.advagg.files'),
57 $container->get('state.advagg.aggregates'),
58 $container->get('request_stack'),
59 $container->get('http_client'),
60 $container->get('renderer')
67 public function getFormId() {
68 return 'advagg_validator_cssw3';
74 public function buildForm(array $form, FormStateInterface $form_state) {
75 $form = parent::generateForm('css', FALSE);
77 '#markup' => '<div>' . t('Notice: The form below will submit files to the <a href="http://jigsaw.w3.org/css-validator/">http://jigsaw.w3.org/css-validator/</a> service if used.') . '</div>',
80 $form = parent::buildForm($form, $form_state);
81 unset($form['actions']);
88 public function submitCheckAll(array &$form, FormStateInterface $form_state) {
89 $dir = $form_state->getTriggeringElement()['#name'];
91 foreach ($form_state->getValues() as $key => $value) {
92 if (strpos($key, 'hidden') === FALSE || strpos($value, $dir) === FALSE || ($dir === '.' && substr_count($value, '/') > 0)) {
99 $info = $this->testFiles($files);
100 $info = $this->hideGoodFiles($info);
103 '#theme' => 'item_list',
106 drupal_set_message($this->renderer->render($output));
110 * Display validation info via ajax callback.
113 * An associative array containing the structure of the form.
114 * @param \Drupal\Core\Form\FormStateInterface $form_state
115 * The current state of the form.
117 public function ajaxCheck(array &$form, FormStateInterface $form_state) {
118 $dir = $form_state->getTriggeringElement()['#name'];
119 return $this->getElement($form, explode('/', $dir))['wrapper'];
125 public function submitCheckDirectory(array &$form, FormStateInterface $form_state) {
126 $dir = $form_state->getTriggeringElement()['#name'];
128 $slash_count = substr_count('/' . $dir, '/');
129 foreach ($form_state->getValues() as $key => $value) {
130 if (strpos($key, 'hidden') === FALSE || strpos($value, $dir) === FALSE || substr_count($value, '/') > $slash_count || ($dir === '.' && substr_count($value, '/') > 0)) {
137 $info = $this->testFiles($files);
138 $info = $this->hideGoodFiles($info);
141 '#theme' => 'item_list',
144 drupal_set_message($this->renderer->render($output));
150 protected function testFiles(array $files, array $options = []) {
152 $file_info = $this->advaggFiles->getMultiple($files);
153 foreach ($files as $filename) {
154 // Skip missing files.
155 if (!file_exists($filename)) {
159 $file_contents = file_get_contents($filename);
160 $lines = file($filename);
161 $content_hash = Crypt::hashBase64($file_contents);
163 // If saved file information not current update filestore.
164 if ($file_info[$filename]['content_hash'] != $content_hash) {
165 $this->advagg_files->scanFile($filename, $file_info[$filename], $file_contents);
168 // If saved validation results available use them rather than re-run.
169 if (isset($file_info[$filename]['validation']['w3'])) {
170 $output[$filename]['jigsaw.w3.org'] = $file_info[$filename]['validation']['w3'];
174 // Run jigsaw.w3.org validator.
175 $output[$filename]['jigsaw.w3.org'] = $this->testW3C($filename, $options);
177 // Get extra context for errors.
178 if (!empty($output[$filename]['jigsaw.w3.org']['errors'])) {
179 foreach ($output[$filename]['jigsaw.w3.org']['errors'] as &$value) {
180 if (isset($value['line'])) {
181 $value['linedata'] = $lines[($value['line'] - 1)];
182 if (strlen($value['linedata']) > 512) {
183 unset($value['linedata']);
189 if (!empty($output[$filename]['jigsaw.w3.org']['warnings'])) {
190 foreach ($output[$filename]['jigsaw.w3.org']['warnings'] as &$value) {
191 if (isset($value['line'])) {
192 $value['linedata'] = $lines[$value['line'] - 1];
193 if (strlen($value['linedata']) > 512) {
194 unset($value['linedata']);
202 $file_info[$filename]['validation']['w3'] = $output[$filename]['jigsaw.w3.org'];
203 $this->advaggFiles->set($filename, $file_info[$filename]);
209 * Given a CSS file, test to make sure it is valid CSS.
211 * @param string $filename
212 * The name of the file.
213 * @param array $validator_options
214 * List of options to pass along to the CSS Validator.
217 * Info from the w3c server.
219 private function testW3C($filename, array &$validator_options = []) {
220 // Get CSS files contents.
221 $validator_options['text'] = file_get_contents($filename);
222 if (strlen($validator_options['text']) > 50000) {
223 unset($validator_options['text']);
224 $validator_options['uri'] = $this->requestStack->getCurrentRequest()->getBaseUrl() . $filename;
228 $validator_options += [
229 'output' => 'soap12',
232 'usermedium' => 'all',
236 // Build request URL.
237 // API Documentation http://jigsaw.w3.org/css-validator/api.html
238 $request_url = 'http://jigsaw.w3.org/css-validator/validator';
239 $query = http_build_query($validator_options, '', '&');
240 $url = $request_url . '?' . $query;
242 $data = $this->httpClient
246 catch (RequestException $e) {
247 watchdog_exception('AdvAgg Validator', $e);
249 catch (\Exception $e) {
250 watchdog_exception('AdvAgg Validator', $e);
253 // Parse XML and return info.
254 $return = $this->parseSoapResponse($data);
255 $return['filename'] = $filename;
256 if (isset($validator_options['text'])) {
257 unset($validator_options['text']);
259 elseif (isset($validator_options['uri'])) {
260 unset($validator_options['uri']);
262 $return['options'] = $validator_options;
266 return ['error' => t('W3C Server did not return a 200 or request data was empty.')];
272 private function parseSoapResponse($xml) {
273 $doc = new DOMDocument();
276 // Try to load soap 1.2 XML response, and suppress warning reports if any.
277 if (!@$doc->loadXML($xml)) {
278 // Could not load the XML document.
282 // Get the standard CDATA elements.
283 $cdata = ['uri', 'checkedby', 'csslevel', 'date'];
284 foreach ($cdata as $var) {
285 $element = $doc->getElementsByTagName($var);
286 if ($element->length) {
287 $response[$var] = $element->item(0)->nodeValue;
291 // Handle the element validity and get errors if not valid.
292 $element = $doc->getElementsByTagName('validity');
293 if ($element->length && $element->item(0)->nodeValue === 'true') {
294 $response['validity'] = TRUE;
297 $response['validity'] = FALSE;
298 $errors = $doc->getElementsByTagName('error');
299 foreach ($errors as $error) {
300 $response['errors'][] = $this->domExtractor($error);
305 $warnings = $doc->getElementsByTagName('warning');
306 foreach ($warnings as $warning) {
307 $response['warnings'][] = $this->domExtractor($warning);
310 // Return response array.