Completed
Push — 7.x-1.x ( 2f9e3c...1070be )
by Frédéric G.
01:36
created

qa.module (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * @file
5
 * OSInet Quality Assurance module for Drupal
6
 *
7
 * @copyright Copyright (C) 2005-2018 Frederic G. MARAND for Ouest Systèmes Informatiques (OSInet)
8
 *
9
 * @since DRUPAL-4-6
10
 *
11
 * @license Licensed under the disjunction of the CeCILL, version 2 and General Public License version 2 and later
12
 *
13
 * License note: QA is distributed by OSInet to its customers under the
14
 * CeCILL 2.0 license. OSInet support services only apply to the module
15
 * when distributed by OSInet, not by any third-party further down the
16
 * distribution chain.
17
 *
18
 * If you obtained QA from drupal.org, that site received it under the
19
 * GPLv2 license and can therefore distribute it under the GPLv2, and
20
 * so can you and just anyone down the chain as long as the GPLv2 terms
21
 * are abided by, the module distributor in that case being the
22
 * drupal.org organization or the downstream distributor, not OSInet.
23
 */
24
25
use Drupal\qa\Exportable;
26
use Drupal\qa\Plugin\Qa\Control\BaseControl;
27
use Drupal\qa\Plugin\Qa\Control\BasePackage;
28
use Drupal\qa\Plugin\Qa\Control\Variable\Variable;
29
30
/**
31
 * Implements hook_init().
32
 *
33
 * - add module CSS.
34
 *
35
 * TODO probabaly remove the CSS from here.
36
 */
37
function qa_init() {
38
  drupal_add_css(drupal_get_path('module', 'qa') .'/qa.css');
39
}
40
41
/**
42
 * Implements hook_boot().
43
 *
44
 * Menu loaders may need objects before hook_init().
45
 *
46
 * - register custom autoloader.
47
 */
48
function qa_boot() {
49
  spl_autoload_register(qa_autoload_psr4::class);
50
}
51
52
/**
53
 * Legacy Dedicated autoloader for QA.
54
 *
55
 * Only load symbols in the Drupal\qa namespace.
56
 *
57
 * @param string $name
58
 *
59
 * @deprecated
60
 */
61
function qa_autoload($name) {
62
  // Adjust verbosity if needed.
63
  $verbose = FALSE;
64
65
  $verbose && watchdog('qa/autoload', 'Loading %name', array('%name' => $name), WATCHDOG_DEBUG);
66
  if (strpos($name, 'Drupal\qa\\') !== 0) {
67
    return;
68
  }
69
70
  $module_path = drupal_get_path('module', 'qa');
71
  $path_array = explode('\\', $name);
72
  $filename = array_pop($path_array);
73
  array_splice($path_array, 0, 2, [$module_path, 'src/Plugin/Qa/Control']);
74
  $path = implode('/', $path_array);
75
  if (!is_dir($path) || !is_readable($path)) {
76
    $args = array('%path' => $path);
77
    drupal_set_message(t("Cannot read plugins directory %path.", $args), 'warning');
78
    watchdog('qa', "Cannot read plugins directory %path", $args, WATCHDOG_WARNING);
79
  }
80
  $path_array[] = "{$filename}.php";
81
  $path = implode('/', $path_array);
82
  $sts = include_once $path;
83
  $verbose && drupal_set_message(t('QA Autoloaded %path: @result', array(
84
    '%path' => $path,
85
    '@result' => $sts ? t('Success'): t('Failure'),
86
  )));
87
}
88
89
function qa_autoload_psr4($class) {
90
  // project-specific namespace prefix
91
  $prefix = 'Drupal\\qa\\';
92
93
  // base directory for the namespace prefix
94
  $base_dir = drupal_get_path('module', 'qa') . '/src/';
95
96
  // does the class use the namespace prefix?
97
  $len = strlen($prefix);
98
  if (strncmp($prefix, $class, $len) !== 0) {
99
    // no, move to the next registered autoloader
100
    return;
101
  }
102
103
  // get the relative class name
104
  $relative_class = substr($class, $len);
105
106
  // replace the namespace prefix with the base directory, replace namespace
107
  // separators with directory separators in the relative class name, append
108
  // with .php
109
  $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
110
111
  // if the file exists, require it
112
  if (file_exists($file)) {
113
    require $file;
114
  }
115
}
116
117
/**
118
 * Implements hook_menu().
119
 */
120
function qa_menu() {
121
  $items = array();
122
  $items['admin/reports/qa'] = array(
123
    'title'            => 'Quality Assurance',
124
    'description'      => 'Assisted auditing tools by OSInet',
125
    'page callback'    => 'drupal_get_form',
126
    'page arguments'   => array('qa_report_form'),
127
    'access arguments' => array('access site reports'),
128
  );
129
  $items['admin/reports/qa/projects'] = array(
130
    'title'           => 'Projects',
131
    'type'             => MENU_LOCAL_TASK,
132
    'page callback'    => 'qa_report_projects',
133
    'access arguments' => array('access site reports'),
134
    'file'             => 'qa_projects.inc',
135
  );
136
  $items['admin/reports/qa/variable'] = array(
137
    'title'           => 'Variables',
138
    'type'             => MENU_LOCAL_TASK,
139
    'page callback'    => 'qa_report_variables',
140
    'access arguments' => array('access site reports'),
141
    'file'             => 'qa_variables.inc',
142
  );
143
  $items['admin/reports/qa/variable/%qa_variable'] = array(
144
    'title'           => 'Variables',
145
    'type'             => MENU_CALLBACK,
146
    'page callback'    => 'qa_report_variable',
147
    'page arguments'   => array(4),
148
    'access arguments' => array('access site reports'),
149
    'file'             => 'qa_variables.inc',
150
  );
151
152
  $items['admin/reports/qa/results'] = array(
153
    'title'            => 'Quality Assurance results',
154
    'page callback'    => 'qa_report_results',
155
    'page arguments'   => array(),
156
    'access arguments' => array('access site reports'),
157
    'type'             => MENU_CALLBACK,
158
  );
159
  $items['admin/reports/qa/list'] = array(
160
    'title'            => 'QA Tests',
161
    'type'             => MENU_DEFAULT_LOCAL_TASK,
162
  );
163
  $items['admin/reports/qa/dependencies'] = array(
164
    'title'            => 'Dependencies',
165
    'type'             => MENU_LOCAL_TASK,
166
    'page callback'    => 'qa_page_dependencies',
167
    'access arguments' => array('access site reports'),
168
    'file'             => 'qa_dependencies.inc',
169
    'weight'           => 1,
170
  );
171
  return $items;
172
}
173
174
/**
175
 * Page callback for qa/dependencies
176
 *
177
 * TODO convert to native Image_GraphViz to remove dependency on graphviz_filter
178
 * XXX convert to Grafizzi to remove dependency on Image_GraphViz
179
 *
180
 * @return string
181
 */
182
function qa_page_dependencies() {
183
  $G = qa_dependencies();
184
  // passed by reference: cannot pass a function return
185
  return graphviz_filter_render($G);
186
}
187
188
/**
189
 * Batch conclusion callback.
190
 *
191
 * @param boolean $success
192
 * @param array $results
193
 * @param array $operations
194
 */
195
function qa_report_finished($success, $results, $operations) {
196
  unset($results['#message']);
197
  if ($success) {
198
    $message = format_plural(count($results), 'One control pass ran.', '@count control passes ran.');
199
  }
200
  else {
201
    $message = t('Finished with an error.');
202
  }
203
  drupal_set_message($message);
204
  $_SESSION['qa_results'] = $results;
205
  drupal_goto('admin/reports/qa/results');
206
}
207
208
/**
209
 * Results page for QA Controls batch.
210
 *
211
 * @link http://www.php.net/manual/fr/function.unserialize.php @endlink
212
 */
213
function qa_report_results() {
214
  if (empty($_SESSION['qa_results'])) {
215
    drupal_goto('admin/reports/qa');
216
  }
217
  // Work around incomplete classes
218
  $results = unserialize(serialize($_SESSION['qa_results']));
219
220
  $header = array(
221
    t('Control'),
222
    t('Status'),
223
    t('Results'),
224
  );
225
  $data = array();
226
  foreach ($results as $pass) {
227
    $control = $pass->control;
228
    $data[] = array(
229
      $control->title,
230
      $pass->status
231
        ? theme('image', array(
232
          'path' => 'misc/watchdog-ok.png',
233
          'alt' => t('OK'),
234
          ))
235
        : theme('image', array(
236
          'path' => 'misc/watchdog-error.png',
237
          'alt' => t('Error'),
238
          )),
239
      $pass->result,
240
    );
241
  }
242
  $ret = theme('table', array(
243
    'header' => $header,
244
    'rows' => $data,
245
    'attributes' => array(
246
      'id' => 'qa-results',
247
    )));
248
  // unset($_SESSION['qa_results']);
249
  return $ret;
250
}
251
252
/**
253
 * Form builder for QA packages/controls selection form.
254
 *
255
 * @return array
256
 */
257
function qa_report_form($form, $form_state) {
258
  $form = array();
259
  $base = drupal_get_path('module', 'qa');
260
  $packages = Exportable::getClasses($base, BasePackage::class);
261
  ksort($packages);
262
  foreach ($packages as $package_name => $package) {
263
    $collapsed = TRUE;
264
    $form[$package_name] = array(
265
      '#type' => 'fieldset',
266
      '#title' => filter_xss_admin($package->title),
267
      '#description' => filter_xss_admin($package->description),
268
      '#collapsible' => TRUE,
269
    );
270
    $controls = $package->getClasses($package->dir, BaseControl::class);
271
272
    foreach ($controls as $control_name => $control) {
273
      $default_value = isset($_SESSION[$control_name])
274
        ? $_SESSION[$control_name]
275
        : NULL;
276
      if ($default_value) {
277
        $collapsed = FALSE;
278
      }
279
280
      $deps = array();
281
      $met = TRUE;
282
      foreach ($control->getDependencies() as $dep_name) {
283
        if (module_exists($dep_name)) {
284
          $deps[] = t('@module (<span class="admin-enabled">available</span>)', array('@module' => $dep_name));
285
        }
286
        else {
287
          $deps[] = t('@module (<span class="admin-disabled">unavailable</span>)', array('@module' => $dep_name));
288
          $met = FALSE;
289
        }
290
      }
291
      $form[$package_name][$control_name] = array(
292
        '#type'          => 'checkbox',
293
        '#default_value' => $met ? $default_value : 0,
294
        '#title'         => filter_xss_admin($control->title),
295
        '#description'   => filter_xss_admin($control->description),
296
        '#disabled'      => !$met,
297
      );
298
      $form[$package_name][$control_name .'-dependencies'] = array(
299
        '#value' => t('Depends on: !dependencies', array(
300
          '!dependencies' => implode(', ', $deps),
301
          )),
302
        '#prefix' => '<div class="admin-dependencies">',
303
        '#suffix' => '</div>',
304
      );
305
    }
306
    $form[$package_name]['#collapsed'] = $collapsed;
307
  }
308
309
  $form['submit'] = array(
310
    '#type'  => 'submit',
311
    '#value' => t('Run controls'),
312
  );
313
314
  return $form;
315
}
316
317
/**
318
 * Submit handler for QA packages/controls selection form
319
 *
320
 * @param array $form
321
 * @param array $form_state
322
 */
323
function qa_report_form_submit($form, &$form_state) {
324
  $controls = array();
325
  foreach ($form_state['values'] as $item => $value) {
326
    if (class_exists($item) && is_subclass_of($item, BaseControl::class)) {
0 ignored issues
show
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Drupal\qa\Plugin\Qa\Control\BaseControl::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
327
      if ($value) {
328
        $controls[$item] = $value;
329
      }
330
      $_SESSION[$item] = $value;
331
    }
332
    elseif ($value == 1) {
333
      $args = array(
334
        '%control' => $item,
335
      );
336
      drupal_set_message(t('Requested invalid control %control', $args), 'error');
337
      watchdog('qa', 'Requested invalid control %control', $args, WATCHDOG_ERROR);
338
    }
339
  }
340
341
  drupal_set_message(t('Prepare to run these controls: @controls', array(
342
    '@controls' => implode(', ', array_keys($controls)))), 'status');
343
  $batch = array(
344
    'operations'       => array(),
345
    'title'            => t('QA Controls running'),
346
    'init_message'     => t('QA Controls initializing'),
347
    // 'progress_message' => t('current: @current, Remaining: @remaining, Total: @total'),
348
    'error_message'    => t('Error in QA Control'),
349
    'finished'         => 'qa_report_finished',
350
    // 'file'             => '', // only if outside module file
351
  );
352
353
  foreach ($controls as $item => $value) {
354
    $batch['operations'][] = array('qa_report_run_pass', array($item));
355
  }
356
  batch_set($batch);
357
}
358
359
/**
360
 * Batch progress step.
361
 *
362
 * @return void
363
 */
364
function qa_report_run_pass($class_name, &$context) {
365
  $name_arg = array('@class' => $class_name);
366
367
  $control = new $class_name();
368
  if (!is_object($control)) {
369
    drupal_set_message(t('Cannot obtain an instance for @class', $name_arg), 'error');
370
    $context['results']['#message'] = t('Control @class failed to run.', $name_arg);
371
    $context['message'] = t('Control @class failed to run.', $name_arg);
372
    $context['results'][$class_name] = 'wow';
373
  }
374
  else {
375
    drupal_set_message(t('Running a control instance for @class', $name_arg), 'status');
376
    $pass = $control->run();
377
    if (!$pass->status) {
378
      $context['success'] = FALSE;
379
    }
380
    $context['results']['#message'][] = t('Control @class ran', $name_arg);
381
    $context['message'] = theme('item_list', $context['results']['#message']);
382
    $context['results'][$class_name] = $pass;
383
  }
384
}
385
386
function qa_variable_load($name) {
387
  $variable = new Variable($name);
388
  if (!$variable->is_set) {
389
    return FALSE;
390
  }
391
392
  return $variable;
393
}
394