You are here

w3c_validator.module in W3C Validator 7

Same filename and directory in other branches
  1. 8 w3c_validator.module
  2. 6 w3c_validator.module

File

w3c_validator.module
View source
<?php

/**
 * @file
 * W3C Validator proxy.
 *
 * TODO: Refactor some output using theme functions.
 */
include_once 'validators/W3CvalidatorAPI.php';

/**
 * Implements hook_menu().
 */
function w3c_validator_menu() {
  $items = array();
  $items['admin/config/development/w3c_validator'] = array(
    'title' => 'W3C Validator',
    'description' => 'Settings for the W3C validator API.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'w3c_validator_admin_settings',
    ),
    'access arguments' => array(
      'administer_w3c',
    ),
    'file' => 'w3c_validator.admin.inc',
  );
  $items['admin/reports/w3c_validator'] = array(
    'title' => 'Site W3C validation',
    'description' => 'Site W3C validation checker page.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'w3c_validator_site_validation_report_page',
    ),
    'access arguments' => array(
      'use_w3c',
    ),
    'file' => 'w3c_validator.site_validation_report.page.inc',
  );
  $items['validator'] = array(
    'title' => 'Validate a URI',
    'description' => 'Validate URI using the W3C Validator API',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'w3c_validator_uri_validator_page',
    ),
    'access arguments' => array(
      'use_w3c',
    ),
    'file' => 'w3c_validator.uri_validator.page.inc',
  );
  return $items;
}

/**
 * Implements hook_permission().
 */
function w3c_validator_permission() {
  return array(
    'administer_w3c' => array(
      'title' => t('Administer w3c_validator'),
      'description' => t('Administer settings for w3c_validator module.'),
    ),
    'use_w3c' => array(
      'title' => t('Use w3c_validator'),
      'description' => t('Access the W3C result page and use the module to validate URLs.'),
    ),
  );
}

/**
 * Implements hook_node_update().
 */
function w3c_validator_node_update($node) {

  // If the node is published
  if ($node->status == 1) {

    // Build the uri
    $uri = 'node/' . $node->nid;

    // Check if a validation operation is possible
    //	(user permission + module configration).
    $process_mode = variable_get('w3c_validator_options', array(
      'w3c_on_run' => 1,
    ));
    if (user_access('use_w3c') && isset($process_mode['w3c_on_run']) && $process_mode['w3c_on_run']) {

      // Check the result.
      _w3c_validator_full_validation_operation($uri, 1);
    }
  }
}

/**
 * Implements hook_node_delete().
 */
function w3c_validator_node_delete($node) {

  // Build the uri
  $uri = 'node/' . $node->nid;

  // Delete
  db_delete('w3c_validator')
    ->condition('uri', $uri)
    ->execute();
}

/**
 * Implements hook_node_load().
 */
function w3c_validator_node_view($node, $view_mode, $langcode) {

  // Build the uri
  $uri = 'node/' . $node->nid;

  // Check if a validation operation is possible
  //	(not on frontpage + validation is outdated + user permission + module configration + node is published).
  $process_mode = variable_get('w3c_validator_options', array(
    'w3c_on_run' => 1,
  ));
  if (!drupal_is_front_page() && _w3c_validator_check_uri_expired($uri) && user_access('use_w3c') && $process_mode['w3c_on_run'] && $node->status == 1) {

    // Check the result.
    _w3c_validator_full_validation_operation($uri, 1);
  }
}

/**
 * Implements hook_requirements().
 */
function w3c_validator_requirements($phase) {
  $requirements = array();
  if ($phase == 'runtime' && variable_get('w3c_validator_method', 'w3c_markup_validator') == 'w3c_markup_validator') {
    $endpoint = variable_get('w3c_validator_api_endpoint_uri', 'http://validator.w3.org/check');
    $severity = REQUIREMENT_OK;
    $description = '';
    if ($endpoint == 'http://validator.w3.org/check') {
      $severity = REQUIREMENT_WARNING;
      $description = t('W3C Validator module is configured to use the official W3C validator API endpoint. You should <a href="!url">change the configuration</a> to point to your own instance of the validator, using the official endpoint intensively could be considered abuse of service. See the module documentation on how to install your own instance of the validator.', array(
        '!url' => url('admin/settings/w3c_validator'),
      ));
    }
    else {
      if (empty($endpoint)) {
        $severity = REQUIREMENT_ERROR;
        $description = t('W3C Validator module is not configured to use a proper API endpoint. You should <a href="!url">change the onfiguration</a> to point to your own instance of the validator, using the official endpoint intensively could be considered abuse of service. See the module documentation on how to install your own instance of the validator.', array(
          '!url' => url('admin/settings/w3c_validator'),
        ));
      }
    }
    $requirements['w3c_validator'] = array(
      'title' => t('W3C Validator endpoint'),
      'value' => $endpoint,
      'description' => $description,
      'severity' => $severity,
    );
  }
  return $requirements;
}

/**
 * Implements hook_theme().
 */
function w3c_validator_theme($existing, $type, $theme, $path) {
  return array(
    'w3c_validator_site_validation_report_row' => array(
      'arguments' => array(
        'page' => NULL,
        'validation' => NULL,
      ),
      'file' => 'w3c_validator.site_validation_report.page.inc',
    ),
    'w3c_validator_site_validation_report_row_page_status' => array(
      'arguments' => array(
        'page' => NULL,
        'validation' => NULL,
      ),
      'file' => 'w3c_validator.site_validation_report.page.inc',
    ),
  );
}

/**
 * Mark in the database a URI as expired
 *
 * @param string $uri
 */
function _w3c_validator_mark_uri_as_expired($uri) {

  // Merge the result with eventual previous result for the same URI.
  db_merge("w3c_validator")
    ->key(array(
    'url' => $uri,
  ))
    ->fields(array(
    'url' => $uri,
    'need_validation' => 1,
  ))
    ->execute();
}

/**
 * Check in the database a URI as expired
 *
 * @param string $uri
 */
function _w3c_validator_check_uri_expired($uri) {

  // Merge the result with eventual previous result for the same URI.
  $db_result = db_select('w3c_validator', 'w')
    ->fields('w', array(
    'need_validation',
  ))
    ->condition('uri', $uri)
    ->execute();
  if ($db_result = $db_result
    ->fetchAssoc()) {
    return $db_result['need_validation'];
  }
  return true;
}

/**
 * Check and save the validation result of a URI.
 * @param string $uri
 */
function _w3c_validator_full_validation_operation($uri, $verbose = 0) {
  $result = _w3c_validator_validate_uri($uri, $verbose);
  _w3c_validator_save_result($result);
}

/**
 * Validate a URI using the configured validator endpoint.
 *
 * @param string $uri
 * @return void
 */
function _w3c_validator_validate_uri($uri, $verbose = 0) {

  // Retrieve the validation method
  $method = variable_get('w3c_validator_method', 'w3c_markup_validator');

  // Retrieve full URI :
  $uri = url($uri, array(
    'absolute' => TRUE,
  ));

  // If php_tidy is not nabled on server,
  // switch to w3C_markup_validator anyway.
  $tidy_available = function_exists('tidy_get_output');
  if (!$tidy_available) {
    $method = 'w3c_markup_validator';
  }

  // If the method to validate is W3C HTML Validator :
  if ($method == 'w3c_markup_validator') {
    $baseUrl = variable_get('w3c_validator_api_endpoint_uri', 'http://validator.w3.org/check');
    $validator = new W3CvalidatorAPI($baseUrl);
    $result = $validator
      ->validate($uri);
  }
  if ($verbose) {
    if (!isset($result->uri)) {

      // If here : the process has failed due to misconfigurtion.
      drupal_set_message(t('An error has occured while validating this url (@url).', array(
        '@url' => $uri,
      )) . '<br/>' . t('Please check the following before retry : ') . '<br/><ul><li>' . t('is your URL correct ?') . '</li><li>' . t('is your server able to send external GET request ?') . '</li><li>' . t('is your server being a proxy - thus is this proxy setted in your server configuration ?') . '</li></ul>', 'error');
    }
    else {
      if ($result->validity) {
        drupal_set_message(t('This page (@url) as been verified and is valid !', array(
          '@url' => $uri,
        )), 'status');
      }
      else {
        drupal_set_message(t('This page (@url) as been verified but is invalid : @error_count errors, @warning_count warnings found !', array(
          '@url' => $uri,
          '@error_count' => $result->error_count,
          '@warning_count' => $result->warning_count,
        )), 'warning');
        drupal_set_message(t('You can view the validation errors in the <a href="@link-page">validator result page</a> or check <a href="@link-all">all site validations<a>.', array(
          '@link-page' => url('validator', array(
            'query' => array(
              'uri' => $uri,
            ),
          )),
          '@link-all' => url('admin/reports/w3c_validator'),
        )), 'warning');
      }
    }
  }
  return $result;
}

/**
 * Save a validation result in the database.
 *
 * @param array 		$result
 * @param boolean 	$need_validation (default is false)
 * @return void
 */
function _w3c_validator_save_result($result, $need_validation = 0) {

  // If the result is defined :
  if ($result != null && isset($result->uri)) {

    // Check if the URL belongs to this website. If yes, store it under
    // it's Drupal System path.
    $path_analysis = _w3c_validator_get_path_from_url($result->uri);
    $uri = $path_analysis['path'];
    if ($uri) {

      // Merge the result with eventual previous result for the same URI.
      db_merge("w3c_validator")
        ->key(array(
        'uri' => rtrim($uri, "/"),
      ))
        ->fields(array(
        'uri' => rtrim($uri, "/"),
        'error_count' => $result->error_count,
        'errors' => serialize($result->errors),
        'warning_count' => $result->warning_count,
        'warnings' => serialize($result->warnings),
        'need_validation' => $need_validation,
        'doctype' => $result->doctype,
        'validity' => $result->validity ? 1 : 0,
        'charset' => $result->charset,
      ))
        ->execute();
    }
  }
}

/**
 * Retrieve a validation result from the database.
 *
 * @param string 		$uri
 * @return W3Cvalidator_Response
 */
function _w3c_validator_retrieve_result($uri) {
  $db_result = db_select('w3c_validator')
    ->fields('w3c_validator')
    ->condition('uri', rtrim($uri, "/"))
    ->execute();
  if ($db_result = $db_result
    ->fetchObject()) {

    // If the URI has already been validated :																												// Otherwise, just :
    return $db_result;
  }
  return null;
}

/**
 * Build the validation result display.
 *
 * @param array 		$result
 * @param boolean 	$need_validation (default is false)
 * @return array		the new page part containing the result display
 */
function _w3c_validator_display_result($result) {
  $result->uri = url($result->uri, array(
    'absolute' => TRUE,
  ));
  $endpoint = variable_get('w3c_validator_api_endpoint_uri', 'http://validator.w3.org/check');
  $check_url = $endpoint . '?uri=' . urlencode($result->uri);

  // ---------------------------------------------------
  // BUILD THE RESULT SUMMARY.
  $headers = null;
  $rows = array();
  $rows[] = array(
    t('URI'),
    l($result->uri, $result->uri, array(
      'attributes' => array(
        'target' => '_new',
      ),
    )),
  );
  $rows[] = array(
    t('Validity'),
    $result->validity == 'true' ? t('Valid') : t('Invalid'),
  );
  $rows[] = array(
    t('Validator results'),
    l($check_url, $check_url, array(
      'attributes' => array(
        'target' => '_new',
      ),
    )),
  );
  $rows[] = array(
    t('Doctype'),
    $result->doctype,
  );
  $rows[] = array(
    t('Summary'),
    t(' Found @error_count Errors, @warning_count Warnings', array(
      '@error_count' => $result->error_count,
      '@warning_count' => $result->warning_count,
    )),
  );
  if ($result->validity) {
    $output = '<h2 class="title valid">' . t('This document was successfully checked !') . '</h2>';
  }
  else {
    $output = '<h2 class="title invalid">' . t('Errors found while checking this document !') . '</h2>';
  }
  $output .= theme('table', array(
    'header' => $headers,
    'rows' => $rows,
    'attributes' => array(
      'class' => array(
        'summary-table',
      ),
    ),
  ));

  // ---------------------------------------------------
  // BUILD ERRORS RESULTS.
  $output .= '<h2>' . t('Errors') . '</h2>';
  $result->errors = is_array($result->errors) ? $result->errors : unserialize($result->errors);
  if (is_array($result->errors) && !empty($result->errors)) {
    foreach ($result->errors as $error) {
      $output .= '<div class="message-wrapper message-error">';
      $output .= '<div class="message">' . '<span class="where">' . t('Line @line, Column @col:', array(
        '@line' => $error->line,
        '@col' => $error->col,
      )) . '</span><span class="descr">' . t(' @descr', array(
        '@descr' => $error->message,
      )) . '</div>';
      $output .= '<div class="source">' . $error->source . '</div>';
      $output .= '</div>';
    }
  }
  else {
    $output .= t('No errors');
  }

  // ---------------------------------------------------
  // BUILD WARNINGS RESULTS.
  $output .= '<h2>' . t('Warnings') . '</h2>';
  $result->warnings = is_array($result->warnings) ? $result->warnings : unserialize($result->warnings);
  if (is_array($result->warnings) && !empty($result->warnings)) {
    foreach ($result->warnings as $warning) {
      $output .= '<div class="message-wrapper message-warning">';
      $output .= '<div class="message">' . '<span class="where">' . t('Line @line, Column @col:', array(
        '@line' => $warning->line,
        '@col' => $warning->col,
      )) . '</span><span class="descr">' . t(' @descr', array(
        '@descr' => $warning->message,
      )) . '</div>';
      $output .= '<div class="source">' . $warning->source . '</div>';
      $output .= '</div>';
    }
  }
  else {
    $output .= t('No warnings');
  }
  return $output;
}

/**
 * Returns internal path, query and fragment from external URL.
 * 
 * @param string $url
 * @param string $normal_path
 * @return array
 *   An associative array containing the keys:
 *   - 'path': the internal relatif path
 *   - 'query': An array of query parameters of $url, if existent.
 *   - 'fragment': The fragment of $url, if existent.
 * 	- language : the language
 */
function _w3c_validator_get_path_from_url($url, $normal_path = TRUE) {
  global $base_path;

  // Get hostname
  preg_match("/^(https?:\\/\\/)?([^\\/]+)/i", $url, $domain);

  // Format localhost + base_path (http://hostname/drupalbase)
  $base = (isset($domain[0]) ? $domain[0] : '') . $base_path;

  // If $url contains $base, remove the $base part
  if (strpos($url, $base) === 0) {
    $path = str_replace($base, '', $url);
  }
  else {
    $path = $url;
  }

  // Parse the path (without $base)
  $result = drupal_parse_url($path);

  // Get normal path
  if ($normal_path) {
    $result['path'] = drupal_get_normal_path($result['path']);
  }
  return $result;
}

Functions

Namesort descending Description
w3c_validator_menu Implements hook_menu().
w3c_validator_node_delete Implements hook_node_delete().
w3c_validator_node_update Implements hook_node_update().
w3c_validator_node_view Implements hook_node_load().
w3c_validator_permission Implements hook_permission().
w3c_validator_requirements Implements hook_requirements().
w3c_validator_theme Implements hook_theme().
_w3c_validator_check_uri_expired Check in the database a URI as expired
_w3c_validator_display_result Build the validation result display.
_w3c_validator_full_validation_operation Check and save the validation result of a URI.
_w3c_validator_get_path_from_url Returns internal path, query and fragment from external URL.
_w3c_validator_mark_uri_as_expired Mark in the database a URI as expired
_w3c_validator_retrieve_result Retrieve a validation result from the database.
_w3c_validator_save_result Save a validation result in the database.
_w3c_validator_validate_uri Validate a URI using the configured validator endpoint.