Yaffs site version 1.1
[yaffs-website] / web / modules / contrib / advagg / advagg_validator / src / Form / CssW3Form.php
1 <?php
2
3 namespace Drupal\advagg_validator\Form;
4
5 use DOMDocument;
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;
15
16 /**
17  * Configure form for W3C validation of CSS files.
18  */
19 class CssW3Form extends BaseValidatorForm {
20
21   /**
22    * The Guzzle HTTP Client.
23    *
24    * @var \GuzzleHttp\Client
25    */
26   protected $httpClient;
27
28   /**
29    * The Drupal renderer.
30    *
31    * @var \Drupal\Core\Render\RendererInterface
32    */
33   protected $renderer;
34
35   /**
36    * {@inheritdoc}
37    *
38    * @param \GuzzleHttp\Client $http_client
39    *   The Guzzle HTTP Client.
40    * @param \Drupal\Core\Render\RendererInterface $renderer
41    *   The Drupal renderer.
42    */
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;
48   }
49
50   /**
51    * {@inheritdoc}
52    */
53   public static function create(ContainerInterface $container) {
54     return new static(
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')
61     );
62   }
63
64   /**
65    * {@inheritdoc}
66    */
67   public function getFormId() {
68     return 'advagg_validator_cssw3';
69   }
70
71   /**
72    * {@inheritdoc}
73    */
74   public function buildForm(array $form, FormStateInterface $form_state) {
75     $form = parent::generateForm('css', FALSE);
76     $form['notice'] = [
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>',
78       '#weight' => -1,
79     ];
80     $form = parent::buildForm($form, $form_state);
81     unset($form['actions']);
82     return $form;
83   }
84
85   /**
86    * {@inheritdoc}
87    */
88   public function submitCheckAll(array &$form, FormStateInterface $form_state) {
89     $dir = $form_state->getTriggeringElement()['#name'];
90     $files = [];
91     foreach ($form_state->getValues() as $key => $value) {
92       if (strpos($key, 'hidden') === FALSE || strpos($value, $dir) === FALSE || ($dir === '.' && substr_count($value, '/') > 0)) {
93         continue;
94       }
95       $files[] = $value;
96     }
97
98     // Check list.
99     $info = $this->testFiles($files);
100     $info = $this->hideGoodFiles($info);
101
102     $output = [
103       '#theme' => 'item_list',
104       '#items' => $info,
105     ];
106     drupal_set_message($this->renderer->render($output));
107   }
108
109   /**
110    * Display validation info via ajax callback.
111    *
112    * @param array $form
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.
116    */
117   public function ajaxCheck(array &$form, FormStateInterface $form_state) {
118     $dir = $form_state->getTriggeringElement()['#name'];
119     return $this->getElement($form, explode('/', $dir))['wrapper'];
120   }
121
122   /**
123    * {@inheritdoc}
124    */
125   public function submitCheckDirectory(array &$form, FormStateInterface $form_state) {
126     $dir = $form_state->getTriggeringElement()['#name'];
127     $files = [];
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)) {
131         continue;
132       }
133       $files[] = $value;
134     }
135
136     // Check list.
137     $info = $this->testFiles($files);
138     $info = $this->hideGoodFiles($info);
139
140     $output = [
141       '#theme' => 'item_list',
142       '#items' => $info,
143     ];
144     drupal_set_message($this->renderer->render($output));
145   }
146
147   /**
148    * {@inheritdoc}
149    */
150   protected function testFiles(array $files, array $options = []) {
151     $output = [];
152     $file_info = $this->advaggFiles->getMultiple($files);
153     foreach ($files as $filename) {
154       // Skip missing files.
155       if (!file_exists($filename)) {
156         continue;
157       }
158
159       $file_contents = file_get_contents($filename);
160       $lines = file($filename);
161       $content_hash = Crypt::hashBase64($file_contents);
162
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);
166       }
167
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'];
171         continue;
172       }
173
174       // Run jigsaw.w3.org validator.
175       $output[$filename]['jigsaw.w3.org'] = $this->testW3C($filename, $options);
176
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']);
184             }
185           }
186         }
187         unset($value);
188       }
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']);
195             }
196           }
197         }
198         unset($value);
199       }
200
201       // Save data.
202       $file_info[$filename]['validation']['w3'] = $output[$filename]['jigsaw.w3.org'];
203       $this->advaggFiles->set($filename, $file_info[$filename]);
204     }
205     return $output;
206   }
207
208   /**
209    * Given a CSS file, test to make sure it is valid CSS.
210    *
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.
215    *
216    * @return array
217    *   Info from the w3c server.
218    */
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;
225     }
226
227     // Add in defaults.
228     $validator_options += [
229       'output' => 'soap12',
230       'warning' => '1',
231       'profile' => 'css3',
232       'usermedium' => 'all',
233       'lang' => 'en',
234     ];
235
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;
241     try {
242       $data = $this->httpClient
243         ->get($url)
244         ->getBody();
245     }
246     catch (RequestException $e) {
247       watchdog_exception('AdvAgg Validator', $e);
248     }
249     catch (\Exception $e) {
250       watchdog_exception('AdvAgg Validator', $e);
251     }
252     if (!empty($data)) {
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']);
258       }
259       elseif (isset($validator_options['uri'])) {
260         unset($validator_options['uri']);
261       }
262       $return['options'] = $validator_options;
263       return $return;
264     }
265
266     return ['error' => t('W3C Server did not return a 200 or request data was empty.')];
267   }
268
269   /**
270    * {@inheritdoc}
271    */
272   private function parseSoapResponse($xml) {
273     $doc = new DOMDocument();
274     $response = [];
275
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.
279       return $response;
280     }
281
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;
288       }
289     }
290
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;
295     }
296     else {
297       $response['validity'] = FALSE;
298       $errors = $doc->getElementsByTagName('error');
299       foreach ($errors as $error) {
300         $response['errors'][] = $this->domExtractor($error);
301       }
302     }
303
304     // Get warnings.
305     $warnings = $doc->getElementsByTagName('warning');
306     foreach ($warnings as $warning) {
307       $response['warnings'][] = $this->domExtractor($warning);
308     }
309
310     // Return response array.
311     return $response;
312   }
313
314 }