node_accessibility.module in Node Accessibility 7
Same filename and directory in other branches
Module file for the node accessibility project.
File
node_accessibility.moduleView source
<?php
/**
* @file
* Module file for the node accessibility project.
*/
/**
* Implements hook_help().
*/
function node_accessibility_help($path, $arg) {
$output = '';
switch ($path) {
case "admin/help#node_accessibility":
$output .= '<p>' . t("This module provides accessibilty validation support on node entities.") . '</p>';
return $output;
}
}
/**
* Implements hook_quail_api_permission_alter().
*/
function node_accessibility_quail_api_permission_alter(&$permissions) {
if (!is_array($permissions)) {
$permissions = array();
}
$permissions['access node accessibility tab'] = array(
'title' => t("Access Node Accessibility Tab"),
'description' => t("Grants permissions to access the accessibility tab on node pages."),
);
$permissions['perform node accessibility validation'] = array(
'title' => t("Perform node accessibility validation"),
'description' => t("Grants permissions to use the perform a manual validation of any given node."),
);
}
/**
* Implements hook_action_info().
*/
function node_accessibility_action_info() {
return array(
'node_accessibility_validate_action' => array(
'type' => 'node',
'label' => t("Accessibility validate content"),
'configurable' => FALSE,
'triggers' => array(
'node_presave',
'node_insert',
'node_update',
),
),
'node_accessibility_delete_action' => array(
'type' => 'node',
'label' => t("Delete accessibility statistics for content"),
'configurable' => FALSE,
'triggers' => array(
'node_presave',
'node_insert',
'node_update',
),
),
);
}
/**
* Implements hook_views_api().
*/
function node_accessibility_views_api() {
return array(
'api' => 2.0,
'path' => drupal_get_path('module', 'node_accessibility'),
);
}
/**
* Implements hook_menu().
*/
function node_accessibility_menu() {
$items = array();
$items['node/%node/accessibility'] = array(
'title' => "Accessibility",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'node_accessibility_accessibility_tab_page',
1,
),
'access callback' => 'node_accessibility_access_accessibility_tab',
'access arguments' => array(
1,
),
'file' => 'pages.inc',
'file path' => drupal_get_path('module', 'node_accessibility') . '/includes',
'weight' => 8,
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
);
$items['node/%node/accessibility/%/revision'] = array(
'title' => "Accessibility",
'load arguments' => array(
3,
3,
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'node_accessibility_accessibility_tab_page',
1,
3,
),
'access callback' => 'node_accessibility_access_accessibility_tab',
'access arguments' => array(
1,
),
'file' => 'pages.inc',
'file path' => drupal_get_path('module', 'node_accessibility') . '/includes',
);
return $items;
}
/**
* Implements hook_menu_alter().
*/
function node_accessibility_menu_alter(&$items) {
// provide a failsafe way to prevent menu override conflicts
$alter_menu = variable_get('node_accessibility_alter_revision_menu', TRUE);
if ($alter_menu) {
$items['node/%node/revisions']['page callback'] = 'node_accessibility_revision_overview';
}
}
/**
* Implements hook_menu().
*/
function node_accessibility_admin_paths() {
$paths = array();
$paths['node/*/accessibility'] = TRUE;
$paths['node/*/accessibility/*/revision'] = TRUE;
return $paths;
}
/**
* Implements hook_node_delete().
*/
function node_accessibility_node_delete($node) {
if (!is_object($node)) {
if (class_exists('cf_error')) {
cf_error::invalid_object('node');
}
return;
}
node_accessibility_delete_node_problems($node->nid, NULL);
}
/**
* Implements hook_node_revision_delete().
*/
function node_accessibility_node_revision_delete($node) {
if (!is_object($node)) {
if (class_exists('cf_error')) {
cf_error::invalid_object('node');
}
return;
}
node_accessibility_delete_node_problems($node->nid, $node->vid);
}
/**
* Implements hook_theme().
*/
function node_accessibility_theme($existing, $type, $theme, $path) {
$themes = array();
$themes['node_accessibility_information'] = array(
'template' => 'node_accessibility_information',
'variables' => array(
'node' => NULL,
),
'path' => drupal_get_path('module', 'node_accessibility') . '/includes',
);
return $themes;
}
/**
* Template preprocess function for node_accessibility_information.tpl.php
*/
function template_preprocess_node_accessibility_information(&$variables) {
$variables['workbench_moderation'] = FALSE;
$variables['published'] = t("No");
drupal_add_css(drupal_get_path('module', 'node_accessibility') . '/includes/node_accessibility_information.css');
if (!isset($variables['node']) || !is_object($variables['node'])) {
if (class_exists('cf_error')) {
cf_error::invalid_object('node');
}
return;
}
if ($variables['node']->status) {
$variables['published'] = t("Yes");
}
if (module_exists('workbench_moderation') && property_exists($variables['node'], 'workbench_moderation')) {
$variables['workbench_moderation'] = TRUE;
$variables['live'] = t("No");
$variables['current'] = t("No");
if (isset($variables['node']->workbench_moderation['current']) && is_object($variables['node']->workbench_moderation['current'])) {
if ($variables['node']->workbench_moderation['current']->vid == $variables['node']->vid) {
$variables['current'] = t("Yes");
}
}
if (isset($variables['node']->workbench_moderation['published']) && is_object($variables['node']->workbench_moderation['published'])) {
if ($variables['node']->workbench_moderation['published']->vid == $variables['node']->vid) {
$variables['live'] = t("Yes");
}
}
}
}
/**
* Checks if user can access the accessibility tab.
*
* @param object $node
* A node object whose access is to be returned.
*
* @return bool
* TRUE if user can make conversions using this type, FALSE otherwise.
*/
function node_accessibility_access_accessibility_tab($node) {
$access = FALSE;
if (!is_object($node)) {
if (class_exists('cf_error')) {
cf_error::invalid_object('node');
}
return $access;
}
if (node_access('view', $node)) {
$access = user_access('access node accessibility tab');
if ($access && node_accessibility_is_enabled($node->type)) {
$access = TRUE;
}
else {
$access = FALSE;
}
}
return $access;
}
/**
* Implements hook_form_FORM_ID_alter() for the node type form.
*/
function node_accessibility_form_node_type_form_alter(&$form, &$form_state, $form_id) {
$node_type_settings_object = FALSE;
$node_type_settings_array = array(
'type' => NULL,
'required' => NULL,
'standards' => NULL,
);
$standards = quail_api_get_standards_list(NULL, 'snippet');
$display_levels = quail_api_get_display_levels_list(NULL);
$methods = quail_api_get_validation_methods(NULL);
$methods_list = quail_api_get_validation_methods_list(NULL);
$filter_formats = filter_formats();
$formats = array();
$options = array();
foreach ($filter_formats as $filter_format => $filter_format_settings) {
$formats[$filter_format] = $filter_format_settings->name;
}
$default_enabled = 'disabled';
$default_standards = array();
$default_method = 'quail_api_method_immediate';
$default_format = 'full_html';
if (!empty($form['#node_type']->type)) {
$node_type = $form['#node_type']->type;
$node_type_settings = node_accessibility_load_node_type_settings();
if (isset($node_type_settings[$node_type])) {
$default_enabled = isset($node_type_settings[$node_type]['required']) && $node_type_settings[$node_type]['required'] ? 'required' : 'optional';
$default_standards = isset($node_type_settings[$node_type]['standards']) ? $node_type_settings[$node_type]['standards'] : $default_standards;
$default_method = isset($node_type_settings[$node_type]['method']) ? $node_type_settings[$node_type]['method'] : $default_method;
$default_format = isset($node_type_settings[$node_type]['format']) ? $node_type_settings[$node_type]['format'] : $default_format;
}
}
$form['node_accessibility_validation'] = array(
'#type' => 'fieldset',
'#title' => t("Accessibility Validation"),
'#description' => t("Provides options for enabled and disabled accessibility validation on text stored in this field."),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#tree' => TRUE,
'#group' => 'additional_settings',
);
$form['node_accessibility_validation']['quail_enabled'] = array(
'#type' => 'select',
'#title' => t("Accessibility Validation"),
'#default_value' => $default_enabled,
'#options' => array(
'disabled' => t("Disabled"),
'optional' => t("Enabled (optional)"),
'required' => t("Enabled (required)"),
),
'#description' => t("Choose if accessibility validation should be enabled for this node type and if the node type is required to pass accessibility validation."),
);
$form['node_accessibility_validation']['method'] = array(
'#type' => 'select',
'#title' => t("Validation Method"),
'#default_value' => $default_method,
'#options' => $methods_list,
'#description' => t("Choose the way in which validation is performed."),
'#dependency' => array(
'edit-node-accessibility-validation-quail-enabled' => array(
'optional',
'required',
),
),
);
// add documentation for each option
$form['node_accessibility_validation']['method']['#description'] .= '<ul>';
foreach ($methods as $method => $method_settings) {
$form['node_accessibility_validation']['method']['#description'] .= '<li>' . $method_settings['human_name'] . ': ' . $method_settings['description'] . '</li>';
}
$form['node_accessibility_validation']['method']['#description'] .= '</ul>';
$form['node_accessibility_validation']['format'] = array(
'#type' => 'select',
'#title' => t("Markup Format"),
'#default_value' => $default_format,
'#options' => $formats,
'#description' => t("Choose the filter to use when presenting the results of validation checks. This is only used for the html markup associated with the problem or suggestion."),
'#dependency' => array(
'edit-node-accessibility-validation-quail-enabled' => array(
'optional',
'required',
),
),
);
$form['node_accessibility_validation']['standards'] = array(
'#type' => 'checkboxes',
'#title' => t("Accessibility Standards"),
'#default_value' => $default_standards,
'#options' => $standards,
'#description' => t("Choose which accessibility standards to validate against. It is strongly suggested that only 1 standard should be used because many of the validation tests are performed by more than one standard."),
'#dependency' => array(
'edit-node-accessibility-validation-quail-enabled' => array(
'optional',
'required',
),
),
);
$form['#submit'][] = 'node_accessibility_node_type_form_submit';
}
/**
* Handles submitting the accessible content specific node type settings.
*
* @param array $form
* A form array
* @param array $form_state
* A form state
*/
function node_accessibility_node_type_form_submit($form, &$form_state) {
if (empty($form_state['values']['type'])) {
if (class_exists('cf_error')) {
cf_error::empty_string('form_state[values][type]');
}
}
$node_type_settings = node_accessibility_load_node_type_settings();
$columns = array(
'enabled',
'required',
'standards',
'method',
'format',
);
$record = array();
$record['type'] = $form_state['values']['type'];
$record['method'] = isset($form_state['values']['node_accessibility_validation']['method']) ? $form_state['values']['node_accessibility_validation']['method'] : 'quail_api_method_immediate';
$record['format'] = isset($form_state['values']['node_accessibility_validation']['format']) ? $form_state['values']['node_accessibility_validation']['format'] : 'full_html';
if (isset($form_state['values']['node_accessibility_validation']['quail_enabled'])) {
$record['required'] = $form_state['values']['node_accessibility_validation']['quail_enabled'] === 'required';
}
else {
$record['required'] = FALSE;
}
if (isset($form_state['values']['node_accessibility_validation']['standards'])) {
foreach ($form_state['values']['node_accessibility_validation']['standards'] as $key => $value) {
if ($value == '0') {
unset($form_state['values']['node_accessibility_validation']['standards'][$key]);
}
}
$record['standards'] = $form_state['values']['node_accessibility_validation']['standards'];
}
else {
$record['standards'] = array();
}
$node_type_settings[$form_state['values']['type']] = $record;
variable_set('node_accessibility_node_type_settings', $node_type_settings);
}
/**
* Implements hook_node_operations().
*/
function node_accessibility_node_operations() {
$operations = array(
'node_accessibility_operation_validate' => array(
'label' => t("Accessibility Validate selected content"),
'callback' => 'node_accessibility_operation_validate',
),
);
return $operations;
}
/**
* Perform validation on any number of nodes.
*
* Only nodes that have accessibility enabled will be validated in this way.
* All other nodes are silently ignored.
*
* @param array $nids
* An array of node ids
*/
function node_accessibility_operation_validate($nids) {
$nodes = node_load_multiple($nids);
$node_type_settings = node_accessibility_load_node_type_settings();
$result = TRUE;
foreach ($nodes as $key => $node) {
if (!is_object($node)) {
continue;
}
if (!array_key_exists($node->type, $node_type_settings)) {
continue;
}
if ($node_type_settings[$node->type] === FALSE) {
continue;
}
$methods = quail_api_get_validation_methods(NULL);
$database = FALSE;
$node_settings = $node_type_settings[$node->type];
if (!empty($node_settings['method']) && is_array($methods) && array_key_exists('database', $methods[$node_settings['method']])) {
$database = $methods[$node_settings['method']]['database'];
}
$results = node_accessibility_perform_validation(array(
$node,
), array(), NULL, NULL);
if (isset($results[$node->nid][$node->vid]['report'])) {
$reports = $results[$node->nid][$node->vid]['report'];
unset($results);
if ($database && !empty($reports)) {
$no_failures = TRUE;
foreach ($reports as $severity => $severity_results) {
if (isset($severity_results['total']) && $severity_results['total'] > 0) {
$no_failures = FALSE;
break;
}
}
if ($no_failures) {
node_accessibility_delete_node_problems($node->nid, $node->vid);
}
else {
node_accessibility_save_node_problems($node->nid, $node->vid, $reports);
}
}
}
else {
$result = FALSE;
}
}
if ($result) {
drupal_set_message(t("The validation has been performed."));
}
else {
drupal_set_message(t("Unable to perform the validation, something went wrong."), 'error');
}
}
/**
* Performs validation on the given nodes and stores the results.
*
* @param array $nodes_or_nids
* An array of node objects or node ids.
* @param array $vids
* (optional) The an array vids to use during validation, with the following
* structure:
* - nid: an array of vids With the array key being the node id.
* - example: $vids = array('1' => array('2', '3', '4')) such '1' is the
* node id and 2, 3, and 4 are the vids for node 1.
* @param string|null $language
* (optional) The language to use during validation
* @param array|null $display_level
* (optional) An array of booleans representing the qual test display levels
* (defaults to quail_api_create_quail_display_level_array()).
*
* @return array
* An array of all test failures, if any.
*/
function node_accessibility_perform_validation($nodes_or_nids, $vids = array(), $language = NULL, $display_level = NULL) {
if (!is_array($nodes_or_nids)) {
if (class_exists('cf_error')) {
cf_error::invalid_array('node_or_nids');
}
return array();
}
if (!is_array($vids)) {
if (class_exists('cf_error')) {
cf_error::invalid_array('vids');
}
return array();
}
if (count($nodes_or_nids) == 0) {
return array();
}
$node_type_settings = node_accessibility_load_node_type_settings();
$results = array();
$standards = quail_api_get_standards(NULL, 'snippet');
foreach ($nodes_or_nids as $node_or_nid) {
if (is_object($node_or_nid)) {
$node = $node_or_nid;
}
else {
$node = node_load($node_or_nid);
if (!is_object($node)) {
if (class_exists('cf_error')) {
cf_error::invalid_variable('node', "Unable to load the node with the following node id: :nid.", array(
':nid' => $node_or_nid,
));
}
continue;
}
}
$node_settings = FALSE;
if (isset($node_type_settings[$node->type])) {
$node_settings = $node_type_settings[$node->type];
}
if (array_key_exists($node->nid, $vids) && !empty($vids[$node->nid])) {
$revisions = $vids[$node->nid];
}
else {
$revisions = array(
$node->vid,
);
}
$results[$node->nid] = array();
foreach ($revisions as $rid) {
$results[$node->nid][$rid] = array();
if ($node->vid == $rid) {
$revision = $node;
}
else {
$revision = node_load($node->nid, $rid);
if (!is_object($revision)) {
if (class_exists('cf_error')) {
cf_error::invalid_variable('revision', "Unable to load the node revision with the node id of :nid and the revision id of :rid.", array(
':nid' => $node->nid,
':rid' => $rid,
));
}
continue;
}
}
if (is_array($node_settings)) {
$node_view = node_view($revision, 'full', $language);
$rendered_node = drupal_render($node_view);
unset($node_view);
if (!empty($node_settings['standards'])) {
foreach ($node_settings['standards'] as $standard_name) {
$results[$revision->nid][$rid] = array_merge($results[$revision->nid][$rid], quail_api_validate_markup($rendered_node, $standards[$standard_name], $display_level));
if (module_exists('rules')) {
rules_invoke_event('node_accessibility_after_validating', $revision, $results[$node->nid][$rid]);
}
}
}
}
}
}
return $results;
}
/**
* Loads the node type settings table data for the given node type.
*
* @return array
* An array of values returned by variable_get().
*/
function node_accessibility_load_node_type_settings() {
static $node_accessibility_node_type_settings;
if (!isset($node_accessibility_node_type_settings)) {
$node_accessibility_node_type_settings = variable_get('node_accessibility_node_type_settings', array());
}
return $node_accessibility_node_type_settings;
}
/**
* Saves the node report data to the database.
*
* @param int $nid
* The node id of the node associated with the given reports.
* @param int $vid
* The node revision id of the node associated with the given reports.
* @param array $reports
* The reports array as returned by the quail library quail api reporter.
*
* @return bool
* TRUE on success, FALSE otherwise.
*/
function node_accessibility_save_node_problems($nid, $vid, $reports) {
if (!is_array($reports)) {
if (class_exists('cf_error')) {
cf_error::invalid_array('reports');
}
return FALSE;
}
$tests_known = (array) quail_api_load_tests(array(), 'machine_name');
$problems = array();
foreach ($reports as $severity => $severity_results) {
if ($severity != 'total') {
foreach ($severity_results as $test_name => $test_results) {
if ($test_name == 'total') {
continue;
}
if (!array_key_exists($test_name, $tests_known)) {
if (empty($test_results['body']['title']) || empty($test_results['body']['description'])) {
// @todo should this send a watchdog warning?
continue;
}
$test_data = array();
$test_data['machine_name'] = $test_name;
$test_data['severity'] = $severity;
$test_data['human_name'] = $test_results['body']['title'];
$test_data['description'] = $test_results['body']['description'];
$results = quail_api_save_test($test_data);
if ($results === FALSE) {
watchdog('node_accessibility', "Failed to insert :machine_name into quail api tests database table.", array(
':machine_name' => $test_name,
), WATCHDOG_ERROR);
continue;
}
// The row must be loaded from the database so that the id can be retrieved
$loaded_test = quail_api_load_tests(array(
'machine_name' => $test_name,
), NULL);
if (!isset($loaded_test['0']) || !is_object($loaded_test['0'])) {
watchdog('node_accessibility', "Failed to insert :machine_name problems into node accessibility tests database table because tests_known[:machine_name] is not a valid object.", array(
':machine_name' => $test_name,
), WATCHDOG_ERROR);
continue;
}
$tests_known[$test_name] = $loaded_test['0'];
}
foreach ($test_results['problems'] as $problem_name => $problem) {
if (empty($problem['line']) || empty($problem['element'])) {
// @todo should this send a watchdog warning?
continue;
}
$problem_data = array();
$problem_data['nid'] = $nid;
$problem_data['vid'] = $vid;
$problem_data['test_id'] = $tests_known[$test_name]->id;
$problem_data['line'] = $problem['line'];
$problem_data['element'] = $problem['element'];
$problems[] = $problem_data;
}
}
}
}
if (!empty($problems)) {
$results = node_accessibility_replace_problems($nid, $vid, $problems);
if ($results === FALSE) {
watchdog('node_accessibility', "Failed to insert :machine_name problems into node accessibility problems database table.", array(
':machine_name' => $test_name,
), WATCHDOG_ERROR);
}
}
}
/**
* Deletes the node report data from the database.
*
* This is primarly used to remove data for a node that no longer has any
* validation failures.
*
* @param int $nid
* The node id of the node associated with the given reports.
* @param int|null $vid
* (optional) The node revision id of the node associated with the given
* reports.
*
* @return bool
* TRUE on success, FALSE otherwise.
*/
function node_accessibility_delete_node_problems($nid, $vid = NULL) {
if (!is_numeric($nid)) {
if (class_exists('cf_error')) {
cf_error::invalid_numeric('nid');
}
return FALSE;
}
if (!is_null($vid) && !is_numeric($vid)) {
if (class_exists('cf_error')) {
cf_error::invalid_numeric('vid');
}
return FALSE;
}
// @todo when vid support is added, be sure to add a conditional check against vid here
$query = db_delete('node_accessibility_problems');
$query
->condition('nid', $nid);
if (!is_null($vid)) {
$query
->condition('vid', $vid);
}
try {
return $query
->execute();
} catch (Exception $e) {
if (class_exists('cf_error')) {
cf_error::on_query_execution($e);
}
}
return FALSE;
}
/**
* Returns TRUE if accessibility validation functionality is enabled.
*
* @param string $node_type
* A node type string.
*
* @return bool
* The return TRUE when accessibility is enabled for the given node type.
* Otherwise, FALSE is returned.
*/
function node_accessibility_is_enabled($node_type) {
$node_type_settings = node_accessibility_load_node_type_settings();
if (isset($node_type_settings[$node_type]) && $node_type_settings[$node_type] !== FALSE) {
return TRUE;
}
return FALSE;
}
/**
* Returns TRUE if accessibility validation functionality is required.
*
* @param string $node_type
* A node type string
*
* @return bool|null
* TRUE or FALSE depending on whether or not accessibility validation
* functionality is required for the given node type.
* NULL is returned if accessibility validation functionality is not enabled.
*/
function node_accessibility_is_required($node_type) {
$node_type_settings = node_accessibility_load_node_type_settings();
if (!isset($node_type_settings[$node_type]) || $node_type_settings[$node_type] === FALSE) {
return NULL;
}
if (isset($node_type_settings[$node_type]['required']) && $node_type_settings[$node_type]['required'] === TRUE) {
return TRUE;
}
return FALSE;
}
/**
* Loads the nodes problem data.
*
* @param array $conditions
* (optional) An array with the following possible keys:
* - 'id' The unique id representing a specific problem.
* - 'nid' the node id.
* - 'vid' the node revision id.
* - 'test_id' a numeric value representing the id of the test the problem is
* associated with.
* - 'line' a numeric value representing the line number in which a problem
* applies to.
* - 'live_only' a boolean that specifies whether or not to restrict loading
* of problems to live content (active revision). (defaults to FALSE)
* - 'unlive_only' a boolean that specifies whether or not to restrict
* loading of problems to non-live content (active revision). (defaults to
* FALSE)
* - 'published_only' a boolean that specifies whether or not to restrict
* loading of problems to published content (active revision). (defaults
* to FALSE)
* - 'unpublished_only' a boolean that specifies whether or not to restrict
* loading of problems to unpublished content (active revision). (defaults
* to FALSE)
* - 'sort_by' the name of a column to sort by.
* - 'sort_order' the order in which to sort by ('asc' or 'desc').
* - 'node_columns' a boolean that specifies whether or not to load the node
* column fields in addition to the node accessibility problems fields.
* (defaults to FALSE)
* @param string|null $keyed
* (optional) A string matching one of the following: 'id'
* When this is NULL, the default behavior is to return the array exactly as
* it was returned by the database call.
* When this is a valid string, the key names of the returned array will use
* the specified key name.
*
* @return array
* An array of database results.
*/
function node_accessibility_load_problems($conditions = array(), $keyed = NULL) {
if (!is_array($conditions)) {
if (class_exists('cf_error')) {
cf_error::invalid_array('conditions');
}
return array();
}
$query = db_select('node_accessibility_problems', 'nap');
$query
->fields('nap');
$sort_by = 'nap.nid';
$sort_order = 'ASC';
if (isset($conditions['sort_order'])) {
switch ($conditions['sort_order']) {
case 'ASC':
case 'DESC':
$sort_order = $conditions['sort_order'];
break;
}
}
if (isset($conditions['sort_by']) && is_string($conditions['sort_by'])) {
switch ($conditions['sort_by']) {
case 'id':
case 'nid':
case 'vid':
case 'test_id':
case 'line':
case 'element':
$sort_by = 'nap.' . $conditions['sort_by'];
break;
default:
$sort_by = $conditions['sort_by'];
break;
}
}
$query
->orderBy($sort_by, $sort_order);
$and = NULL;
$joined = FALSE;
if (isset($conditions['live_only']) && is_bool($conditions['live_only'])) {
$query
->innerjoin('node', 'n', 'nap.vid = n.vid');
$joined = TRUE;
}
else {
if (isset($conditions['unlive_only']) && is_bool($conditions['unlive_only'])) {
$query
->innerjoin('node', 'n', 'nap.nid = n.nid');
$joined = TRUE;
$and = db_and();
$and
->where('NOT nap.vid = n.vid');
}
}
if (isset($conditions['published_only']) && is_bool($conditions['published_only'])) {
if (!$joined) {
$query
->innerjoin('node', 'n', 'nap.nid = n.nid');
$joined = TRUE;
}
if (is_null($and)) {
$and = db_and();
}
$and
->condition('n.status', 1, '=');
}
else {
if (isset($conditions['unpublished_only']) && is_bool($conditions['unpublished_only'])) {
if (!$joined) {
$query
->innerjoin('node', 'n', 'nap.nid = n.nid');
$joined = TRUE;
}
if (is_null($and)) {
$and = db_and();
}
$and
->condition('n.status', 0, '=');
}
}
if (isset($conditions['id']) && is_numeric($conditions['id'])) {
if (is_null($and)) {
$and = db_and();
}
$and
->condition('nap.id', $conditions['id'], '=');
}
if (isset($conditions['nid']) && is_numeric($conditions['nid'])) {
if (is_null($and)) {
$and = db_and();
}
$and
->condition('nap.nid', $conditions['nid'], '=');
}
if (isset($conditions['vid']) && is_numeric($conditions['vid'])) {
if (is_null($and)) {
$and = db_and();
}
$and
->condition('nap.vid', $conditions['vid'], '=');
}
if (isset($conditions['test_id']) && is_numeric($conditions['test_id'])) {
if (is_null($and)) {
$and = db_and();
}
$and
->condition('nap.test_id', $conditions['test_id'], '=');
}
if (!empty($conditions['line'])) {
if (is_null($and)) {
$and = db_and();
}
$and
->condition('nap.line', $conditions['line'], '=');
}
if (is_object($and)) {
$query
->condition($and);
}
if (isset($conditions['node_columns']) && is_bool($conditions['node_columns'])) {
if (!$joined) {
$query
->innerjoin('node', 'n', 'nap.nid = n.nid');
$joined = TRUE;
}
$query
->fields('n');
}
if ($keyed === 'id') {
$results = array();
try {
$records = $query
->execute();
} catch (Exception $e) {
if (class_exists('cf_error')) {
cf_error::on_query_execution($e);
}
return array();
}
foreach ($records as $record) {
if (!is_object($record)) {
continue;
}
$results[$record->{$keyed}] = $record;
}
return $results;
}
try {
return $query
->execute()
->fetchAll();
} catch (Exception $e) {
if (class_exists('cf_error')) {
cf_error::on_query_execution($e);
}
}
return array();
}
/**
* This stores validation problems for a single node to the database.
*
* This will delete all pre-existing problems for the given node.
*
* @param int $nid
* The node id.
* @param int $vid
* The node revision id.
* @param array $problems
* An array of arrays of test data containing the test problems
* - each nested array should containt the following keys:
* - nid: The node id of the node.
* - vid: The node revision id of the node.
* - test_id: The id of the problem.
* - line: The line number of the problem.
* - element: The html markup of the problem.
*
* @return int|false
* The return states of either FALSE, SAVED_NEW, or SAVED_UPDATED.
*/
function node_accessibility_replace_problems($nid, $vid, $problems) {
if (!is_numeric($nid)) {
if (class_exists('cf_error')) {
cf_error::invalid_numeric('nid');
}
return FALSE;
}
if (!is_numeric($vid)) {
if (class_exists('cf_error')) {
cf_error::invalid_numeric('vid');
}
return FALSE;
}
if (!is_array($problems)) {
if (class_exists('cf_error')) {
cf_error::invalid_array('problems');
}
return FALSE;
}
$result = FALSE;
$transaction = db_transaction();
try {
$query = db_delete('node_accessibility_problems');
$query
->condition('nid', $nid);
$query
->condition('vid', $vid);
$query
->execute();
foreach ($problems as $problem) {
$result = node_accessibility_save_problem($problem);
// @todo handle return errors
}
// force transaction to execute
unset($transaction);
} catch (Exception $e) {
$transaction
->rollback();
if (class_exists('cf_error')) {
cf_error::on_query_execution($e);
}
return FALSE;
}
return $result;
}
/**
* This stores a validation problem for a given node to the database.
*
* @param array $problem_data
* An array of test data with the following keys:
* - nid: The node id of the node.
* - vid: The node revision id of the node.
* - test_id: The id of the problem.
* - line: The line number of the problem.
* - element: The html markup of the problem.
*
* @return int|false
* The return states of either FALSE, SAVED_NEW, or SAVED_UPDATED.
*/
function node_accessibility_save_problem($problem_data) {
if (!is_array($problem_data)) {
if (class_exists('cf_error')) {
cf_error::invalid_array('problem_data');
}
return FALSE;
}
$result = FALSE;
$columns = array(
'nid',
'vid',
'test_id',
'line',
'element',
);
foreach ($columns as $key) {
if (empty($problem_data[$key])) {
if (class_exists('cf_error')) {
cf_error::missing_array_key('problem_data', $key);
}
return FALSE;
}
}
$data = array();
$primary_key = array();
$results = FALSE;
if (!empty($problem_data['id'])) {
if (!is_numeric($problem_data['id'])) {
if (class_exists('cf_error')) {
cf_error::invalid_numeric('problem_data[id]');
}
return FALSE;
}
$results = node_accessibility_load_problems(array(
'id' => $problem_data['id'],
), NULL);
if (is_array($results) && !empty($results)) {
$data['id'] = $problem_data['id'];
$primary_key[] = 'id';
}
else {
// if a specific id is requested but does not exist, then it cannot be updated.
return FALSE;
}
}
$data['nid'] = $problem_data['nid'];
$data['vid'] = $problem_data['vid'];
$data['test_id'] = $problem_data['test_id'];
$data['line'] = $problem_data['line'];
$data['element'] = $problem_data['element'];
$result = drupal_write_record('node_accessibility_problems', $data, $primary_key);
return $result;
}
/**
* This restructures an array of problems as returned from database calls.
*
* The data is converted into a format that can be processed by the quail api
* theme functions.
*
* @param int $nid
* A node id the results belong to.
* @param int $vid
* A node revision id the results belong to.
* @param array|null $display_levels
* (optional) An array of display levels as returned by
* quail_api_get_display_levels().
* If NULL is passed, then this will auto-call
* quail_api_get_display_levels().
*
* @return array
* An array of database results in a format that can be processed by the
* quail_api theme functions.
*/
function node_accessibility_restructure_results($nid, $vid, $display_levels = NULL) {
$problems = (array) node_accessibility_load_problems(array(
'nid' => $nid,
'vid' => $vid,
), NULL);
$tests = quail_api_load_tests(array(), 'id');
$results = array();
if (!is_array($display_levels)) {
$display_levels = quail_api_get_display_levels(NULL);
}
foreach ($display_levels as $key => $value) {
if (empty($value['id'])) {
continue;
}
$results[$value['id']] = array(
'total' => 0,
);
}
foreach ($problems as $problem_key => $problem_data) {
if (!is_object($problem_data)) {
continue;
}
$test = $tests[$problem_data->test_id];
if (!isset($results[$test->severity][$test->machine_name])) {
$results[$test->severity][$test->machine_name] = array();
$results[$test->severity][$test->machine_name]['body'] = array();
$results[$test->severity][$test->machine_name]['body']['title'] = $test->human_name;
$results[$test->severity][$test->machine_name]['body']['description'] = $test->description;
$results[$test->severity][$test->machine_name]['problems'] = array();
}
$problem = array();
$problem['line'] = $problem_data->line;
$problem['element'] = $problem_data->element;
$results[$test->severity][$test->machine_name]['problems'][] = $problem;
$results[$test->severity]['total']++;
}
return $results;
}
/**
* Implements hook_node_type_delete().
*/
function node_accessibility_node_type_delete($info) {
if (!is_object($info)) {
if (class_exists('cf_error')) {
cf_error::invalid_object('info');
}
return;
}
$node_type_settings = variable_get('node_accessibility_node_type_settings', array());
unset($node_type_settings[$info->type]);
variable_set('node_accessibility_node_type_settings', $node_type_settings);
}
/**
* Performs accessibility validation on a given node.
*
* $param object $node
* A node object.
*
* @return array|false
* An array containing the validation results or FALSE.
*/
function node_accessibility_validate_action($node) {
if (!is_object($node)) {
if (class_exists('cf_error')) {
cf_error::invalid_object('node');
}
return FALSE;
}
$results = node_accessibility_perform_validation(array(
$node,
));
$reports = array();
$node_type_settings = node_accessibility_load_node_type_settings();
$node_settings = $node_type_settings[$node->type];
if (isset($results[$node->nid][$node->vid]['report'])) {
$reports = $results[$node->nid][$node->vid]['report'];
$methods = quail_api_get_validation_methods(NULL);
$database = isset($methods[$node_settings['method']]['database']) ? $methods[$node_settings['method']]['database'] : FALSE;
unset($results);
if ($database && !empty($reports)) {
$no_failures = TRUE;
foreach ($reports as $severity => $severity_results) {
if (isset($severity_results['total']) && $severity_results['total'] > 0) {
$no_failures = FALSE;
break;
}
}
if ($no_failures) {
node_accessibility_delete_node_problems($node->nid, $node->vid);
}
else {
node_accessibility_save_node_problems($node->nid, $node->vid, $reports);
}
}
}
watchdog('actions', "Accessibility validating node %nid:%vid.", array(
'%nid' => $node->nid,
'%vid' => $node->vid,
));
return $reports;
}
/**
* Delete accessibility validation statistics for a node.
*
* $param object $node
* A node object.
*
* @return bool
* An TRUE on success, FALSE otherwise.
*/
function node_accessibility_delete_action($node) {
if (!is_object($node)) {
if (class_exists('cf_error')) {
cf_error::invalid_object('node');
}
return FALSE;
}
$result = node_accessibility_delete_node_problems($node->nid, $node->vid);
if ($result === FALSE) {
return FALSE;
}
watchdog('actions', "Deleted accessibility validation statistics for node %nid:%vid.", array(
'%nid' => $node->nid,
'%vid' => $node->vid,
));
return TRUE;
}
/**
* Wraps or Replaces the node_revision_overview function.
*
* This adds add an "accessibility" link.
*
* $param object $node
* A node object.
*
* @return array
* An array containing the overview page structure.
*/
function node_accessibility_revision_overview($node) {
if (!user_access('access node accessibility tab')) {
return node_revision_overview($node);
}
drupal_set_title(t("Revisions for %title", array(
'%title' => $node->title,
)), PASS_THROUGH);
$header = array(
t("Revision"),
array(
'data' => t("Operations"),
'colspan' => 3,
),
);
$revisions = node_revision_list($node);
$rows = array();
$revert_permission = FALSE;
if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
$revert_permission = TRUE;
}
$delete_permission = FALSE;
if ((user_access('delete revisions') || user_access('administer nodes')) && node_access('delete', $node)) {
$delete_permission = TRUE;
}
foreach ($revisions as $revision) {
$row = array();
$operations = array();
if ($revision->current_vid > 0) {
$row[] = array(
'data' => t("!date by !username", array(
'!date' => l(format_date($revision->timestamp, 'short'), 'node/' . $node->nid),
'!username' => theme('username', array(
'account' => $revision,
)),
)) . ($revision->log != '' ? "<p class='revision-log'>" . filter_xss($revision->log) . "</p>" : ""),
'class' => array(
'revision-current',
),
);
$operations[] = array(
'data' => drupal_placeholder(t("current revision")),
'class' => array(
'revision-current',
),
'colspan' => 3,
);
}
else {
$row[] = t('!date by !username', array(
'!date' => l(format_date($revision->timestamp, 'short'), 'node/' . $node->nid . '/revisions/' . $revision->vid . '/view'),
'!username' => theme('username', array(
'account' => $revision,
)),
)) . ($revision->log != '' ? "<p class='revision-log'>" . filter_xss($revision->log) . "</p>" : '');
$operations[] = l(t("accessibility"), 'node/' . $node->nid . '/accessibility/' . $revision->vid . '/revision');
if ($revert_permission) {
$operations[] = l(t("revert"), 'node/' . $node->nid . '/revisions/' . $revision->vid . '/revert');
}
if ($delete_permission) {
$operations[] = l(t("delete"), 'node/' . $node->nid . '/revisions/' . $revision->vid . '/delete');
}
}
$rows[] = array_merge($row, $operations);
}
$build['node_revisions_table'] = array(
'#theme' => 'table',
'#rows' => $rows,
'#header' => $header,
);
return $build;
}