Location: PHPKode > projects > VaMoLà - Validator > vamola-validator/include/classes/AccessibilityValidator.class.php
<?php
/************************************************************************/
/* AChecker                                                             */
/************************************************************************/
/* Copyright (c) 2008 by Greg Gay, Cindy Li                             */
/* Adaptive Technology Resource Centre / University of Toronto			    */
/*                                                                      */
/* This program is free software. You can redistribute it and/or        */
/* modify it under the terms of the GNU General Public License          */
/* as published by the Free Software Foundation.                        */
/************************************************************************/

/**
* AccessibilityValidator
* Class for accessibility validate
* This class checks the accessibility of the given html based on requested guidelines. 
* @access	public
* @author	Cindy Qi Li
* @package checker
*/
if (!defined("AC_INCLUDE_PATH")) die("Error: AC_INCLUDE_PATH is not defined.");

include (AC_INCLUDE_PATH . "lib/simple_html_dom.php");
//include_once (AC_INCLUDE_PATH . "classes/Checks.class.php");
include_once (AC_INCLUDE_PATH . "classes/BasicChecks.class.php");
include_once (AC_INCLUDE_PATH . "classes/BasicFunctions.class.php");
include_once (AC_INCLUDE_PATH . "classes/BasicFunctionsVamola.class.php");
include_once (AC_INCLUDE_PATH . "classes/CheckFuncUtility.class.php");
include_once (AC_INCLUDE_PATH . "classes/DAO/ChecksDAO.class.php");

define("SUCCESS_RESULT", "success");
define("FAIL_RESULT", "fail");
define("DISPLAY_PREVIEW_HTML_LENGTH", 100);

class AccessibilityValidator {

	// all private
	var $num_success;
	var $num_of_errors = 0;              // number of errors
	
	var $validate_content;               // html content to check
	var $guidelines;                     // array, guidelines to check on
	var $uri;                            // the URI that $validate_content is from, used in check image size in BasicFunctions
	
	// structure: line_number, check_id, result (success, fail)
	var $result = array();               // all check results, including success ones and failed ones
	
	var $check_for_all_elements_array = array(); // array of the to-be-checked check_ids 
	var $check_for_tag_array = array();          // array of the to-be-checked check_ids 
	var $prerequisite_check_array = array();     // array of prerequisite check_ids of the to-be-checked check_ids 
//	var $next_check_array = array();             // array of the next check_ids of the to-be-checked check_ids
	var $check_func_array = array();         // array of all the check functions 
		
	var $content_dom;                    // dom of $validate_content

	var $line_offset;                    // 1. ignore the problems on the lines before the line of $line_offset
	                                     // 2. report line_number = real_line_number - $line_offset 
	/**
	 * public
	 * $content: string, html content to check
	 * $guidelines: array, guidelines to check on
	 */
	function AccessibilityValidator($content, $guidelines, $uri = '')
	{
		$this->validate_content = $content;
		$this->guidelines = $guidelines;
		$this->line_offset = 0;
		$this->uri = $uri;
	}
	
	/* public
	 * Validation
	 */
	public function validate()
	{
		// dom of the content to be validated
		$this->content_dom = $this->get_simple_html_dom($this->validate_content);
		

		
		// prepare gobal vars used in BasicFunctions.class.php to fasten the validation
		$this->prepare_global_vars();
		
		// set arrays of check_id, prerequisite check_id, next check_id
		$this->prepare_check_arrays($this->guidelines);
		
		
		$this->validate_element($this->content_dom->find('html'));
		
		$this->finalize();

		// end of validation process
	}
	
	
	/** private
	 * set global vars used in Checks.class.php and BasicFunctions.class.php
	 * to fasten the validation process.
	 * return nothing.
	 */
	private function prepare_global_vars()
	{
		global $header_array, $base_href;

		// find all header tags which are used in BasicFunctions.class.php
		$header_array = $this->content_dom->find("h1, h2, h3, h4, h5, h6, h7");

		// find base href, used to check image size
		$all_base_elements = $this->content_dom->find("base");

		if (is_array($all_base_elements))
		{
			foreach ($all_base_elements as $base)
			{
				if (isset($base->attr['href']))
				{
					$base_href = $base->attr['href'];
					break;
				}
			}
		}

		// set all check functions
		$checksDAO = new ChecksDAO();
		$rows = $checksDAO->getAllOpenChecks();
		
		if (is_array($rows))
		{
			foreach ($rows as $row)
				$this->check_func_array[$row['check_id']] = CheckFuncUtility::convertCode($row['func']);
		}
	}
	
	/** private
	 * return a simple_html_dom on the given content.
	 * Because accessibility check is based on the root html element <html>,
	 * check if dom has html tag <html>, if no, add it and the end tag to the content
	 * and return the dom on modified content.
	 */
	private function get_simple_html_dom($content)
	{
		$dom = str_get_dom($content);
		
		if (count($dom->find('html')) == 0)
			$dom = str_get_dom("<html>".$content."</html>");
			
		return $dom;
	}
	
	/**
	 * private
	 * generate arrays of check ids, prerequisite check ids, next check ids
	 * array structure:
	 check_array
	 (
	 [html_tag] => Array
	 (
	 [0] => check_id 1
	 [1] => check_id 2
	 ...
	 )
	 ...
	 )

	 prerequisite_check_array
	 (
	 [check_id] => Array
	 (
	 [0] => prerequisite_check_id 1
	 [1] => prerequisite_check_id 2
	 ...
	 )
	 ...
	 )

//	 next_check_array
//	 (
//	 [check_id] => Array
//	 (
//	 [0] => next_check_id 1
//	 [1] => next_check_id 2
//	 ...
//	 )
	 ...
	 )
	 */
	private function prepare_check_arrays($guidelines)
	{
		
		if (!($guideline_query = $this->convert_array_to_string($guidelines, ',')))
			return false;
		// validation process
		else  
		{
			// SIMO: gestione tab per Wcag e Stanca ///////////////////////////////////////////////
			$idStanca = 10; // Identificativo Legge Stanca
			$idWcagA = 7;
			$idWcagAA = 8;
			$idWcagAAA = 8;
			
			$isWcag = FALSE;
			$isStanca = FALSE;
				
			$arrayGid = $guidelines;
			if (is_array($arrayGid) && in_array($idStanca, $arrayGid))
				$isStanca = TRUE;
			
			if (is_array($arrayGid) && (in_array($idWcagA, $arrayGid) || in_array($idWcagAA, $arrayGid) || in_array($idWcagAAA, $arrayGid)))
				$isWcag = TRUE;	
				
			if ($isStanca && $isWcag)
			{
				$_SESSION["show_nav"]="true";
				$_SESSION["show"]="all";
				$_SESSION["tab_ris"]="1";
			}
			else if ($isStanca && !$isWcag)
			{
				$_SESSION["show"]="stanca";$_SESSION["tab_ris"]="1";
			}
			else if (!$isStanca && $isWcag)
			{
				$_SESSION["show"]="wcag";$_SESSION["tab_ris"]="5";
			}	
			// SIMO: fine gestione tab per Wcag e Stanca //////////////////////////////////////////

			
			
			$checksDAO = new ChecksDAO();
			
			// generate array of "all element"
			$rows = $checksDAO->getOpenChecksForAllByGuidelineIDs($guideline_query);
			
			$count = 0;
			if (is_array($rows))
			{
				foreach ($rows as $id => $row)
					$this->check_for_all_elements_array[$count++] = $row["check_id"];
			}
			
			// generate array of check_id
			$rows = $checksDAO->getOpenChecksNotForAllByGuidelineIDs($guideline_query);

			if (is_array($rows))
			{
				foreach ($rows as $id => $row)
				{
					if ($row["html_tag"] <> $prev_html_tag && $prev_html_tag <> "") $count = 0;
					
					$this->check_for_tag_array[$row["html_tag"]][$count++] = $row["check_id"];
					
					$prev_html_tag = $row["html_tag"];
				}
			}
			
			// generate array of prerequisite check_ids
			
			$rows = $checksDAO->getOpenPreChecksByGuidelineIDs($guideline_query);

			if (is_array($rows))
			{
				foreach ($rows as $id => $row)
				{
					if ($row["check_id"] <> $prev_check_id)  $prerequisite_check_array[$row["check_id"]] = array();
					
					array_push($prerequisite_check_array[$row["check_id"]], $row["prerequisite_check_id"]);
					
					$prev_check_id = $row["check_id"];
				}
			}
			$this->prerequisite_check_array = $prerequisite_check_array;

			// generate array of next check_ids
//			$rows = $checksDAO->getOpenNextChecksByGuidelineIDs($guideline_query);
//
//			if (is_array($rows))
//			{
//				foreach ($rows as $id => $row)
//				{
//					if ($row["check_id"] <> $prev_check_id)  $next_check_array[$row["check_id"]] = array();
//					
//					array_push($next_check_array[$row["check_id"]], $row["next_check_id"]);
//					
//					$prev_check_id = $row["check_id"];
//				}
//			}
//			$this->next_check_array = $next_check_array;
//			debug($this->next_check_array);
			return true;
		}
	}

	/**
	 * private
	 * Recursive function to validate html elements
	 */
	private function validate_element($element_array)
	{
		foreach($element_array as $e)
		{
			// generate array of checks for the html tag of this element
			if (is_array($this->check_for_tag_array[$e->tag]))
				$check_array[$e->tag] = array_merge($this->check_for_tag_array[$e->tag], $this->check_for_all_elements_array);
			else
				$check_array[$e->tag] = $this->check_for_all_elements_array;
				
			foreach ($check_array[$e->tag] as $check_id)
			{
				// check prerequisite ids first, if fails, report failure and don't need to proceed with $check_id
				$prerequisite_failed = false;
//debug($check_id);
//debug($this->prerequisite_check_array[$check_id]);
//debug($this->next_check_array[$check_id]);
				if (is_array($this->prerequisite_check_array[$check_id]))
				{
					foreach ($this->prerequisite_check_array[$check_id] as $prerequisite_check_id)
					{
						$check_result = $this->check($e, $prerequisite_check_id);
						
						if ($check_result == FAIL_RESULT)
						{
							$prerequisite_failed = true;
							break;
						}
					}
				}

				// if prerequisite check passes, proceed with current check_id
				if (!$prerequisite_failed)
				{
					$check_result = $this->check($e, $check_id);
					
					// if check_id passes, proceed with next checks
//					if ($check_result == SUCCESS_RESULT)
//					{
//						if (is_array($this->next_check_array[$check_id]))
//							foreach ($this->next_check_array[$check_id] as $next_check_id)
//							{
//								$this->check($e, $next_check_id);
//							}
//					}
				}
			}
			
			$this->validate_element($e->children());
		}
	}

	/**
	 * private
	 * check given html dom node for given check_id, save result into $this->result
	 * parameters:
	 * $e: simple html dom node
	 * $check_id: check id
	 *
	 * return "success" or "fail"
	 */
	private function check($e, $check_id)
	{
		global $msg, $base_href;
		
		// don't check the lines before $line_offset
		if ($e->linenumber <= $this->line_offset) return;
		
		$result = $this->get_check_result($e->linenumber-$this->line_offset, $e->colnumber, $check_id);

		// has not been checked
		if (!$result)
		{
		
			//echo("<p>\$check_result = Checks::check_" . $check_id . "(\$e, \$this->content_dom); </p>");
			//echo("<p>e=".$e->tag." line=".$e->linenumber." col=".$e->colnumber."</p>");
			//eval("\$check_result = Checks::check_" . $check_id . "(\$e, \$this->content_dom);");
			
			// Simo: funzione nuova che prende dal db
			$check_result = eval($this->check_func_array[$check_id]);
		
			//MB $css_code: variabile assegnata alla fine di un check sui css 
			//contiene il codice da stampare in output tra gli errori
			//$css_code viene poi passato a save_result()
			$css_code=VamolaBasicChecks::getCssOutput();
			
			
			$checksDAO = new ChecksDAO();
			$row = $checksDAO->getCheckByID($check_id);
			if (is_null($check_result))
			{ // when $check_result is not true/false, must be something wrong with the check function.
			  // show warning message and skip this check
				$msg->addError(array('CHECK_FUNC', $row['html_tag'].': '._AC($row['name'])));
				
				// skip this check
				$check_result = true;
			}
			
			if ($check_result===true)  // success
			{
				$result = SUCCESS_RESULT;
				
				//MB numero di controlli a buon fine
				if(isset($this->num_success[$check_id]))
					$this->num_success[$check_id]++;
				else 
					$this->num_success[$check_id]=1;
			}
			//MB aggiungo questo elseif per avere un conteggio "veritiero" degli errori potenziali per gli elementi di testo
			//   ora alcuni check restituisco 2 oltre a true e false. 
			elseif($check_result===false)
			{
				$result = FAIL_RESULT;
			}
			else 
			{
				//echo("<p>check non conteggiato</p>");
			}
			
			// minus out the $line_offset from $linenumber
			/*
			//MB salvo anche i controlli andati a buon fine se chiamata REST (soluzione momentanea) 
			if(isset($_GET['output']) && $_GET['output']=='rest' && $result == SUCCESS_RESULT)
			{
				//$html_code = $e->outertext;
				$this->save_result($e->linenumber-$this->line_offset, $e->colnumber, $html_code, $css_code, $check_id, $result);
				
			}
			else
			*/ 
			if ($result == FAIL_RESULT)
			{
				// find out checked html code
				// http://www.atutor.ca/atutor/mantis/view.php?id=3768
				// http://www.atutor.ca/atutor/mantis/view.php?id=3797
				// Display not only the start tag, but a substring from start tag to end tag.
				// Displaying checked html is in HTMLRpt.class.php
				/*//MB tolgo per visualizzazione immagini
				if (strlen($e->outertext) > DISPLAY_PREVIEW_HTML_LENGTH) 
					$html_code = substr($e->outertext, 0, DISPLAY_PREVIEW_HTML_LENGTH) . " ...";
				else
				*/ 
					$html_code = $e->outertext;
//				else
//					$html_code = substr($e->outertext, 0, strpos($e->outertext, '>')+1);

				// find out preview images for validation on <img>
				/* 
				//MB
				if (strtolower(trim($row['html_tag'])) == 'img')
				{
					$image = BasicChecks::getFile($e->attr['src'], $base_href, $this->uri);
				    $handle = @fopen($image, 'r');
				
				    if (!$handle) $image = '';
				    else @fclose($handle);
				    
				    // find out image alt text for preview image
				    if (!isset($e->attr['alt'])) $image_alt = '_NOT_DEFINED';
				    else if ($e->attr['alt'] == '') $image_alt = '_EMPTY';
				    else $image_alt = $e->attr['alt'];
				}
				*/
				//MB $this->save_result($e->linenumber-$this->line_offset, $e->colnumber, $html_code, $check_id, $result, $image, $image_alt);
				
				
				
				$this->save_result($e->linenumber-$this->line_offset, $e->colnumber, $html_code, $css_code, $check_id, $result);
			}
			
		}
		
		return $result;
	}
	
	//MB
	function get_num_success()
	{
		/*
		if(isset($this->num_success[$check_id]))
			return $this->num_success[$check_id];
		else 
			return 0;
		*/
		//print_r($this->num_success);
		return $this->num_success;
	}
	
	
	/**
	 * private
	 * get check result from $result. Return false if the result is not found.
	 * Parameters:
	 * $line_number: line number in the content for this check
	 * $check_id: check id
	 */
	private function get_check_result($line_number, $col_number, $check_id)
	{
		foreach($this->result as $one_result)
		{
			if ($one_result["line_number"] == $line_number && $one_result["col_number"] == $col_number && $one_result["check_id"] == $check_id)
				return $one_result["result"];
		}
		
		return false;
	}

	/**
	 * private
	 * save each check result
	 * Parameters:
	 * $line_number: line number in the content for this check
	 * $check_id: check id
	 * $result: result to save
	 */
	private function save_result($line_number, $col_number, $html_code, $css_code, $check_id, $result /* //MB, $image, $image_alt*/)
	{
		//array_push($this->result, array("line_number"=>$line_number, "col_number"=>$col_number, "html_code"=>$html_code, "check_id"=>$check_id, "result"=>$result, "image"=>$image, "image_alt"=>$image_alt));
		array_push($this->result, array("line_number"=>$line_number, "col_number"=>$col_number, "html_code"=>$html_code, "css_code"=>$css_code, "check_id"=>$check_id, "result"=>$result));
		return true;
	}
	
	/**
	 * private
	 * convert the given array to a string of the array elements separated by the given delimiter.
	 * For example:
	 * array ([0] => 7, [1] => 8)
	 * delimiter: ,
	 * is converted to string "7, 8"
	 */
	private function convert_array_to_string($in_array, $delimiter)
	{
		$count = 0;
		if (is_array($in_array))
		{
			foreach ($in_array as $element)
			{
				if ($count == 0) $str = $element;
				else $str .= $delimiter . $element;
				
				$count++;
			}
			return $str;
		}
		else
			return false;
	}
	
	/**
	 * private 
	 * generate class value: array of error results, number of errors
	 */
	private function finalize()
	{
		$this->num_of_errors = count($this->result);
	}
	
	/**
	 * public 
	 * set line offset
	 */
	public function setLineOffset($lineOffset)
	{
		$this->line_offset = $lineOffset;
	}
	
	/**
	 * public 
	 * return line offset
	 */
	public function getLineOffset()
	{
		return $this->line_offset;
	}
	
	/**
	 * public 
	 * return array of all checks that have been done, including successful and failed ones
	 */
	public function getValidationErrorRpt()
	{
		return $this->result;
	}
	

	/**
	 * public 
	 * return number of errors
	 */
	public function getNumOfValidateError()
	{
		return $this->num_of_errors;
	}

	/**
	 * public 
	 * return array of all checks that have been done by check id, including successful and failed ones
	 */
	public function getResultsByCheckID($check_id)
	{
		$rtn = array();
		foreach ($this->result as $oneResult)
			if ($oneResult["check_id"] == $check_id)
				array_push($rtn, array("line_number"=>$oneResult["line_number"], "col_number"=>$oneResult["col_number"], "check_id"=>$oneResult["check_id"], "result"=>$oneResult["result"]));
	
		return $rtn;
	}

	/**
	 * public 
	 * return array of all checks that have been done by line number, including successful and failed ones
	 */
	public function getResultsByLine($line_number)
	{
		$rtn = array();
		foreach ($this->result as $oneResult)
			if ($oneResult["line_number"] == $line_number)
				array_push($rtn, array("line_number"=>$oneResult["line_number"], "col_number"=>$oneResult["col_number"], "check_id"=>$oneResult["check_id"], "result"=>$oneResult["result"]));
	
		return $rtn;
	}

	
}
?>  
Return current item: VaMoLà - Validator