Skip to content
Snippets Groups Projects
Commit f4a9db3e authored by Michaela Brückner's avatar Michaela Brückner :unicorn: Committed by David Siegfried
Browse files

Accessibility: Adds an alternative, high-contrast color-scheme, closes #96

Closes #96

Merge request studip/studip!728
parent 263edc8e
No related branches found
No related tags found
No related merge requests found
Showing
with 1683 additions and 56 deletions
<?php
/**
* Settings_AccessibilityController - Administration of all user accessibility related
* settings
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*
* @author Michaela Brückner <brueckner@data-quest.de>
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
* @since 5.2
*/
require_once 'settings.php';
class Settings_AccessibilityController extends Settings_SettingsController
{
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
PageLayout::setTitle(_('Barrierefreiheitseinstellungen'));
Navigation::activateItem('/profile/settings/accessibility');
SkipLinks::addIndex(_('Barrierefreiheitseinstellungen anpassen'), 'layout_content', 100);
}
public function index_action()
{
}
public function store_action()
{
CSRFProtection::verifyUnsafeRequest();
$this->config->store('USER_HIGH_CONTRAST', Request::bool('enable_high_contrast'));
$this->config->store('SKIPLINKS_ENABLE', Request::bool('skiplinks_enable'));
PageLayout::postSuccess(_('Ihre Einstellungen wurden gespeichert.'));
$this->redirect('settings/accessibility');
}
}
<? if (!$print_schedules): ?> <? if (!$print_schedules): ?>
<form class="default" method="post"
action="<?= $controller->link_for('resources/print/clipboard_rooms') ?>"> <? if ($clipboard_selected): ?>
<?= CSRFProtection::tokenTag() ?> <form class="default" method="post"
<? if ($clipboard_selected): ?> action="<?= $controller->link_for('resources/print/clipboard_rooms') ?>">
<?= CSRFProtection::tokenTag() ?>
<input type="hidden" name="clipboard_id" value="<?= htmlReady($selected_clipboard_id) ?>"> <input type="hidden" name="clipboard_id" value="<?= htmlReady($selected_clipboard_id) ?>">
<input type="hidden" name="schedule_type" value="<?= htmlReady($schedule_type) ?>"> <input type="hidden" name="schedule_type" value="<?= htmlReady($schedule_type) ?>">
<input type="hidden" name="date" value="<?= htmlReady($selected_date_string) ?>"> <input type="hidden" name="date" value="<?= htmlReady($selected_date_string) ?>">
...@@ -27,16 +29,16 @@ ...@@ -27,16 +29,16 @@
) ?> ) ?>
</legend> </legend>
<ul> <ul>
<? foreach ($available_rooms as $room): ?> <? foreach ($available_rooms as $room): ?>
<li> <li>
<label> <label>
<input type="checkbox" value="<?= htmlReady($room->id) ?>" <input type="checkbox" value="<?= htmlReady($room->id) ?>"
checked="checked" checked="checked"
name="selected_room_ids[]"> name="selected_room_ids[]">
<?= htmlReady($room->getFullName()) ?> <?= htmlReady($room->getFullName()) ?>
</label> </label>
</li> </li>
<? endforeach ?> <? endforeach ?>
</ul> </ul>
<? endif ?> <? endif ?>
</fieldset> </fieldset>
...@@ -52,8 +54,13 @@ ...@@ -52,8 +54,13 @@
'null' 'null'
) ?> ) ?>
</div> </div>
<? else: ?> </form>
<? if(count($available_clipboards)) : ?> <? else: ?>
<? if(count($available_clipboards)) : ?>
<form class="default" method="post"
action="<?= $controller->link_for('resources/print/clipboard_rooms') ?>">
<?= CSRFProtection::tokenTag() ?>
<fieldset> <fieldset>
<label> <label>
<?= _('Individuelle Raumgruppe') ?>: <?= _('Individuelle Raumgruppe') ?>:
...@@ -69,21 +76,21 @@ ...@@ -69,21 +76,21 @@
<?= _('Art des Belegungsplanes') ?>: <?= _('Art des Belegungsplanes') ?>:
<select name="schedule_type"> <select name="schedule_type">
<option value="w" <option value="w"
<?= $selected_schedule == 'w' <?= $selected_schedule == 'w'
? 'selected="selected"' ? 'selected="selected"'
: '' ?>> : '' ?>>
<?= _('Wochenplan') ?> <?= _('Wochenplan') ?>
</option> </option>
<option value="w+we" <option value="w+we"
<?= $selected_schedule == 'w+we' <?= $selected_schedule == 'w+we'
? 'selected="selected"' ? 'selected="selected"'
: '' ?>> : '' ?>>
<?= _('Wochenplan inklusive Wochenende') ?> <?= _('Wochenplan inklusive Wochenende') ?>
</option> </option>
<option value="d" <option value="d"
<?= $selected_schedule == 'd' <?= $selected_schedule == 'd'
? 'selected="selected"' ? 'selected="selected"'
: '' ?>> : '' ?>>
<?= _('Tagesplan') ?> <?= _('Tagesplan') ?>
</option> </option>
</select> </select>
...@@ -111,19 +118,19 @@ ...@@ -111,19 +118,19 @@
'select_clipboard' 'select_clipboard'
) ?> ) ?>
</div> </div>
<? else :?> </form>
<?= MessageBox::info( <? else :?>
_('Sie müssen zunächst Raumgruppen erstellen'), <?= MessageBox::info(
[ _('Sie müssen zunächst Raumgruppen erstellen'),
sprintf( [
_('Klicken %shier%s, um ein Raumgruppen anzulegen.'), sprintf(
'<a href="' . URLHelper::getLink('dispatch.php/room_management/overview/rooms') . '">', _('Klicken %shier%s, um ein Raumgruppen anzulegen.'),
'</a>') '<a href="' . URLHelper::getLink('dispatch.php/room_management/overview/rooms') . '">',
] '</a>')
)?> ]
<? endif ?> )?>
<? endif ?> <? endif ?>
</form> <? endif ?>
<? else: ?> <? else: ?>
<? if (Request::get("allday")) { <? if (Request::get("allday")) {
$min_time = '00:00:00'; $min_time = '00:00:00';
...@@ -158,14 +165,14 @@ ...@@ -158,14 +165,14 @@
], ],
'defaultView' => 'defaultView' =>
in_array(Request::get("defaultView"), ['dayGridMonth','timeGridWeek','timeGridDay']) in_array(Request::get("defaultView"), ['dayGridMonth','timeGridWeek','timeGridDay'])
? Request::get("defaultView") ? Request::get("defaultView")
: 'timeGridWeek', : 'timeGridWeek',
'defaultDate' => Request::get("defaultDate", $print_date), 'defaultDate' => Request::get("defaultDate", $print_date),
'eventSources' => [ 'eventSources' => [
[ [
'url' => URLHelper::getURL( 'url' => URLHelper::getURL(
'api.php/resources/resource/' 'api.php/resources/resource/'
. $room->id . '/booking_plan' . $room->id . '/booking_plan'
), ),
'method' => 'GET', 'method' => 'GET',
'extraParams' => [ 'extraParams' => [
......
<form method="post" action="<?= $controller->store() ?>" class="default">
<?= CSRFProtection::tokenTag() ?>
<fieldset>
<legend id="accessibility"><?= _('Barrierefreiheitseinstellungen') ?></legend>
<label>
<input type="checkbox" name="enable_high_contrast"
value="1"
<? if ($config->USER_HIGH_CONTRAST) echo 'checked'; ?>>
<?= _('Kontrastreiches Farbschema aktivieren') ?>
<?= tooltipIcon(
_('Mit dieser Einstellung wird ein Farbschema mit hohem Kontrast aktiviert.')
) ?>
</label>
<label>
<input type="checkbox" name="skiplinks_enable"
value="1"
<? if ($config->SKIPLINKS_ENABLE) echo 'checked'; ?>>
<?= _('Skiplinks einblenden') ?>
<?= tooltipIcon(_('Mit dieser Einstellung wird nach dem ersten Drücken der Tab-Taste eine '
.'Liste mit Skiplinks eingeblendet, mit deren Hilfe Sie mit der Tastatur '
.'schneller zu den Hauptinhaltsbereichen der Seite navigieren können. '
.'Zusätzlich wird der aktive Bereich einer Seite hervorgehoben.')) ?>
</label>
</fieldset>
<footer>
<?= \Studip\Button::create(_('Speichern')) ?>
</footer>
</form>
...@@ -46,17 +46,6 @@ $start_pages = [ ...@@ -46,17 +46,6 @@ $start_pages = [
</label> </label>
<? endif ?> <? endif ?>
<label>
<input type="checkbox" name="skiplinks_enable"
value="1"
<? if ($config->SKIPLINKS_ENABLE) echo 'checked'; ?>>
<?= _('Skiplinks einblenden') ?>
<?= tooltipIcon(_('Mit dieser Einstellung wird nach dem ersten Drücken der Tab-Taste eine '
.'Liste mit Skiplinks eingeblendet, mit deren Hilfe Sie mit der Tastatur '
.'schneller zu den Hauptinhaltsbereichen der Seite navigieren können. '
.'Zusätzlich wird der aktive Bereich einer Seite hervorgehoben.')) ?>
</label>
<label> <label>
<input type="checkbox" <input type="checkbox"
name="showsem_enable" name="showsem_enable"
......
<?php
class AddHighContrastConfigEntry extends Migration
{
public function description()
{
return 'Adds configuration field USER_HIGH_CONTRAST';
}
public function up()
{
$db = DBManager::get();
$db->exec(
"INSERT IGNORE INTO `config`
(`field`, `value`, `type`, `range`,
`section`,
`mkdate`, `chdate`,
`description`)
VALUES
('USER_HIGH_CONTRAST', '0', 'boolean', 'user',
'accessibility', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(),
'Schaltet ein barrierefreies Stylesheet mit hohem Kontrast ein oder aus.')"
);
}
public function down()
{
$db = DBManager::get();
$db->exec(
"DELETE FROM `config_values`
WHERE `field` = 'USER_HIGH_CONTRAST'"
);
$db->exec(
"DELETE FROM `config`
WHERE `field` = 'USER_HIGH_CONTRAST'"
);
}
}
...@@ -38,6 +38,11 @@ class AvatarNavigation extends Navigation ...@@ -38,6 +38,11 @@ class AvatarNavigation extends Navigation
$navigation = new Navigation(_('Einstellungen'), 'dispatch.php/settings/general'); $navigation = new Navigation(_('Einstellungen'), 'dispatch.php/settings/general');
$navigation->setImage(Icon::create('admin')); $navigation->setImage(Icon::create('admin'));
$this->addSubNavigation('settings', $navigation); $this->addSubNavigation('settings', $navigation);
// Link to accessibility settings
$navigation = new Navigation(_('Barrierefreiheit'), 'dispatch.php/settings/accessibility');
$navigation->setImage(Icon::create('accessibility'));
$this->addSubNavigation('accessibility', $navigation);
} }
// Link to logout // Link to logout
......
...@@ -114,6 +114,11 @@ class ProfileNavigation extends Navigation ...@@ -114,6 +114,11 @@ class ProfileNavigation extends Navigation
$navigation->addSubNavigation('tfa', new Navigation(_('Zwei-Faktor-Authentifizierung'), 'dispatch.php/tfa')); $navigation->addSubNavigation('tfa', new Navigation(_('Zwei-Faktor-Authentifizierung'), 'dispatch.php/tfa'));
} }
$navigation->addSubNavigation('accessibility', new Navigation(
_('Barrierefreiheitseinstellungen'),
'dispatch.php/settings/accessibility'
));
$this->addSubNavigation('settings', $navigation); $this->addSubNavigation('settings', $navigation);
} }
......
...@@ -120,7 +120,7 @@ class Seminar_Auth ...@@ -120,7 +120,7 @@ class Seminar_Auth
# Check for user supplied automatic login procedure # Check for user supplied automatic login procedure
if ($uid = $this->auth_preauth()) { if ($uid = $this->auth_preauth()) {
$this->auth["uid"] = $uid; $this->auth["uid"] = $uid;
$sess->regenerate_session_id(['auth', '_language', 'phpCAS']); $sess->regenerate_session_id(['auth', '_language', 'phpCAS', 'contrast']);
$sess->freeze(); $sess->freeze();
$GLOBALS['user'] = new Seminar_User($this->auth['uid']); $GLOBALS['user'] = new Seminar_User($this->auth['uid']);
return true; return true;
...@@ -170,7 +170,7 @@ class Seminar_Auth ...@@ -170,7 +170,7 @@ class Seminar_Auth
case "log": case "log":
if ($uid = $this->auth_validatelogin()) { if ($uid = $this->auth_validatelogin()) {
$this->auth["uid"] = $uid; $this->auth["uid"] = $uid;
$keep_session_vars = ['auth', 'forced_language', '_language']; $keep_session_vars = ['auth', 'forced_language', '_language', 'contrast'];
if ($this->auth['perm'] === 'root') { if ($this->auth['perm'] === 'root') {
$keep_session_vars[] = 'plugins_disabled'; $keep_session_vars[] = 'plugins_disabled';
} }
...@@ -455,5 +455,9 @@ class Seminar_Auth ...@@ -455,5 +455,9 @@ class Seminar_Auth
// init of output via I18N // init of output via I18N
$_language_path = init_i18n($_SESSION['_language']); $_language_path = init_i18n($_SESSION['_language']);
include 'config.inc.php'; include 'config.inc.php';
if (isset($_SESSION['contrast'])) {
PageLayout::addStylesheet('accessibility.css');
}
} }
} }
...@@ -101,12 +101,20 @@ if ($auth->is_authenticated() && is_object($user) && $user->id != "nobody") { ...@@ -101,12 +101,20 @@ if ($auth->is_authenticated() && is_object($user) && $user->id != "nobody") {
if (UserConfig::get($user->id)->PERSONAL_STARTPAGE > 0 && $i_page == "index.php" && !$perm->have_perm("root")) { if (UserConfig::get($user->id)->PERSONAL_STARTPAGE > 0 && $i_page == "index.php" && !$perm->have_perm("root")) {
$seminar_open_redirected = TRUE; $seminar_open_redirected = TRUE;
} }
if ($_SESSION['contrast']) {
UserConfig::get($GLOBALS['user']->id)->store('USER_HIGH_CONTRAST', 1);
unset($_SESSION['contrast']);
}
$user_did_login = true; $user_did_login = true;
} }
TwoFactorAuth::get()->secureSession(); TwoFactorAuth::get()->secureSession();
} }
if (isset($_SESSION['contrast']) || UserConfig::get($GLOBALS['user']->id)->USER_HIGH_CONTRAST) {
PageLayout::addStylesheet('accessibility.css');
}
// init of output via I18N // init of output via I18N
$_language_path = init_i18n($_SESSION['_language']); $_language_path = init_i18n($_SESSION['_language']);
//force reload of config to get translated data //force reload of config to get translated data
......
<svg viewBox="0 0 54 54" xmlns="http://www.w3.org/2000/svg"><g fill="#28497c"><path d="m27 8c10.5 0 19 8.5 19 19s-8.5 19-19 19-19-8.6-19-19 8.5-19 19-19m0-5c-13.3 0-24 10.7-24 24s10.7 24 24 24 24-10.7 24-24-10.7-24-24-24z"/><circle cx="27" cy="15" r="4"/><path d="m38.2 19.1c-.6-.9-1.7-1.1-2.7-.8l-5.8 2.1c-.8.4-1.8.6-2.7.6h-.2c-.9 0-1.9-.2-2.8-.5l-5.8-2.1c-1-.4-2.1-.1-2.7.8-.8 1.2-.2 2.8 1.1 3.3l7.4 2.7c.3.1.5.4.5.7v3.7c0 .3-.1.6-.2.9l-4.5 7.9c-.5.9-.4 2.1.3 2.8 1.1 1 2.7.7 3.4-.5l3.5-6.1 3.6 6.2c.6 1 1.9 1.4 3 .8 1-.6 1.4-2 .8-3.1l-4.6-7.9c-.2-.3-.2-.6-.2-.9v-3.8c0-.3.2-.6.5-.7l7.2-2.6c1.2-.6 1.8-2.2.9-3.5z"/></g></svg>
\ No newline at end of file
...@@ -23,6 +23,14 @@ page_open(['sess' => 'Seminar_Session', 'auth' => 'Seminar_Default_Auth', 'perm' ...@@ -23,6 +23,14 @@ page_open(['sess' => 'Seminar_Session', 'auth' => 'Seminar_Default_Auth', 'perm'
$auth->login_if(Request::get('again') && ($auth->auth['uid'] == 'nobody')); $auth->login_if(Request::get('again') && ($auth->auth['uid'] == 'nobody'));
// if desired, switch to high contrast stylesheet and store when user logs in
if (Request::get('unset_contrast')) {
unset($_SESSION['contrast']);
}
if (Request::get('set_contrast') ) {
$_SESSION['contrast'] = true;
}
// evaluate language clicks // evaluate language clicks
// has to be done before seminar_open to get switching back to german (no init of i18n at all)) // has to be done before seminar_open to get switching back to german (no init of i18n at all))
if (Request::get('set_language')) { if (Request::get('set_language')) {
......
...@@ -40,6 +40,7 @@ if ($auth->auth["uid"]!="nobody") { ...@@ -40,6 +40,7 @@ if ($auth->auth["uid"]!="nobody") {
$logout_user=$user->id; $logout_user=$user->id;
$_language = $_SESSION['_language']; $_language = $_SESSION['_language'];
$contrast = UserConfig::get($GLOBALS['user']->id)->USER_HIGH_CONTRAST;
// TODO this needs to be generalized or removed // TODO this needs to be generalized or removed
//erweiterung cas //erweiterung cas
...@@ -66,6 +67,6 @@ if ($auth->auth["uid"]!="nobody") { ...@@ -66,6 +67,6 @@ if ($auth->auth["uid"]!="nobody") {
page_close(); page_close();
} }
header("Location:" . URLHelper::getURL("index.php?logout=true&set_language=$_language")); header("Location:" . URLHelper::getURL("index.php?logout=true&set_language=$_language&set_contrast=$contrast"));
?> ?>
This diff is collapsed.
...@@ -59,6 +59,25 @@ div.index_container { ...@@ -59,6 +59,25 @@ div.index_container {
} }
} }
div#contrast {
display: flex;
align-items: center;
gap: 5px;
border-top: 1px solid @light-gray-color;
font-size: 0.9em;
padding: 10px;
a {
text-decoration: underline;
color: @contrast-blue;
&:hover, &:focus {
font-size: 1em;
color: @red;
}
}
}
div.login_info { div.login_info {
border-top: 1px solid @light-gray-color; border-top: 1px solid @light-gray-color;
font-size: 0.8em; font-size: 0.8em;
......
//if you like, change this (your brand color) //if you like, change this (your brand color)
@base-color: #28497c; // #28497c @base-color: #28497c; // #28497c
@contrast-blue: #2849d8;
//PLEASE, no changes from here //PLEASE, no changes from here
//@base-gray: #3c454e; // #3c454e //@base-gray: #3c454e; // #3c454e
......
...@@ -62,6 +62,19 @@ if ($bg_mobile) { ...@@ -62,6 +62,19 @@ if ($bg_mobile) {
<? endforeach; ?> <? endforeach; ?>
</div> </div>
<div id="contrast">
<? if (isset($_SESSION['contrast'])) : ?>
<?= Icon::create('accessibility')->asImg(24) ?>
<a href="index.php?unset_contrast=1"><?= _('Normalen Kontrast aktivieren') ?></a>
<?= tooltipIcon(_('Aktiviert standardmäßige, nicht barrierefreie Kontraste.')); ?>
<? else : ?>
<?= Icon::create('accessibility')->asImg(24) ?>
<a href="index.php?set_contrast=1" id="highcontrastlink"><?= _('Hohen Kontrast aktivieren')?></a>
<?= tooltipIcon(_('Aktiviert einen hohen Kontrast gemäß WCAG 2.1. Diese Einstellung wird nach dem Login übernommen.
Sie können sie in Ihren persönlichen Einstellungen ändern.')); ?>
<? endif ?>
</div>
<div class="login_info"> <div class="login_info">
<div> <div>
<?= _('Aktive Veranstaltungen') ?>: <?= _('Aktive Veranstaltungen') ?>:
......
...@@ -14,7 +14,8 @@ module.exports = { ...@@ -14,7 +14,8 @@ module.exports = {
"studip-wysiwyg": assetsPath + "/entry-wysiwyg.js", "studip-wysiwyg": assetsPath + "/entry-wysiwyg.js",
"studip-installer": assetsPath + "/entry-installer.js", "studip-installer": assetsPath + "/entry-installer.js",
"print": path.resolve(__dirname, "resources/assets/stylesheets") + "/print.less", "print": path.resolve(__dirname, "resources/assets/stylesheets") + "/print.less",
"webservices": path.resolve(__dirname, "resources/assets/stylesheets") + "/webservices.scss" "webservices": path.resolve(__dirname, "resources/assets/stylesheets") + "/webservices.scss",
"accessibility": path.resolve(__dirname, "resources/assets/stylesheets") + "/highcontrast.scss"
}, },
output: { output: {
path: path.resolve(__dirname, "public/assets"), path: path.resolve(__dirname, "public/assets"),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment