diff --git a/.bowerrc b/.bowerrc index 1edc0973b..687b2e00a 100644 --- a/.bowerrc +++ b/.bowerrc @@ -1,3 +1,3 @@ { - "directory": "assets/js/vendor" + "directory": "public/assets/js/vendor" } diff --git a/.buildpacks b/.buildpacks new file mode 100644 index 000000000..6a77caf53 --- /dev/null +++ b/.buildpacks @@ -0,0 +1,2 @@ +https://github.com/heroku/heroku-buildpack-nodejs +https://github.com/heroku/heroku-buildpack-php diff --git a/HEROKU.md b/HEROKU.md new file mode 100644 index 000000000..6352092c7 --- /dev/null +++ b/HEROKU.md @@ -0,0 +1,51 @@ +## Deploy on Heroku + +1. `git clone git@github.com:ucfcdl/UDOIT.git` to grab a copy of the git repo +2. `heroku create` will set up a Heroku project +3. `heroku addons:create heroku-postgresql:hobby-dev` add a database addon +4. `heroku buildpacks:set https://github.com/heroku/heroku-buildpack-multi` may be required +5. `git push heroku master:master` will build build the server using our master branch +6. set up the Heroku config variables below + +## Configure +Set Heroku config variables using `heroku config:set VAR=value1` + +* `CONSUMER_KEY` - LTI consumer key entered when adding UDOIT LTI to Canvas +* `SHARED_SECRET` - LTI secret entered when adding UDOIT LTI to Canvas +* `OAUTH2_ID` - from the developer api key created by your admin +* `OAUTH2_KEY` - from the developer api key created by your admin +* `OAUTH2_URI` - full url to your oauth2responce.php - EX: `https://your.herokuapp.com/oauth2response.php` +* `GOOGLE_API_KEY` - add a google api key for youtube video support +* `USE_HEROKU_CONFIG` - set to `true` to enable the Heroku configuration + +## Create Database Tables +You'll need to have postgresql installed on your own system to connect to the Heroku postgresql database. + +* `heroku pg:psql` will open a psql connection to the remote Heroku database +* copy and paste the postgresql table schemeas for the users and reports table into the prompt +* `\dt` will show you a list of the tables you just created +* `\q` quits the psql terminal + +```sql +/* postgresql */ +CREATE TABLE reports ( + id SERIAL PRIMARY KEY, + user_id integer, + course_id integer, + file_path text, + date_run bigint, + errors integer, + suggestions integer +); +``` + +### Users Table + +```sql +/* postgresql */ +CREATE TABLE users ( + id integer CONSTRAINT users_pk PRIMARY KEY, + api_key varchar(255), + date_created integer +); +``` diff --git a/Procfile b/Procfile new file mode 100644 index 000000000..1d2ee486c --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: vendor/bin/heroku-php-apache2 -F phpfpm_custom.conf public/ diff --git a/README.md b/README.md index d9942c137..299aa75ea 100755 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ UDOIT uses the [QUAIL PHP library](https://code.google.com/p/quail-lib/), which ## Installing +UDOIT uses php, apache or nginx, and mysql or postresql. For instructions on installing to Heroku, view [HEROKU.md](HEROKU.md). We also support instantly deploying UDOIT: [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) + ### System Requirements *PHP 5.4 is required* to run UDOIT without any modifications. We have not tested it on 5.5 or 5.6, but some users have been able to modify the code to work on 5.3. @@ -66,6 +68,7 @@ There are only two tables required to run UDOIT. They are: ### Reports Table ```sql +/* mysql */ CREATE TABLE `reports` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(10) unsigned NOT NULL, @@ -76,11 +79,24 @@ CREATE TABLE `reports` ( `suggestions` int(10) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +/* postgresql */ +CREATE TABLE reports ( + id SERIAL PRIMARY KEY, + user_id integer, + course_id integer, + file_path text, + date_run bigint, + errors integer, + suggestions integer +); ``` + ### Users Table ```sql +/* mysql */ CREATE TABLE `users` ( `id` int(10) unsigned NOT NULL, `api_key` varchar(255) NOT NULL, @@ -88,8 +104,16 @@ CREATE TABLE `users` ( PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +/* postgresql */ +CREATE TABLE users ( + id integer CONSTRAINT users_pk PRIMARY KEY, + api_key varchar(255), + date_created integer +); ``` + ## Configuration Make a copy of `config/localConfig.template.php`, rename it to `localConfig.php`. @@ -110,7 +134,7 @@ UDOIT uses Oauth2 to take actions on behalf of the user, you'll need to [sign up These value of these vars should be obvious: * `$db_host` -* `$db_user` +* `$db_url` * `$db_password` * `$db_name` * `$db_user_table` diff --git a/app.json b/app.json new file mode 100644 index 000000000..9959c63d2 --- /dev/null +++ b/app.json @@ -0,0 +1,60 @@ +{ + "name": "UDOIT", + "description": "The Universal Design Online content Inspection Tool, or UDOIT identifies and fixes accessibility issues in Canvas by Instructure.", + "keywords": [ + "education", + "canvas", + "CDL", + "EDU", + "UCF", + "Instructure", + "508" + ], + "website": "http://online.ucf.edu/teach-online/resources/udoit/", + "repository": "https://github.com/ucfcdl/UDOIT", + "success_url": "/", + "env": { + "CONSUMER_KEY": { + "description": "LTI consumer key entered when adding UDOIT LTI to Canvas", + "generator": "secret" + }, + "SHARED_SECRET": { + "description": "LTI secret entered when adding UDOIT LTI to Canvas", + "generator": "secret" + }, + "OAUTH2_ID": { + "description": "Oauth ID from the developer api key created by your admin", + "generator": "secret" + }, + "OAUTH2_KEY": { + "description": "Oauth Key from the developer api key created by your admin", + "generator": "secret" + }, + "OAUTH2_URI": { + "description": "Full url to your oauth2responce.php file", + "value": "https://your.herokuapp.com/oauth2response.php" + }, + "GOOGLE_API_KEY": { + "description": "add a google api key for youtube video support", + "required": false + }, + "USE_HEROKU_CONFIG": { + "description": "needed to use the Heroku configuration", + "value": "true" + } + }, + "addons": [ + "heroku-postgresql:hobby-dev" + ], + "buildpacks": [ + { + "url": "https://github.com/heroku/heroku-buildpack-nodejs" + }, + { + "url": "https://github.com/heroku/heroku-buildpack-php" + } + ], + "scripts": { + "postdeploy": "php db_pg_setup.php" + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index bf9ea1ce6..ee3e6f2ed 100755 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "repositories": [ ], "require": { - "php": ">=5.4.0", + "php": "^5.4.0 || ^5.5.0 || ^5.6.0", "nategood/httpful": "*", "zaininnari/html-minifier": "dev-master", "mpdf/mpdf": "dev-master", @@ -14,13 +14,11 @@ "ext-pdo": "*" }, "require-dev": { - "symfony/var-dumper": "*" + "symfony/var-dumper": "*", + "heroku/heroku-buildpack-php": "*" }, "scripts": { - "post-install-cmd": [ - "bower install" - ], - "post-update-cmd": [ + "compile": [ "bower install" ] } diff --git a/composer.lock b/composer.lock old mode 100755 new mode 100644 index 5dc16d013..a3173984d --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "279e24152487e695fa54de77461f2a48", - "content-hash": "05c93b4fa9d3879516692dd93f745f68", + "hash": "4ecdb96350cddd6445ab39063bbbcb8c", + "content-hash": "ecb334706b791aa01d6e6870ade3a4c2", "packages": [ { "name": "league/plates", @@ -209,22 +209,126 @@ } ], "packages-dev": [ + { + "name": "heroku/heroku-buildpack-php", + "version": "v90", + "source": { + "type": "git", + "url": "https://github.com/heroku/heroku-buildpack-php.git", + "reference": "5f56ed1ea916bbeec16bf85bec0c99583e33331a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/heroku/heroku-buildpack-php/zipball/5f56ed1ea916bbeec16bf85bec0c99583e33331a", + "reference": "5f56ed1ea916bbeec16bf85bec0c99583e33331a", + "shasum": "" + }, + "bin": [ + "bin/heroku-hhvm-apache2", + "bin/heroku-hhvm-nginx", + "bin/heroku-php-apache2", + "bin/heroku-php-nginx" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Zuelke", + "email": "dz@heroku.com" + } + ], + "description": "Toolkit for starting a PHP application locally, with or without foreman, using the same config for PHP/HHVM and Apache2/Nginx as on Heroku", + "homepage": "http://github.com/heroku/heroku-buildpack-php", + "keywords": [ + "apache", + "apache2", + "foreman", + "heroku", + "hhvm", + "nginx", + "php" + ], + "time": "2015-12-18 14:29:45" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "0b6a8940385311a24e060ec1fe35680e17c74497" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0b6a8940385311a24e060ec1fe35680e17c74497", + "reference": "0b6a8940385311a24e060ec1fe35680e17c74497", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2015-11-04 20:28:58" + }, { "name": "symfony/var-dumper", - "version": "v2.7.7", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8" + "reference": "737e07704cca83f9dd0af926d45ce27eedc25657" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72bcb27411780eaee9469729aace73c0d46fb2b8", - "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/737e07704cca83f9dd0af926d45ce27eedc25657", + "reference": "737e07704cca83f9dd0af926d45ce27eedc25657", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "twig/twig": "~1.20|~2.0" }, "suggest": { "ext-symfony_debug": "" @@ -232,7 +336,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -266,7 +370,7 @@ "debug", "dump" ], - "time": "2015-11-18 13:41:01" + "time": "2015-11-18 13:48:51" } ], "aliases": [], @@ -278,7 +382,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.4.0", + "php": "^5.4.0 || ^5.5.0 || ^5.6.0", "ext-pdo": "*" }, "platform-dev": [] diff --git a/config/herokuConfig.php b/config/herokuConfig.php new file mode 100644 index 000000000..2527e26a0 --- /dev/null +++ b/config/herokuConfig.php @@ -0,0 +1,33 @@ +DOIT) was created by the Center for Distributed Learning at the University of Central Florida. UDOIT will scan your course content, generate a report and provide instructions on how to correct accessibility issues. Funding for UDOIT was provided by a Canvas Grant awarded in 2014.'; -$error_msg_wrong_referrer = 'It looks like you tried to access UDOIT from a website other than Canvas. Please contact support.'; -$error_msg_no_referrer = 'Your web browser did not provide a referrer. Please contact support.'; - -/* Resource links */ -$resource_link = [ - 'doc' => 'http://webaim.org/techniques/word/', - 'pdf' => 'http://webaim.org/techniques/acrobat/', - 'ppt' => 'http://webaim.org/techniques/powerpoint/', -]; - -/* UDOIT test descriptions and examples */ -/* refer to /quail/guidelines/section508.php to view currently enabled tests */ -$udoit_tests = [ - 'severe' => [ - [ - 'name' => 'aMustContainText', - 'title' => 'Links should contain text', - 'desc' => '

Because many users of screen readers use links to navigate the page, providing links with no text (or with images that have empty "alt" attributes and no other readable text) hinders these users.

', - 'resources' => [ - 'Canvas Tutorial', - 'WCAG Guidelines', - 'WCAG Standard 2.4.4' - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('') .'
-

Correct

-
'. htmlspecialchars('read the document') .'
- ', - ], - [ - 'name' => 'imgHasAlt', - 'title' => 'No Alternative Text found', - 'desc' => '

Alternative Text (Alt Text) is an alternative (non-visual) way to describe the meaning of an image. Please provide a brief (under 100 characters) description of the image for a screen reader user. Note: It should not be the image file name.

', - 'resources' => [ - 'Resource on Alternative Text', - 'WCAG Standard: 1.1.1', - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('') .'
-

Correct

-
'. htmlspecialchars('A photograph of a dog') .'
- ', - ], - [ - 'name' => 'imgAltIsDifferent', - 'title' => 'Alternative Text should not be the image filename', - 'desc' => '

Alternative Text (Alt Text) is an alternative (non-visual) way to describe the meaning of an image. Please provide a brief (under 100 characters) description of the image for a screen reader user. Note: It should not be the image file name.

', - 'resources' => [ - 'Resource on Alternative Text', - 'WCAG Standard: 1.1.1', - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('dog.jpg') .'
-
'. htmlspecialchars('http://website.com/dog.jpg') .'
-

Correct

-
'. htmlspecialchars('A photograph of a dog') .'
-
'. htmlspecialchars('A photograph of a dog') .'
- ', - ], - [ - 'name' => 'imgAltIsTooLong', - 'title' => 'Alternative Text is more than 100 characters', - 'desc' => '

Alternative Text (Alt Text) is an alternative (non-visual) way to describe the meaning of an image. Please provide a brief (under 100 characters) description of the image for a screen reader user. Note: It should not be the image file name.

', - 'resources' => [ - 'Resource on Alternative Text', - 'WCAG Standard: 1.1.1' - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('I am alt text that is just way too long, look at me being way too long and being a hassle!') .'
-

Correct

-
'. htmlspecialchars('Short and sweet description') .'
- ', - ], - [ - 'name' => 'imgAltNotEmptyInAnchor', - 'title' => 'Alt text for all img elements used as source anchors should not be empty', - 'desc' => '

Alternative Text (Alt Text) is an alternative (non-visual) way to describe the meaning of an image. Please provide a brief (under 100 characters) description of the image for a screen reader user. Note: It should not be the image file name.

', - 'resources' => [ - 'Resource on Alternative Text', - 'WCAG Standard: 1.1.1' - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars(' ') .'
-

Correct

-
'. htmlspecialchars('Alt text') .'
- ', - ], - [ - 'name' => 'tableDataShouldHaveTh', - 'title' => 'No table headers found', - 'desc' => '

Add a table header because it provides a description of the table structure for sighted and screen reader users.

', - 'resources' => [ - 'Resource Link', - 'WCAG Standard: 1.3.1', - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('
Header OneHeader Two
1.304.50
') .'
-

Correct

-
'. htmlspecialchars('
Header OneHeader Two
1.304.50
') .'
- ', - ], - [ - 'name' => 'tableThShouldHaveScope', - 'title' => 'No row or column scopes declarations found in headers of the table', - 'desc' => '

Scope declarations in headers organize and define table data by row/column for sighted and screen reader users.

', - 'resources' => [ - 'Resource Link', - 'WCAG Standard: 1.3.1', - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars(''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n".'
Heading 1Heading 2
Cell 1Cell 2
') .'
-

Correct

-
'. htmlspecialchars(''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n".'
Heading 1Heading 2
Cell 1Cell 2
') .'
-
'. htmlspecialchars(''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n".'
Heading 1Cell 1
Heading 2Cell 2
') .'
- ', - ], - [ - 'name' => 'cssTextHasContrast', - 'title' => 'Insufficient text color contrast with the background', - 'desc' => '

Text color should be easily viewable and should not be the only indicator of meaning or function. Color balance should have at least a 4.5:1 ratio.

', - 'resources' => [ - 'Resource Link', - 'WCAG Standard 1.4.3', - ], - 'example' => ' -

Incorrect

-

Bad contrasting text

-

Correct

-

Good contrasting text

- ', - ], - [ - 'name' => 'objectMustContainText', - 'title' => 'Multimedia objects should have text equivalents (e.g., transcripts).', - 'desc' => '

Multimedia objects should be accompanied by a link to a transcript of the content.

', - 'resources' => [ - 'WCAG Standard: 1.2.1', - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('') .'
-

Correct

-
'. htmlspecialchars('A widget of stock prices. Access this widget.') .'
- ', - ], - ], - 'suggestion' => [ - [ - 'name' => 'imgGifNoFlicker', - 'title' => 'Avoid the use of animated GIF’s', - 'desc' => '

Animated GIFs may cause seizures if they flash more than 3 times per second. A recommendation is to use an alternative format to deliver the content.

', - 'resources' => [ - 'Resource Link', - 'WCAG Standard: 2.3.1', - ], - 'example' => '', - ], - [ - 'name' => 'videosEmbeddedOrLinkedNeedCaptions', - 'title' => 'Synchronized captions should be provided for prerecorded web-based video', - 'desc' => '

Captions should be included in the video to provide dialogue to users who are hearing impaired.

', - 'resources' => [ - 'Adding Captions to Youtube', - 'Creating Captions for Video Uploaded to Canvas', - 'CDL Video hosted video: CDL Video will caption video if a transcript is provided', - 'WCAG Standard 1.2.2', - ], - 'example' => '', - ], - [ - 'name' => 'aSuspiciousLinkText', - 'title' => 'Link text should be descriptive', - 'desc' => 'Links should be descriptive of the content they\'re linking to, such as "Class Schedule" rather than "schedule.html" or "click here".', - 'resources' => [ - 'Canvas Tutorial', - 'WCAG Guidelines', - 'WCAG Standard 2.4.4' - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('click here!') .'
-

Correct

-
'. htmlspecialchars('read the document') .'
- ', - ], - [ - 'name' => 'objectTextUpdatesWhenObjectChanges', - 'title' => 'The text equivalents (e.g., transcripts and/or captions) for embedded content should update when content changes.', - 'desc' => '', - 'resources' => ['WCAG Standard: 1.2',], - 'example' => '', - ], - // [ - // 'name' => 'objectLinkToMultimediaHasTextTranscript', - // 'title' => 'Multimedia objects should have text equivalents (e.g., transcripts).', - // 'desc' => '

Multimedia objects should be accompanied by a link to a transcript of the content.

', - // 'resources' => [ - // 'WCAG Standard: 1.2.1', - // ], - // 'example' => ' - //

Incorrect

- //
'. htmlspecialchars('') .'
- //

Correct

- //
'. htmlspecialchars('Read Transcript of the video') .'
- // ', - // ], - // [ - // 'name' => 'aLinksToMultiMediaRequireTranscript', - // 'title' => 'Multimedia objects should have text equivalents (e.g., transcripts).', - // 'desc' => '

Multimedia objects should be accompanied by a link to a transcript of the content.

', - // 'resources' => [ - // 'WCAG Standard: 1.2.1', - // ], - // 'example' => ' - //

Incorrect

- //
'. htmlspecialchars('Watch the interview') .'
- //

Correct

- //
'. htmlspecialchars('Watch the interview (transcript)') .'
- // ', - // ], - [ - 'name' => 'headersHaveText', - 'title' => 'Headings should contain text', - 'desc' => '

Sighted and screen reader users depend on headings to organize the content on the page. Headings should not be empty and should represent an accurate outline of the content

', - 'resources' => [ - 'Using H1-H6 to Identify Headings Article', - ], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('

') .'
-

Correct

-
'. htmlspecialchars('

Title

') .'
- ', - ], - [ - 'name' => 'noHeadings', - 'title' => 'Consider adding headings to your document to create more structure', - 'desc' => '

If appropriate, add headings to the page to organize the content for sighted and screen reader users. The headings should represent an accurate outline of the content

', - 'resources' => [ - 'Resource Link', - 'WCAG standard 1.3.1 and 1.3.2', - ], - 'example' => '', - ], - [ - 'name' => 'pNotUsedAsHeader', - 'title' => 'Avoid using styles for document structure', - 'desc' => '

Bold and Italics are used to emphasize text, whereas headings are used to define the structure of the document. Headings like h1-h6 are extremely useful for non-sighted users to navigate the structure of the page, and formatting a paragraph to just be big or bold, while it might visually look like a heading, does not make it one.

', - 'resources' => [], - 'example' => ' -

Incorrect

-
'. htmlspecialchars('

Header 1

') .'
-

Correct

-
'. htmlspecialchars('

Header 1

') .'
- ', - ], - ], -]; diff --git a/config/settings.php b/config/settings.php new file mode 100644 index 000000000..cb85eff9d --- /dev/null +++ b/config/settings.php @@ -0,0 +1,31 @@ +DOIT) was created by the Center for Distributed Learning at the University of Central Florida. UDOIT will scan your course content, generate a report and provide instructions on how to correct accessibility issues. Funding for UDOIT was provided by a Canvas Grant awarded in 2014.'; + +/* Resource links */ +$resource_link = [ + 'doc' => 'http://webaim.org/techniques/word/', + 'pdf' => 'http://webaim.org/techniques/acrobat/', + 'ppt' => 'http://webaim.org/techniques/powerpoint/', +]; diff --git a/config/tests.php b/config/tests.php new file mode 100644 index 000000000..5fd4a321b --- /dev/null +++ b/config/tests.php @@ -0,0 +1,229 @@ + [ + [ + 'name' => 'aMustContainText', + 'title' => 'Links should contain text', + 'desc' => '

Because many users of screen readers use links to navigate the page, providing links with no text (or with images that have empty "alt" attributes and no other readable text) hinders these users.

', + 'resources' => [ + 'Canvas Tutorial', + 'WCAG Guidelines', + 'WCAG Standard 2.4.4' + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('') .'
+

Correct

+
'. htmlspecialchars('read the document') .'
+ ', + ], + [ + 'name' => 'imgHasAlt', + 'title' => 'No Alternative Text found', + 'desc' => '

Alternative Text (Alt Text) is an alternative (non-visual) way to describe the meaning of an image. Please provide a brief (under 100 characters) description of the image for a screen reader user. Note: It should not be the image file name.

', + 'resources' => [ + 'Resource on Alternative Text', + 'WCAG Standard: 1.1.1', + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('') .'
+

Correct

+
'. htmlspecialchars('A photograph of a dog') .'
+ ', + ], + [ + 'name' => 'imgAltIsDifferent', + 'title' => 'Alternative Text should not be the image filename', + 'desc' => '

Alternative Text (Alt Text) is an alternative (non-visual) way to describe the meaning of an image. Please provide a brief (under 100 characters) description of the image for a screen reader user. Note: It should not be the image file name.

', + 'resources' => [ + 'Resource on Alternative Text', + 'WCAG Standard: 1.1.1', + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('dog.jpg') .'
+
'. htmlspecialchars('http://website.com/dog.jpg') .'
+

Correct

+
'. htmlspecialchars('A photograph of a dog') .'
+
'. htmlspecialchars('A photograph of a dog') .'
+ ', + ], + [ + 'name' => 'imgAltIsTooLong', + 'title' => 'Alternative Text is more than 100 characters', + 'desc' => '

Alternative Text (Alt Text) is an alternative (non-visual) way to describe the meaning of an image. Please provide a brief (under 100 characters) description of the image for a screen reader user. Note: It should not be the image file name.

', + 'resources' => [ + 'Resource on Alternative Text', + 'WCAG Standard: 1.1.1' + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('I am alt text that is just way too long, look at me being way too long and being a hassle!') .'
+

Correct

+
'. htmlspecialchars('Short and sweet description') .'
+ ', + ], + [ + 'name' => 'imgAltNotEmptyInAnchor', + 'title' => 'Alt text for all img elements used as source anchors should not be empty', + 'desc' => '

Alternative Text (Alt Text) is an alternative (non-visual) way to describe the meaning of an image. Please provide a brief (under 100 characters) description of the image for a screen reader user. Note: It should not be the image file name.

', + 'resources' => [ + 'Resource on Alternative Text', + 'WCAG Standard: 1.1.1' + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars(' ') .'
+

Correct

+
'. htmlspecialchars('Alt text') .'
+ ', + ], + [ + 'name' => 'tableDataShouldHaveTh', + 'title' => 'No table headers found', + 'desc' => '

Add a table header because it provides a description of the table structure for sighted and screen reader users.

', + 'resources' => [ + 'Resource Link', + 'WCAG Standard: 1.3.1', + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('
Header OneHeader Two
1.304.50
') .'
+

Correct

+
'. htmlspecialchars('
Header OneHeader Two
1.304.50
') .'
+ ', + ], + [ + 'name' => 'tableThShouldHaveScope', + 'title' => 'No row or column scopes declarations found in headers of the table', + 'desc' => '

Scope declarations in headers organize and define table data by row/column for sighted and screen reader users.

', + 'resources' => [ + 'Resource Link', + 'WCAG Standard: 1.3.1', + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars(''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n".'
Heading 1Heading 2
Cell 1Cell 2
') .'
+

Correct

+
'. htmlspecialchars(''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n".'
Heading 1Heading 2
Cell 1Cell 2
') .'
+
'. htmlspecialchars(''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n\t".''."\n\t\t".''."\n\t\t".''."\n\t".''."\n".'
Heading 1Cell 1
Heading 2Cell 2
') .'
+ ', + ], + [ + 'name' => 'cssTextHasContrast', + 'title' => 'Insufficient text color contrast with the background', + 'desc' => '

Text color should be easily viewable and should not be the only indicator of meaning or function. Color balance should have at least a 4.5:1 ratio.

', + 'resources' => [ + 'Resource Link', + 'WCAG Standard 1.4.3', + ], + 'example' => ' +

Incorrect

+

Bad contrasting text

+

Correct

+

Good contrasting text

+ ', + ], + [ + 'name' => 'objectMustContainText', + 'title' => 'Multimedia objects should have text equivalents (e.g., transcripts).', + 'desc' => '

Multimedia objects should be accompanied by a link to a transcript of the content.

', + 'resources' => [ + 'WCAG Standard: 1.2.1', + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('') .'
+

Correct

+
'. htmlspecialchars('A widget of stock prices. Access this widget.') .'
+ ', + ], + ], + 'suggestion' => [ + [ + 'name' => 'imgGifNoFlicker', + 'title' => 'Avoid the use of animated GIF’s', + 'desc' => '

Animated GIFs may cause seizures if they flash more than 3 times per second. A recommendation is to use an alternative format to deliver the content.

', + 'resources' => [ + 'Resource Link', + 'WCAG Standard: 2.3.1', + ], + 'example' => '', + ], + [ + 'name' => 'videosEmbeddedOrLinkedNeedCaptions', + 'title' => 'Synchronized captions should be provided for prerecorded web-based video', + 'desc' => '

Captions should be included in the video to provide dialogue to users who are hearing impaired.

', + 'resources' => [ + 'Adding Captions to Youtube', + 'Creating Captions for Video Uploaded to Canvas', + 'CDL Video hosted video: CDL Video will caption video if a transcript is provided', + 'WCAG Standard 1.2.2', + ], + 'example' => '', + ], + [ + 'name' => 'aSuspiciousLinkText', + 'title' => 'Link text should be descriptive', + 'desc' => 'Links should be descriptive of the content they\'re linking to, such as "Class Schedule" rather than "schedule.html" or "click here".', + 'resources' => [ + 'Canvas Tutorial', + 'WCAG Guidelines', + 'WCAG Standard 2.4.4' + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('click here!') .'
+

Correct

+
'. htmlspecialchars('read the document') .'
+ ', + ], + [ + 'name' => 'objectTextUpdatesWhenObjectChanges', + 'title' => 'The text equivalents (e.g., transcripts and/or captions) for embedded content should update when content changes.', + 'desc' => '', + 'resources' => ['WCAG Standard: 1.2',], + 'example' => '', + ], + [ + 'name' => 'headersHaveText', + 'title' => 'Headings should contain text', + 'desc' => '

Sighted and screen reader users depend on headings to organize the content on the page. Headings should not be empty and should represent an accurate outline of the content

', + 'resources' => [ + 'Using H1-H6 to Identify Headings Article', + ], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('

') .'
+

Correct

+
'. htmlspecialchars('

Title

') .'
+ ', + ], + [ + 'name' => 'noHeadings', + 'title' => 'Consider adding headings to your document to create more structure', + 'desc' => '

If appropriate, add headings to the page to organize the content for sighted and screen reader users. The headings should represent an accurate outline of the content

', + 'resources' => [ + 'Resource Link', + 'WCAG standard 1.3.1 and 1.3.2', + ], + 'example' => '', + ], + [ + 'name' => 'pNotUsedAsHeader', + 'title' => 'Avoid using styles for document structure', + 'desc' => '

Bold and Italics are used to emphasize text, whereas headings are used to define the structure of the document. Headings like h1-h6 are extremely useful for non-sighted users to navigate the structure of the page, and formatting a paragraph to just be big or bold, while it might visually look like a heading, does not make it one.

', + 'resources' => [], + 'example' => ' +

Incorrect

+
'. htmlspecialchars('

Header 1

') .'
+

Correct

+
'. htmlspecialchars('

Header 1

') .'
+ ', + ], + ], +]; diff --git a/db_mysql_setup.php b/db_mysql_setup.php new file mode 100644 index 000000000..e9608c6a3 --- /dev/null +++ b/db_mysql_setup.php @@ -0,0 +1,29 @@ +query($reports_sql); +$sth->execute(); + +$sth = $dbh->query($users_sql); +$sth->execute(); + diff --git a/db_pg_setup.php b/db_pg_setup.php new file mode 100644 index 000000000..76c8cf227 --- /dev/null +++ b/db_pg_setup.php @@ -0,0 +1,25 @@ +query($reports_sql); +$sth->execute(); + +$sth = $dbh->query($users_sql); +$sth->execute(); diff --git a/img/udoit_logo.png b/img/udoit_logo.png deleted file mode 100644 index 97197ed77..000000000 Binary files a/img/udoit_logo.png and /dev/null differ diff --git a/lib/Udoit.php b/lib/Udoit.php index d542bdff2..3cc96386b 100755 --- a/lib/Udoit.php +++ b/lib/Udoit.php @@ -17,8 +17,8 @@ * * Primary Author Contact: Jacob Bates */ -require_once '../core/quail/quail.php'; -require '../vendor/autoload.php'; +require_once('../vendor/autoload.php'); +require_once('quail/quail/quail.php'); use Httpful\Request; diff --git a/lib/Ufixit.php b/lib/Ufixit.php index b7014629a..60f3b288d 100755 --- a/lib/Ufixit.php +++ b/lib/Ufixit.php @@ -17,8 +17,8 @@ * * Primary Author Contact: Jacob Bates */ -require_once '../core/quail/quail.php'; -require '../vendor/autoload.php'; +require_once('../vendor/autoload.php'); +require_once('quail/quail/quail.php'); use Httpful\Request; use zz\Html\HTMLMinify; diff --git a/lib/db.php b/lib/db.php new file mode 100644 index 000000000..1490c3ca5 --- /dev/null +++ b/lib/db.php @@ -0,0 +1,15 @@ +getMessage()); + echo 'Database Connection error'; +} diff --git a/core/CHANGELOG.txt b/lib/quail/CHANGELOG.txt similarity index 100% rename from core/CHANGELOG.txt rename to lib/quail/CHANGELOG.txt diff --git a/core/LICENSE.txt b/lib/quail/LICENSE.txt similarity index 100% rename from core/LICENSE.txt rename to lib/quail/LICENSE.txt diff --git a/core/README.txt b/lib/quail/README.txt similarity index 100% rename from core/README.txt rename to lib/quail/README.txt diff --git a/core/examples/HFT3273-0M62.html b/lib/quail/examples/HFT3273-0M62.html similarity index 100% rename from core/examples/HFT3273-0M62.html rename to lib/quail/examples/HFT3273-0M62.html diff --git a/core/examples/css/main.css b/lib/quail/examples/css/main.css similarity index 100% rename from core/examples/css/main.css rename to lib/quail/examples/css/main.css diff --git a/assets/img/error.png b/lib/quail/examples/img/error.png similarity index 100% rename from assets/img/error.png rename to lib/quail/examples/img/error.png diff --git a/assets/img/suggestion.png b/lib/quail/examples/img/suggestion.png similarity index 100% rename from assets/img/suggestion.png rename to lib/quail/examples/img/suggestion.png diff --git a/assets/img/warning.png b/lib/quail/examples/img/warning.png similarity index 100% rename from assets/img/warning.png rename to lib/quail/examples/img/warning.png diff --git a/core/examples/test.php b/lib/quail/examples/test.php similarity index 100% rename from core/examples/test.php rename to lib/quail/examples/test.php diff --git a/core/examples/test_form.php b/lib/quail/examples/test_form.php similarity index 98% rename from core/examples/test_form.php rename to lib/quail/examples/test_form.php index b4a921ec9..7950ebb7a 100755 --- a/core/examples/test_form.php +++ b/lib/quail/examples/test_form.php @@ -33,7 +33,7 @@ function find_directory($dir, $ignore) { $fullReport = array(); - require_once('../quail/quail.php'); + require_once('../lib/quail/quail/quail.php'); foreach($test as $html) { $error = 0; diff --git a/core/quail/common/accessibility_tests.php b/lib/quail/quail/common/accessibility_tests.php similarity index 99% rename from core/quail/common/accessibility_tests.php rename to lib/quail/quail/common/accessibility_tests.php index 1d592c727..dda92086e 100755 --- a/core/quail/common/accessibility_tests.php +++ b/lib/quail/quail/common/accessibility_tests.php @@ -1,6 +1,6 @@ '+data+''); $('#result').fadeIn(); @@ -355,7 +354,7 @@ $(document).ready(function() { result_html.find('.error-desc').append('

'); result_html.find('a.list-group-item').after('
'); - var form = $('
' + + var form = $('' + '' + '' + '
'); @@ -373,7 +372,7 @@ $(document).ready(function() { } $.ajax({ - url: "./lib/cached.php", + url: "cached.php", type: "GET", success: function(data) { $("#cached").html(data); @@ -421,4 +420,4 @@ $(document).ready(function() { } }); // END click to remove/fill link with no text -}); \ No newline at end of file +}); diff --git a/lib/cached.php b/public/cached.php similarity index 80% rename from lib/cached.php rename to public/cached.php index 29ce3a4f0..3ac7fc084 100755 --- a/lib/cached.php +++ b/public/cached.php @@ -17,18 +17,10 @@ * * Primary Author Contact: Jacob Bates */ -require '../vendor/autoload.php'; -include_once('../config/localConfig.php'); +require_once('../config/settings.php'); +$dbh = include('../lib/db.php'); // saves the report to the database -$dsn = "mysql:dbname=$db_name;host=$db_host"; - -try { - $dbh = new PDO($dsn, $db_user, $db_password); -} catch (PDOException $e) { - echo 'Connection failed: ' . $e->getMessage(); -} - $sth = $dbh->prepare(" SELECT * FROM $db_reports_table @@ -52,7 +44,6 @@ $reports = $sth->fetchAll(); ?> -
@@ -66,12 +57,12 @@ - - - - + + + +
Saved reports for this course
-
\ No newline at end of file + diff --git a/index.php b/public/index.php similarity index 90% rename from index.php rename to public/index.php index 195c05c7b..45ef8ff97 100755 --- a/index.php +++ b/public/index.php @@ -17,8 +17,8 @@ * * Primary Author Contact: Jacob Bates */ -require_once('vendor/autoload.php'); -require_once('config/localConfig.php'); +require_once('../config/settings.php'); + use Httpful\Request; error_reporting(E_ALL & ~E_NOTICE); @@ -26,18 +26,20 @@ session_start(); header('Content-Type: text/html; charset=utf-8'); -$templates = new League\Plates\Engine('templates'); +$templates = new League\Plates\Engine('../templates'); if ( ! isset($_SESSION['valid'])) { $_SESSION['valid'] = false; } if ($_SESSION['valid'] === false) { - include_once('lib/ims-blti/blti.php'); + require_once('../lib/ims-blti/blti.php'); // Initialize, all secrets are 'secret', do not set session, and do not redirect $context = new BLTI($consumer_key, $shared_secret, false, false); if ( ! $context->valid) { + error_log("BLTI not valid: our key: {$consumer_key}"); + error_log($context->message); $error = 'Configuration problem, please ensure that your instance of UDOIT is configured correctly.'; echo $templates->render('error', ['error' => $error]); exit(); @@ -79,16 +81,9 @@ die(); } -// Pull the API key from the database -try { - $dsn = "mysql:dbname=$db_name;host=$db_host"; - $dbh = new PDO($dsn, $db_user, $db_password); -} catch (PDOException $e) { - $_SESSION['valid'] = false; - echo $templates->render('error', ['error' => 'Connection failed: ' . $e->getMessage()]); - exit(); -} +$dbh = include('../lib/db.php'); +// Pull the API key from the database $sth = $dbh->prepare("SELECT * FROM $db_user_table WHERE id=:userid LIMIT 1"); $sth->bindParam(':userid', $_SESSION['launch_params']['custom_canvas_user_id'], PDO::PARAM_INT); $sth->execute(); diff --git a/oauth2response.php b/public/oauth2response.php similarity index 79% rename from oauth2response.php rename to public/oauth2response.php index 34cab705d..82b137cc7 100755 --- a/oauth2response.php +++ b/public/oauth2response.php @@ -17,8 +17,8 @@ * * Primary Author Contact: Jacob Bates */ -include_once('config/localConfig.php'); -include_once('vendor/autoload.php'); +require_once('../config/settings.php'); + session_start(); function printError($msg){ @@ -69,19 +69,25 @@ function printError($msg){ $_SESSION['api_key'] = $response->access_token; // Save API Key to DB - $dsn = "mysql:dbname=$db_name;host=$db_host"; + $dbh = include('../lib/db.php'); - try { - $dbh = new PDO($dsn, $db_user, $db_password); - } catch (PDOException $e) { - echo 'Connection failed: ' . $e->getMessage(); - } - $sth = $dbh->prepare("INSERT INTO $db_user_table (id, api_key, date_created) VALUES (:userid, :key, NOW()) ON DUPLICATE KEY UPDATE api_key=VALUES(api_key)"); + $sth = $dbh->prepare("SELECT * FROM $db_user_table WHERE id=:userid"); $sth->bindParam(':userid', $_SESSION['launch_params']['custom_canvas_user_id'], PDO::PARAM_INT); + $sth->execute(); + + if($sth->rowCount()) { + $sth = $dbh->prepare("UPDATE $db_user_table (api_key, date_created) VALUES (:key, :time)"); + } + else { + $sth = $dbh->prepare("INSERT INTO $db_user_table (id, api_key, date_created) VALUES (:userid, :key, :time)"); + $sth->bindParam(':userid', $_SESSION['launch_params']['custom_canvas_user_id'], PDO::PARAM_INT); + } + $sth->bindParam(':key', $_SESSION['api_key'], PDO::PARAM_STR); + $sth->bindValue(':time', time(), PDO::PARAM_INT); $sth->execute(); - + session_write_close(); header('Location:index.php'); } elseif (isset($_GET['error'])) { diff --git a/lib/parsePdf.php b/public/parsePdf.php similarity index 97% rename from lib/parsePdf.php rename to public/parsePdf.php index bec8e2f0b..76269fe1e 100755 --- a/lib/parsePdf.php +++ b/public/parsePdf.php @@ -17,7 +17,7 @@ * * Primary Author Contact: Jacob Bates */ -require '../vendor/autoload.php'; +require_once('../config/settings.php'); use zz\Html\HTMLMinify; @@ -42,5 +42,3 @@ $pdf->WriteHTML($html, 2); $pdf->Output($title.'_'.date("Y-m-d_g:i-a").'.pdf', 'D'); - -exit(); \ No newline at end of file diff --git a/lib/parseResults.php b/public/parseResults.php similarity index 98% rename from lib/parseResults.php rename to public/parseResults.php index 153d6843d..b9b001917 100755 --- a/lib/parseResults.php +++ b/public/parseResults.php @@ -17,17 +17,10 @@ * * Primary Author Contact: Jacob Bates */ -include_once('../config/localConfig.php'); +require_once('../config/settings.php'); if (isset($_POST['cached_id'])) { - // saves the report to the database - $dsn = "mysql:dbname=$db_name;host=$db_host"; - - try { - $dbh = new PDO($dsn, $db_user, $db_password); - } catch (PDOException $e) { - echo 'Connection failed: ' . $e->getMessage(); - } + $dbh = include('../lib/db.php'); $sth = $dbh->prepare(" SELECT * FROM @@ -39,7 +32,8 @@ $sth->bindParam(':cachedid', $_POST['cached_id'], PDO::PARAM_INT); if (!$sth->execute()) { - die('Ya done goof\'d'); + error_log(print_r($sth->errorInfo(), true)); + die('Error searching for report'); } $the_json = file_get_contents($sth->fetchAll(PDO::FETCH_OBJ)[0]->file_path); diff --git a/lib/process.php b/public/process.php similarity index 91% rename from lib/process.php rename to public/process.php index 40daced8b..327c335c1 100755 --- a/lib/process.php +++ b/public/process.php @@ -17,11 +17,10 @@ * * Primary Author Contact: Jacob Bates */ -require_once('../config/localConfig.php'); -require_once('../core/quail/quail.php'); -require('../vendor/autoload.php'); -include 'Udoit.php'; -include 'Ufixit.php'; +require_once('../config/settings.php'); +require_once('../lib/quail/quail/quail.php'); +require_once('../lib/Udoit.php'); +require_once('../lib/Ufixit.php'); session_start(); @@ -77,26 +76,16 @@ file_put_contents($file, $encoded_report); chmod($file, 0777); - // saves the report to the database - $dsn = "mysql:dbname=$db_name;host=$db_host"; - - try { - $dbh = new PDO($dsn, $db_user, $db_password); - } catch (PDOException $e) { - echo 'Connection failed: ' . $e->getMessage(); - } + $dbh = include('../lib/db.php'); $sth = $dbh->prepare(" INSERT INTO $db_reports_table - SET - user_id=:userid, - course_id=:courseid, - file_path=:filepath, - date_run=NOW(), - errors=:errors, - suggestions=:suggestions - "); + (user_id, course_id, file_path, date_run, errors, suggestions) + VALUES + (:userid, :courseid, :filepath, :time, :errors, :suggestions)"); + $now = time(); + $sth->bindParam(':time', $now, PDO::PARAM_INT); $sth->bindParam(':userid', $user_id, PDO::PARAM_INT); $sth->bindParam(':courseid', $data['course_id'], PDO::PARAM_INT); $sth->bindParam(':filepath', $file, PDO::PARAM_STR); @@ -104,7 +93,8 @@ $sth->bindParam(':suggestions', $udoit->total_results['suggestions'], PDO::PARAM_STR); if (!$sth->execute()) { - die('Ya done goof\'d'); + error_log(print_r($sth->errorInfo(), true)); + die('Error inserting report into database'); } $udoit_report = json_decode($encoded_report); diff --git a/lib/progress.php b/public/progress.php similarity index 100% rename from lib/progress.php rename to public/progress.php diff --git a/reports/.gitkeep b/public/reports/.gitkeep similarity index 100% rename from reports/.gitkeep rename to public/reports/.gitkeep diff --git a/udoit.xml.php b/public/udoit.xml.php similarity index 88% rename from udoit.xml.php rename to public/udoit.xml.php index de134dd79..0d9041447 100755 --- a/udoit.xml.php +++ b/public/udoit.xml.php @@ -17,12 +17,13 @@ * * Primary Author Contact: Jacob Bates */ - $servername = 'https://' . $_SERVER['SERVER_NAME']; - $scriptname=end(explode('/',$_SERVER['PHP_SELF'])); - $scriptpath=str_replace($scriptname,'',$_SERVER['PHP_SELF']); - $launch = $servername . $scriptpath; - header('Content-type: text/xml'); - echo ''; +$servername = 'https://' . $_SERVER['SERVER_NAME']; +$scriptname=end(explode('/',$_SERVER['PHP_SELF'])); +$scriptpath=str_replace($scriptname,'',$_SERVER['PHP_SELF']); +$launch = $servername . $scriptpath; +header('Content-type: text/xml'); +echo ''; + ?> UDOIT This tool allows you scan your courses and check for common accessibility issues. - + /assets/img/udoit_logo.png udoit diff --git a/templates/udoit.php b/templates/udoit.php index 1b2049c9d..f1de0aa48 100644 --- a/templates/udoit.php +++ b/templates/udoit.php @@ -5,7 +5,7 @@ "//code.jquery.com/jquery-2.1.1.min.js", "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js", "assets/js/vendor/JSColor/jscolor.js", - "assets/js/default.js?c=0", + "assets/js/default.js?cachebuster=".time(), ] ];