00001 <?php
00024 define('QUAIL_TEST_SEVERE', 1);
00025 define('QUAIL_TEST_MODERATE', 2);
00026 define('QUAIL_TEST_SUGGESTION', 3);
00027 define('QUAIL_LIB_VERSION', 038);
00028
00033 require_once('common/test.php');
00034 require_once('common/css.php');
00035 require_once('common/elements.php');
00036 require_once('common/domExtensions.php');
00037 require_once('common/accessibility_tests.php');
00038
00043 class quail {
00047 var $dom;
00048
00052 var $type;
00053
00057 var $value;
00058
00062 var $uri;
00063
00067 var $domain;
00068
00072 var $guideline_name = 'wcag';
00073
00077 var $reporter_name = 'static';
00078
00082 var $reporter;
00083
00087 var $guideline;
00088
00092 var $base_url;
00093
00097 var $path = array();
00098
00102 var $css_files;
00103
00107 var $css;
00108
00112 var $options = array(
00113 'cms_mode' => false,
00114 'start_element' => 0,
00115 'end_element' => 0,
00116 'cms_template' => array());
00117
00122 var $is_valid = true;
00123
00132 function __construct($value, $guideline = 'wcag', $type = 'string', $reporter = 'static', $domain = 'en') {
00133 $this->dom = new DOMDocument();
00134 $this->type = $type;
00135 if($type == 'uri' || $type == 'file') {
00136 $this->uri = $value;
00137 }
00138 $this->domain = $domain;
00139 $this->guideline_name = $guideline;
00140 $this->reporter_name = $reporter;
00141 $this->value = $value;
00142
00143 }
00144
00149 function prepareDOM() {
00150 $this->prepareValue();
00151 $this->is_valid = @$this->dom->loadHTML($this->value);
00152 $this->prepareBaseUrl($this->value, $this->type);
00153 }
00154
00160 function prepareValue() {
00161
00162
00163 if($this->type == 'file' || $this->type == 'uri') {
00164 $this->value = @file_get_contents($this->value);
00165 }
00166 }
00167
00168
00175 function setOption($variable, $value = null) {
00176 if(!is_array($variable)) {
00177 $variable = array($variable => $value);
00178 }
00179 foreach($variable as $k => $value) {
00180 if(isset($this->options[$k])) {
00181 $this->options[$k] = $value;
00182 }
00183 }
00184 }
00185
00193 function getAbsolutePath($absolute, $relative) {
00194 if(substr($relative, 0, 2) == '//') {
00195 if($this->uri) {
00196 $current = parse_url($this->uri);
00197 }
00198 else {
00199 $current = array('scheme' => 'http');
00200 }
00201 return $current['scheme'] .':'. $relative;
00202 }
00203 $relative_url = parse_url($relative);
00204 if (isset($relative_url['scheme'])) {
00205 return $relative;
00206 }
00207 $absolute_url = parse_url($absolute);
00208 if(isset($absolute_url['path'])) {
00209 $path = dirname($absolute_url['path']);
00210 }
00211 if ($relative{0} == '/') {
00212 $c_parts = array_filter(explode('/', $relative));
00213 }
00214 else {
00215 $a_parts = array_filter(explode('/', $path));
00216 $r_parts = array_filter(explode('/', $relative));
00217 $c_parts = array_merge($a_parts, $r_parts);
00218 foreach ($c_parts as $i => $part) {
00219 if ($part == '.') {
00220 $c_parts[$i] = NULL;
00221 }
00222 if ($part == '..') {
00223 $c_parts[$i - 1] = NULL;
00224 $c_parts[$i] = NULL;
00225 }
00226 }
00227 $c_parts = array_filter($c_parts);
00228 }
00229 $path = implode('/', $c_parts);
00230 $url = "";
00231 if (isset($absolute_url['scheme'])) {
00232 $url = $absolute_url['scheme'] .'://';
00233 }
00234 if (isset($absolute_url['user'])) {
00235 $url .= $absolute_url['user'];
00236 if ($absolute_url['pass']) {
00237 $url .= ':'. $absolute_url['user'];
00238 }
00239 $url .= '@';
00240 }
00241 if (isset($absolute_url['host'])) {
00242 $url .= $absolute_url['host'];
00243 if(isset($absolute_url['port'])) {
00244 $url .= ':'. $absolute_url['port'];
00245 }
00246 $url .= '/';
00247 }
00248 $url .= $path;
00249 return $url;
00250 }
00251
00257 function setUri($uri) {
00258 if(parse_url($uri)) {
00259 $this->uri = $uri;
00260 }
00261 }
00262
00270 function prepareBaseUrl($value, $type) {
00271 if($type == 'file') {
00272 $path = explode('/', $this->uri);
00273 array_pop($path);
00274 $this->path = $path;
00275 }
00276 elseif($type == 'uri' || $this->uri) {
00277
00278 $parts = explode('://', $this->uri);
00279 $this->path[] = $parts[0] .':/';
00280 if(is_array($parts[1])) {
00281 foreach(explode('/', $this->getBaseFromFile($parts[1])) as $part) {
00282 $this->path[] = $part;
00283 }
00284 }
00285 else {
00286 $this->path[] = $parts[1] .'/';
00287 }
00288 }
00289 }
00290
00296 function getBaseFromFile($file) {
00297 $find = '/';
00298 $after_find = substr(strrchr($file, $find), 1);
00299 $strlen_str = strlen($after_find);
00300 $result = substr($file, 0, -$strlen_str);
00301 return $result;
00302 }
00303
00304
00305
00310 function addCSS($css) {
00311 if(is_array($css)) {
00312 $this->css_files = $css;
00313 }
00314 else {
00315 $this->css_files[] = $css;
00316 }
00317 }
00318
00324 function getError($error) {
00325 return $this->reporter->getError($error);
00326 }
00327
00332 function loadReporter($options = array()) {
00333 $classname = 'report'.ucfirst($this->reporter_name);
00334 if(!class_exists($classname)) {
00335 require_once('reporters/reporter.'. $this->reporter_name .'.php');
00336 }
00337 $this->reporter = new $classname($this->dom, $this->css, $this->guideline, $this->path);
00338 if(count($options))
00339 $this->reporter->setOptions($options);
00340 }
00341
00342
00347 function isValid() {
00348 return $this->is_valid;
00349 }
00350
00351
00356 function runCheck($options = null) {
00357 $this->prepareDOM();
00358 if(!$this->isValid())
00359 return false;
00360 $this->getCSSObject();
00361 $classname = ucfirst(strtolower($this->guideline_name)).'Guideline';
00362 if(!class_exists($classname)) {
00363 require_once('guidelines/'. $this->guideline_name .'.php');
00364 }
00365 $this->guideline = new $classname($this->dom, $this->css, $this->path, $options, $this->domain, $this->options['cms_mode']);
00366 }
00367
00371 function getCSSObject() {
00372 $this->css = new quailCSS($this->dom, $this->uri, $this->type, $this->path, false, $this->css_files);
00373 }
00374
00380 function getReport($options = array()) {
00381 if(!$this->reporter)
00382 $this->loadReporter($options);
00383 if($options) {
00384 $this->reporter->setOptions($options);
00385 }
00386 return $this->reporter->getReport();
00387 }
00388
00394 function getTest($test) {
00395 if(!class_exists($test)) {
00396 return false;
00397 }
00398 $test_class = new $test($this->dom, $this->css, $this->path);
00399 return $test_class->report;
00400 }
00401
00407 function getTestSeverity($test) {
00408 $test_class = new $test($this->dom, $this->css, $this->path);
00409 return $test_class->getSeverity();
00410 }
00411
00416 function cleanup() {
00417 unset($this->dom);
00418 unset($this->css);
00419 unset($this->guideline);
00420 unset($this->reporter);
00421 }
00422 }
00423
00427 class quailReporter {
00428
00432 var $dom;
00433
00437 var $css;
00438
00442 var $translation;
00443
00447 var $report;
00448
00452 var $path;
00453
00457 var $options;
00458
00463 var $absolute_attributes = array('src', 'href');
00464
00473 function __construct(&$dom, &$css, &$guideline, $path = '') {
00474 $this->dom = &$dom;
00475 $this->css = &$css;
00476 $this->path = $path;
00477 $this->options = new stdClass;
00478 $this->guideline = &$guideline;
00479 }
00480
00481
00482
00487 function setOptions($options) {
00488 foreach($options as $key => $value) {
00489 $this->options->$key = $value;
00490 }
00491 }
00492
00497 function setAbsolutePath(&$element) {
00498 foreach($this->absolute_attributes as $attribute) {
00499 if($element->hasAttribute($attribute))
00500 $attr = $attribute;
00501 }
00502
00503 if($attr) {
00504 $item = $element->getAttribute($attr);
00505
00506 if(strpos($item, '://') === false) {
00507
00508 $item = implode('/', $this->path) . ltrim($item, '/');
00509 $element->setAttribute($attr, $item);
00510
00511 }
00512 }
00513 if($element->tagName == 'style') {
00514 if(strpos($element->nodeValue, '@import') !== false) {
00515
00516
00517 }
00518 }
00519 }
00520
00521 }
00522
00527 class quailReportItem {
00528
00532 var $element;
00533
00537 var $message;
00538
00542 var $manual;
00543
00547 var $pass;
00548
00554 function getLine() {
00555 if(is_object($this->element) && method_exists($this->element, 'getLineNo')) {
00556 return $this->element->getLineNo();
00557 }
00558 return 0;
00559 }
00560
00566 function getHTML($extra_attributes = array()) {
00567 if(!$this->element)
00568 return '';
00569 $result_dom = new DOMDocument();
00570 try {
00571
00572 $result_element = $result_dom->createElement(utf8_encode($this->element->tagName));
00573 }
00574 catch (Exception $e) {
00575 return false;
00576 }
00577 foreach($this->element->attributes as $attribute) {
00578 if($attribute->name != 'quail_style_index') {
00579 $result_element->setAttribute($attribute->name, $attribute->value);
00580 }
00581 }
00582 foreach($extra_attributes as $name => $value) {
00583 $result_element->setAttribute($name, $value);
00584 }
00585 $result_element->nodeValue = htmlentities($this->element->nodeValue);
00586
00587
00588 @$result_dom->appendChild($result_element);
00589 return @$result_dom->saveHTML();
00590
00591 }
00592
00593 }
00594
00599 class quailGuideline {
00600
00604 var $dom;
00605
00609 var $css;
00610
00614 var $path;
00615
00619 var $report;
00620
00624 var $translations;
00625
00629 var $cms_mode = false;
00630
00635 var $severity = array();
00636
00644 function __construct(&$dom, &$css, &$path, $arg = null, $domain = 'en', $cms_mode = false) {
00645 $this->dom = &$dom;
00646 $this->css = &$css;
00647 $this->path = &$path;
00648 $this->cms_mode = $cms_mode;
00649 $this->loadTranslations($domain);
00650 $this->run($arg, $domain);
00651 }
00652
00657 function getTests() {
00658 return $this->tests;
00659 }
00660
00666 function loadTranslations($domain) {
00667 $csv = fopen(dirname(__FILE__) .'/guidelines/translations/'. $domain .'.txt', 'r');
00668 if ($csv) {
00669 while ($translation = fgetcsv($csv)) {
00670 if(count($translation) == 4) {
00671 $this->translations[$translation[0]] = array(
00672 'title' => $translation[1],
00673 'description' => $translation[2],
00674 );
00675 }
00676 }
00677 }
00678 }
00683 function getTranslation($testname) {
00684 $translation = (isset($this->translations[$testname]))
00685 ? $this->translations[$testname]
00686 : $testname;
00687 return $translation;
00688 }
00692 function run($arg = null, $language = 'en') {
00693 foreach($this->tests as $testname => $options) {
00694 if(is_numeric($testname) && !is_array($options)) {
00695 $testname = $options;
00696 }
00697 if(class_exists($testname) && $this->dom) {
00698 $$testname = new $testname($this->dom, $this->css, $this->path, $language, $arg);
00699 if(!$this->cms_mode || ($$testname->cms && $this->cms_mode)) {
00700 $this->report[$testname] = $$testname->getReport();
00701 }
00702 $this->severity[$testname] = $$testname->default_severity;
00703 unset($$testname);
00704 }
00705 else {
00706 $this->report[$testname] = false;
00707 }
00708 }
00709 }
00710
00715 function getReport(){
00716 return $this->report;
00717 }
00718
00724 function getSeverity($testname) {
00725 if(isset($this->tests[$testname]['severity'])) {
00726 return $this->tests[$testname]['severity'];
00727 }
00728 if(isset($this->severity[$testname])) {
00729 return $this->severity[$testname];
00730 }
00731 return QUAIL_TEST_MODERATE;
00732 }
00733 }