Completed
Push — 7.x-1.x ( 18d8f3...5a6692 )
by Frédéric G.
01:16
created

qa.module (5 issues)

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-2012 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
use OSInet\DrupalQA\Variable\Variable;
25
26
/**
27
 * Implements hook_init().
28
 *
29
 * - add module CSS.
30
 * - register custom autoloader.
31
 *
32
 * TODO probabaly remove the CSS from here.
33
 */
34
function qa_init() {
35
  drupal_add_css(drupal_get_path('module', 'qa') .'/qa.css');
36
}
37
38
/**
39
 * Implements hook_boot().
40
 *
41
 * Menu loaders may need objects before hook_init().
42
 */
43
function qa_boot() {
44
  spl_autoload_register('qa_autoload');
45
}
46
47
/**
48
 * Dedicated autoloader for QA.
49
 *
50
 * Only load symbols in the OSInet\DrupalQA namespace.
51
 *
52
 * @param string $name
53
 */
54
function qa_autoload($name) {
55
  // Adjust verbosity if needed.
56
  $verbose = FALSE;
57
58
  $verbose && watchdog('qa/autoload', 'Loading %name', array('%name' => $name), WATCHDOG_DEBUG);
59
  if (strpos($name, 'OSInet\DrupalQA\\') !== 0) {
60
    return;
61
  }
62
63
  $module_path = drupal_get_path('module', 'qa');
64
  $path_array = explode('\\', $name);
65
  $filename = array_pop($path_array);
66
  array_unshift($path_array, $module_path);
67
  $path = implode('/', $path_array);
68
  if (!is_dir($path) || !is_readable($path)) {
69
    $args = array('%path' => $path);
70
    drupal_set_message(t("Cannot read plugins directory %path.", $args), 'warning');
71
    watchdog('qa', "Cannot read plugins directory %path", $args, WATCHDOG_WARNING);
72
  }
73
  $path_array[] = "{$filename}.php";
74
  $path = implode('/', $path_array);
75
  $sts = include_once $path;
76
  $verbose && drupal_set_message(t('QA Autoloaded %path: @result', array(
77
    '%path' => $path,
78
    '@result' => $sts ? t('Success'): t('Failure'),
79
  )));
80
}
81
82
/**
83
 * Implements hook_menu().
84
 */
85
function qa_menu() {
86
  $items = array();
87
  $items['admin/reports/qa'] = array(
88
    'title'            => 'Quality Assurance',
89
    'description'      => 'Assisted auditing tools by OSInet',
90
    'page callback'    => 'drupal_get_form',
91
    'page arguments'   => array('qa_report_form'),
92
    'access arguments' => array('access site reports'),
93
  );
94
  $items['admin/reports/qa/projects'] = array(
95
    'title'           => 'Projects',
96
    'type'             => MENU_LOCAL_TASK,
97
    'page callback'    => 'qa_report_projects',
98
    'access arguments' => array('access site reports'),
99
    'file'             => 'qa_projects.inc',
100
  );
101
  $items['admin/reports/qa/variable'] = array(
102
    'title'           => 'Variables',
103
    'type'             => MENU_LOCAL_TASK,
104
    'page callback'    => 'qa_report_variables',
105
    'access arguments' => array('access site reports'),
106
    'file'             => 'qa_variables.inc',
107
  );
108
  $items['admin/reports/qa/variable/%qa_variable'] = array(
109
    'title'           => 'Variables',
110
    'type'             => MENU_CALLBACK,
111
    'page callback'    => 'qa_report_variable',
112
    'page arguments'   => array(4),
113
    'access arguments' => array('access site reports'),
114
    'file'             => 'qa_variables.inc',
115
  );
116
117
  $items['admin/reports/qa/results'] = array(
118
    'title'            => 'Quality Assurance results',
119
    'page callback'    => 'qa_report_results',
120
    'page arguments'   => array(),
121
    'access arguments' => array('access site reports'),
122
    'type'             => MENU_CALLBACK,
123
  );
124
  $items['admin/reports/qa/list'] = array(
125
    'title'            => 'QA Tests',
126
    'type'             => MENU_DEFAULT_LOCAL_TASK,
127
  );
128
  $items['admin/reports/qa/dependencies'] = array(
129
    'title'            => 'Dependencies',
130
    'type'             => MENU_LOCAL_TASK,
131
    'page callback'    => 'qa_page_dependencies',
132
    'access arguments' => array('access site reports'),
133
    'file'             => 'qa_dependencies.inc',
134
    'weight'           => 1,
135
  );
136
  return $items;
137
}
138
139
/**
140
 * Page callback for qa/dependencies
141
 *
142
 * TODO convert to native Image_GraphViz to remove dependency on graphviz_filter
143
 * XXX convert to Grafizzi to remove dependency on Image_GraphViz
144
 *
145
 * @return string
146
 */
147
function qa_page_dependencies() {
148
  $G = qa_dependencies();
149
  // passed by reference: cannot pass a function return
150
  return graphviz_filter_render($G);
151
}
152
153
/**
154
 * Batch conclusion callback.
155
 *
156
 * @param boolean $success
157
 * @param array $results
158
 * @param array $operations
159
 */
160
function qa_report_finished($success, $results, $operations) {
0 ignored issues
show
The parameter $operations is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
161
  unset($results['#message']);
162
  if ($success) {
163
    $message = format_plural(count($results), 'One control pass ran.', '@count control passes ran.');
164
  }
165
  else {
166
    $message = t('Finished with an error.');
167
  }
168
  drupal_set_message($message);
169
  $_SESSION['qa_results'] = $results;
170
  drupal_goto('admin/reports/qa/results');
171
}
172
173
/**
174
 * Results page for QA Controls batch.
175
 *
176
 * @link http://www.php.net/manual/fr/function.unserialize.php @endlink
177
 */
178
function qa_report_results() {
179
  if (empty($_SESSION['qa_results'])) {
180
    drupal_goto('admin/reports/qa');
181
  }
182
  // Work around incomplete classes
183
  $results = unserialize(serialize($_SESSION['qa_results']));
184
185
  $header = array(
186
    t('Control'),
187
    t('Status'),
188
    t('Results'),
189
  );
190
  $data = array();
191
  foreach ($results as $pass) {
192
    $control = $pass->control;
193
    $data[] = array(
194
      $control->title,
195
      $pass->status
196
        ? theme('image', array(
197
          'path' => 'misc/watchdog-ok.png',
198
          'alt' => t('OK'),
199
          ))
200
        : theme('image', array(
201
          'path' => 'misc/watchdog-error.png',
202
          'alt' => t('Error'),
203
          )),
204
      $pass->result,
205
    );
206
  }
207
  $ret = theme('table', array(
208
    'header' => $header,
209
    'rows' => $data,
210
    'attributes' => array(
211
      'id' => 'qa-results',
212
    )));
213
  // unset($_SESSION['qa_results']);
214
  return $ret;
215
}
216
217
/**
218
 * Form builder for QA packages/controls selection form.
219
 *
220
 * @return array
221
 */
222
function qa_report_form($form, $form_state) {
0 ignored issues
show
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $form_state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
223
  $form = array();
224
  $base = drupal_get_path('module', 'qa');
225
  $packages = OSInet\DrupalQA\Exportable::getClasses($base, 'OSInet\DrupalQA\BasePackage');
226
  ksort($packages);
227
  foreach ($packages as $package_name => $package) {
228
    $collapsed = TRUE;
229
    $form[$package_name] = array(
230
      '#type' => 'fieldset',
231
      '#title' => filter_xss_admin($package->title),
232
      '#description' => filter_xss_admin($package->description),
233
      '#collapsible' => TRUE,
234
    );
235
    $controls = $package->getClasses($package->dir, 'OSInet\DrupalQA\BaseControl');
236
237
    foreach ($controls as $control_name => $control) {
238
      $default_value = isset($_SESSION[$control_name])
239
        ? $_SESSION[$control_name]
240
        : NULL;
241
      if ($default_value) {
242
        $collapsed = FALSE;
243
      }
244
245
      $deps = array();
246
      $met = TRUE;
247
      foreach ($control->getDependencies() as $dep_name) {
248
        if (module_exists($dep_name)) {
249
          $deps[] = t('@module (<span class="admin-enabled">available</span>)', array('@module' => $dep_name));
250
        }
251
        else {
252
          $deps[] = t('@module (<span class="admin-disabled">unavailable</span>)', array('@module' => $dep_name));
253
          $met = FALSE;
254
        }
255
      }
256
      $form[$package_name][$control_name] = array(
257
        '#type'          => 'checkbox',
258
        '#default_value' => $met ? $default_value : 0,
259
        '#title'         => filter_xss_admin($control->title),
260
        '#description'   => filter_xss_admin($control->description),
261
        '#disabled'      => !$met,
262
      );
263
      $form[$package_name][$control_name .'-dependencies'] = array(
264
        '#value' => t('Depends on: !dependencies', array(
265
          '!dependencies' => implode(', ', $deps),
266
          )),
267
        '#prefix' => '<div class="admin-dependencies">',
268
        '#suffix' => '</div>',
269
      );
270
    }
271
    $form[$package_name]['#collapsed'] = $collapsed;
272
  }
273
274
  $form['submit'] = array(
275
    '#type'  => 'submit',
276
    '#value' => t('Run controls'),
277
  );
278
279
  return $form;
280
}
281
282
/**
283
 * Submit handler for QA packages/controls selection form
284
 *
285
 * @param array $form
286
 * @param array $form_state
287
 */
288
function qa_report_form_submit($form, &$form_state) {
0 ignored issues
show
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
289
  $controls = array();
290
  foreach ($form_state['values'] as $item => $value) {
291
    if (class_exists($item) && is_subclass_of($item, '\OSInet\DrupalQA\BaseControl')) {
0 ignored issues
show
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
292
      if ($value) {
293
        $controls[$item] = $value;
294
      }
295
      $_SESSION[$item] = $value;
296
    }
297
    elseif ($value == 1) {
298
      $args = array(
299
        '%control' => $item,
300
      );
301
      drupal_set_message(t('Requested invalid control %control', $args), 'error');
302
      watchdog('qa', 'Requested invalid control %control', $args, WATCHDOG_ERROR);
303
    }
304
  }
305
306
  drupal_set_message(t('Prepare to run these controls: @controls', array(
307
    '@controls' => implode(', ', array_keys($controls)))), 'status');
308
  $batch = array(
309
    'operations'       => array(),
310
    'title'            => t('QA Controls running'),
311
    'init_message'     => t('QA Controls initializing'),
312
    // 'progress_message' => t('current: @current, Remaining: @remaining, Total: @total'),
313
    'error_message'    => t('Error in QA Control'),
314
    'finished'         => 'qa_report_finished',
315
    // 'file'             => '', // only if outside module file
316
  );
317
318
  foreach ($controls as $item => $value) {
319
    $batch['operations'][] = array('qa_report_run_pass', array($item));
320
  }
321
  batch_set($batch);
322
}
323
324
/**
325
 * Batch progress step.
326
 *
327
 * @return void
328
 */
329
function qa_report_run_pass($class_name, &$context) {
330
  $name_arg = array('@class' => $class_name);
331
332
  $control = new $class_name();
333
  if (!is_object($control)) {
334
    drupal_set_message(t('Cannot obtain an instance for @class', $name_arg), 'error');
335
    $context['results']['#message'] = t('Control @class failed to run.', $name_arg);
336
    $context['message'] = t('Control @class failed to run.', $name_arg);
337
    $context['results'][$class_name] = 'wow';
338
  }
339
  else {
340
    drupal_set_message(t('Running a control instance for @class', $name_arg), 'status');
341
    $pass = $control->run();
342
    if (!$pass->status) {
343
      $context['success'] = FALSE;
344
    }
345
    $context['results']['#message'][] = t('Control @class ran', $name_arg);
346
    $context['message'] = theme('item_list', $context['results']['#message']);
347
    $context['results'][$class_name] = $pass;
348
  }
349
}
350
351
function qa_variable_load($name) {
352
  $variable = new Variable($name);
353
  if (!$variable->is_set) {
354
    return FALSE;
355
  }
356
357
  return $variable;
358
}
359