From b6bdba58a8e090cb144bae382457c2e439d8a72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michaela=20Br=C3=BCckner?= <brueckner@data-quest.de> Date: Tue, 2 Jan 2024 08:32:34 +0000 Subject: [PATCH] Step 2660 closes #2660 Closes #2660 Merge request studip/studip!2124 --- .../admin/{loginstyle.php => login_style.php} | 112 +++++++++--- app/views/admin/login_style/edit_faq.php | 30 ++++ app/views/admin/login_style/index.php | 78 ++++---- app/views/admin/login_style/login_faq.php | 57 ++++++ app/views/admin/login_style/newpic.php | 5 +- app/views/new_password/index.php | 2 +- .../5.5.16_add_tooltip_fields_for_login.php | 71 ++++++++ db/migrations/5.5.17_add_login_faq_table.php | 29 +++ db/migrations/5.5.18_add_login_faq_config.php | 53 ++++++ .../auth_plugins/CASUserDataMapping.php | 3 +- .../auth_plugins/StudipAuthAbstract.class.php | 40 +++++ .../auth_plugins/StudipAuthLdap.class.php | 1 + .../auth_plugins/StudipAuthStandard.class.php | 1 + lib/models/LoginFaq.class.php | 32 ++++ lib/navigation/AdminNavigation.php | 13 +- lib/navigation/LoginNavigation.php | 19 +- lib/navigation/StartNavigation.php | 2 +- lib/phplib/Seminar_Auth.class.php | 28 +++ lib/seminar_open.php | 7 + locale/en/LC_MESSAGES/studip.po | 2 +- public/index.php | 44 +---- .../javascripts/bootstrap/application.js | 65 +++++++ resources/assets/stylesheets/scss/index.scss | 85 ++++++++- .../assets/stylesheets/scss/responsive.scss | 7 +- .../assets/stylesheets/scss/variables.scss | 3 + templates/_standard_loginform.php | 76 ++++++++ templates/loginform.php | 167 ++++++++++++------ tests/e2e/login.spec.ts | 2 +- 28 files changed, 859 insertions(+), 175 deletions(-) rename app/controllers/admin/{loginstyle.php => login_style.php} (57%) create mode 100644 app/views/admin/login_style/edit_faq.php create mode 100644 app/views/admin/login_style/login_faq.php create mode 100644 db/migrations/5.5.16_add_tooltip_fields_for_login.php create mode 100644 db/migrations/5.5.17_add_login_faq_table.php create mode 100644 db/migrations/5.5.18_add_login_faq_config.php create mode 100644 lib/models/LoginFaq.class.php create mode 100644 templates/_standard_loginform.php diff --git a/app/controllers/admin/loginstyle.php b/app/controllers/admin/login_style.php similarity index 57% rename from app/controllers/admin/loginstyle.php rename to app/controllers/admin/login_style.php index f998b3a6a66..615e777f7a7 100644 --- a/app/controllers/admin/loginstyle.php +++ b/app/controllers/admin/login_style.php @@ -11,6 +11,7 @@ class Admin_LoginStyleController extends AuthenticatedController { + protected $_autobind = true; /** * common tasks for all actions * @@ -21,15 +22,24 @@ class Admin_LoginStyleController extends AuthenticatedController { parent::before_filter($action, $args); - // user must have root permission $GLOBALS['perm']->check('root'); //setting title and navigation PageLayout::setTitle(_('Hintergrundbilder für den Startbildschirm')); Navigation::activateItem('/admin/locations/loginstyle'); - // Setup sidebar - $this->setSidebar(); + $views = new ViewsWidget(); + $views->addLink( + _('Bilder'), + $this->indexURL() + )->setActive($action === 'index'); + + $views->addLink( + _('Hinweise zum Login'), + $this->login_faqURL() + )->setActive($action === 'login_faq'); + + Sidebar::Get()->addWidget($views); } /** @@ -37,6 +47,8 @@ class Admin_LoginStyleController extends AuthenticatedController */ public function index_action() { + // Setup sidebar + $this->setSidebar('index'); $this->pictures = LoginBackground::findBySQL("1 ORDER BY `background_id`"); } @@ -50,7 +62,7 @@ class Admin_LoginStyleController extends AuthenticatedController /** * Adds a new picture ass possible login background. */ - public function add_action() + public function add_pic_action() { CSRFProtection::verifyRequest(); $success = 0; @@ -96,15 +108,16 @@ class Admin_LoginStyleController extends AuthenticatedController $fail ), $fail)); } - $this->relocate('admin/loginstyle'); + $this->relocate($this->indexURL()); } /** * Deletes the given picture. - * @param $id the picture to delete + * @param string $id the picture to delete */ - public function delete_action($id) + public function delete_pic_action($id) { + CSRFProtection::verifyUnsafeRequest(); $pic = LoginBackground::find($id); if ($pic->in_release) { PageLayout::postError(_('Dieses Bild wird vom System mitgeliefert und kann daher nicht gelöscht werden.')); @@ -114,17 +127,22 @@ class Admin_LoginStyleController extends AuthenticatedController PageLayout::postError(_('Das Bild konnte nicht gelöscht werden.')); } - $this->relocate('admin/loginstyle'); + $this->relocate($this->indexURL()); } /** * (De-)activate the given picture for given view. - * @param $id the picture to change activation for - * @param $view one of 'desktop', 'mobile', view to (de-) activate picture for - * @param $newStatus new activation status for given view. + * @param string $id the picture to change activation for + * @param string $view one of 'desktop', 'mobile', view to (de-) activate picture for + * @param string $newStatus new activation status for given view. */ public function activation_action($id, $view, $newStatus) { + CSRFProtection::verifyUnsafeRequest(); + if (!in_array($view, ['desktop', 'mobile'])) { + throw new InvalidArgumentException('You may not change this attribute.'); + } + $pic = LoginBackground::find($id); $pic->$view = $newStatus; if ($pic->store()) { @@ -132,22 +150,74 @@ class Admin_LoginStyleController extends AuthenticatedController } else { PageLayout::postSuccess(_('Der Aktivierungsstatus konnte nicht gespeichert werden.')); } - $this->relocate('admin/loginstyle'); + $this->relocate($this->indexURL()); } + /** - * Adds the content to sidebar + * FAQ part of login page */ - protected function setSidebar() + public function login_faq_action() { - $sidebar = Sidebar::get(); + PageLayout::setTitle(_('Hinweise zum Login für den Startbildschirm')); + + $this->setSidebar('login_faq'); + $this->faq_entries = LoginFaq::findBySql('1'); + } + + public function edit_faq_action(LoginFaq $entry = null) + { + PageLayout::setTitle( + $entry->isNew() ? _('Hinweistext hinzufügen') : _('Hinweistext bearbeiten') + ); + } + + public function store_faq_action(LoginFaq $entry = null) + { + CSRFProtection::verifyRequest(); + + $entry->setData([ + 'title' => trim(Request::get('title')), + 'description' => trim(Request::get('description')), + ]); + if ($entry->store()) { + PageLayout::postSuccess(_('Hinweistext wurde gespeichert.')); + } + + $this->relocate($this->login_faqURL()); + } + + public function delete_faq_action(LoginFaq $entry) + { + CSRFProtection::verifyRequest(); + + if ($entry->delete()) { + PageLayout::postSuccess(_('Der Hinweistext wurde gelöscht.')); + } + + $this->relocate($this->login_faqURL()); + } + + /** + * Adds the content to sidebar + */ + protected function setSidebar($action) + { $links = new ActionsWidget(); - $links->addLink( - _('Bild hinzufügen'), - $this->url_for('admin/loginstyle/newpic'), - Icon::create('add', 'clickable') - )->asDialog('size=auto'); - $sidebar->addWidget($links); + if ($action === 'index') { + $links->addLink( + _('Bild hinzufügen'), + $this->newpicURL(), + Icon::create('add') + )->asDialog('size=auto'); + } else if ($action === 'login_faq') { + $links->addLink( + _('Hinweistext hinzufügen'), + $this->edit_faqURL(), + Icon::create('add') + )->asDialog(); + } + Sidebar::get()->addWidget($links); } } diff --git a/app/views/admin/login_style/edit_faq.php b/app/views/admin/login_style/edit_faq.php new file mode 100644 index 00000000000..66020a02274 --- /dev/null +++ b/app/views/admin/login_style/edit_faq.php @@ -0,0 +1,30 @@ +<?php +/** + * @var Admin_LoginStyleController $controller + * @var LoginFaq $entry + */ +?> +<form action="<?= $controller->store_faq($entry) ?>" + method="post" + enctype="multipart/form-data" + class="default"> + <?= CSRFProtection::tokenTag() ?> + + <label class="studiprequired"> + <?= _('Titel') ?> + <span title="<?= _('Dies ist ein Pflichtfeld') ?>" aria-hidden="true" class="asterisk">*</span> + <input type="text" name="title" value="<?= htmlReady($entry->title) ?>" required> + </label> + + <label class="studiprequired"> + <?= _('Text') ?> + <span title="<?= _('Dies ist ein Pflichtfeld') ?>" aria-hidden="true" class="asterisk">*</span> + <textarea name="description" + class="add_toolbar wysiwyg" data-editor="toolbar=minimal"><?= htmlReady($entry->description)?></textarea> + </label> + + <div data-dialog-button> + <?= \Studip\Button::create(_('Speichern')) ?> + </div> + +</form> diff --git a/app/views/admin/login_style/index.php b/app/views/admin/login_style/index.php index 195cc6a9fc7..24c0b703f08 100644 --- a/app/views/admin/login_style/index.php +++ b/app/views/admin/login_style/index.php @@ -4,28 +4,31 @@ * @var Admin_LoginStyleController $controller */ ?> -<? if (count($pictures) > 0) : ?> +<form method="post"> + <?= CSRFProtection::tokenTag(); ?> + + <? if (count($pictures) > 0) : ?> <table class="default"> <caption> <?= _('Hintergrundbilder für den Startbildschirm') ?> </caption> <colgroup> <col> - <col width="400"> - <col width="100"> - <col width="25"> + <col style="width: 400px"> + <col style="width: 100px"> + <col style="width: 25px"> </colgroup> <thead> - <tr> - <th><?= _('Info') ?></th> - <th><?= _('Vorschau') ?></th> - <th><?= _('Aktiviert für') ?></th> - <th><?= _('Aktionen') ?></th> - </tr> + <tr> + <th><?= _('Info') ?></th> + <th><?= _('Vorschau') ?></th> + <th><?= _('Aktiviert für') ?></th> + <th><?= _('Aktionen') ?></th> + </tr> </thead> <? foreach ($pictures as $pic) : $dim = $pic->getDimensions(); - ?> + ?> <tr> <td> <?= htmlReady($pic->filename) ?> @@ -37,34 +40,41 @@ <img src="<?= $pic->getURL() ?>" width="400"> </td> <td> - <a href="<?= $controller->link_for("admin/loginstyle/activation/{$pic->id}/desktop", (int) !$pic->desktop) ?>"> - <?= Icon::create('computer', $pic->desktop ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(32, [ - 'title' => $pic->desktop - ? _('Bild nicht mehr für die Desktopansicht verwenden') - : _('Bild für die Desktopansicht verwenden') - ]) ?> - </a> - <a href="<?= $controller->link_for("admin/loginstyle/activation/{$pic->id}/mobile", (int) !$pic->mobile) ?>"> - <?= Icon::create('cellphone', $pic->mobile ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(32, [ + <?= Icon::create('computer', $pic->desktop ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asInput( + 32, + [ + 'title' => $pic->mobile + ? _('Bild nicht mehr für die Mobilansicht verwenden') + : _('Bild für die Mobilansicht verwenden'), + 'formaction' => $controller->activationURL($pic->id, 'desktop', (int) !$pic->desktop) + ] + )?> + + <?= Icon::create('cellphone', $pic->mobile ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asInput( + 32, + [ 'title' => $pic->mobile - ? _('Bild nicht mehr für die Mobilansicht verwenden') - : _('Bild für die Mobilansicht verwenden') - ]) ?> - </a> + ? _('Bild nicht mehr für die Mobilansicht verwenden') + : _('Bild für die Mobilansicht verwenden'), + 'formaction' => $controller->activationURL($pic->id, 'mobile', (int) !$pic->mobile) + ] + )?> </td> <td class="actions"> - <? if (!$pic->in_release): ?> - <a href="<?= $controller->link_for("admin/loginstyle/delete/{$pic->id}") ?>"> - <?= Icon::create('trash')->asImg([ - 'title' => _('Bild löschen'), - 'data-confirm' => _('Soll das Bild wirklich gelöscht werden?'), - ]) ?> - </a> - <? endif; ?> + <? if (!$pic->in_release): ?> + <?= Icon::create('trash')->asInput( + [ + 'title' => _('Bild löschen'), + 'data-confirm' => _('Soll das Bild wirklich gelöscht werden?'), + 'formaction' => $controller->delete_picURL($pic->id) + ] + )?> + <? endif ?> </td> </tr> <? endforeach ?> </table> -<? else : ?> + <? else : ?> <?= MessageBox::info(_('In Ihrem System sind leider keine Bilder für den Startbildschirm hinterlegt.')) ?> -<? endif ?> + <? endif ?> +</form> diff --git a/app/views/admin/login_style/login_faq.php b/app/views/admin/login_style/login_faq.php new file mode 100644 index 00000000000..4ed19e19adc --- /dev/null +++ b/app/views/admin/login_style/login_faq.php @@ -0,0 +1,57 @@ +<?php +/** + * @var Admin_LoginStyleController $controller + * @var LoginFaq[] $faq_entries + */ +?> +<form method="post"> + <?= CSRFProtection::tokenTag() ?> + <table class="default"> + <caption><?= _('Hinweise zum Login') ?></caption> + <thead> + <tr> + <th><?= _('Titel') ?></th> + <th class="actions"><?= _('Aktionen') ?></th> + </tr> + </thead> + <tbody> + <? if (count($faq_entries) > 0) : ?> + <? foreach ($faq_entries as $entry) : ?> + <tr> + <td><?= htmlReady($entry->title) ?></td> + <td class="actions"> + <?= ActionMenu::get() + ->setContext($entry->title) + ->addLink( + $controller->edit_faqURL($entry), + _('Hinweistext bearbeiten'), + Icon::create('edit'), + ['data-dialog' => 'size=medium'] + )->addButton( + 'delete', + _('Hinweistext löschen'), + Icon::create('trash'), + [ + 'formaction' => $controller->delete_faqURL($entry), + 'data-confirm' => sprintf( + _('Wollen Sie den Hinweistext "%s" wirklich löschen?'), + $entry->title + ), + 'data-dialog' => 'size=auto', + ] + ) + ?> + </td> + </tr> + <? endforeach ?> + <? else : ?> + <tr> + <td colspan="3" style="text-align: center"> + <?= _('Keine Hinweistexte vorhanden') ?> + </td> + </tr> + <? endif ?> + </tbody> + + </table> +</form> diff --git a/app/views/admin/login_style/newpic.php b/app/views/admin/login_style/newpic.php index d5bc74a677c..c5f7fbf0c4e 100644 --- a/app/views/admin/login_style/newpic.php +++ b/app/views/admin/login_style/newpic.php @@ -3,7 +3,8 @@ * @var Admin_LoginStyleController $controller */ ?> -<form class="default" action="<?= $controller->link_for('admin/loginstyle/add') ?>" method="post" enctype="multipart/form-data"> +<form class="default" action="<?= $controller->add_pic() ?>" method="post" enctype="multipart/form-data"> + <?= CSRFProtection::tokenTag() ?> <fieldset> <legend> <?= _('Bild(er) hinzufügen') ?> @@ -33,6 +34,6 @@ <footer data-dialog-button> <?= CSRFProtection::tokenTag() ?> <?= Studip\Button::createAccept(_('Speichern'), 'store') ?> - <?= Studip\LinkButton::createCancel(_('Abbrechen'), $controller->url_for('loginstyle/index')) ?> + <?= Studip\LinkButton::createCancel(_('Abbrechen'), $controller->indexURL()) ?> </footer> </form> diff --git a/app/views/new_password/index.php b/app/views/new_password/index.php index 9cddc31719e..d0446291099 100644 --- a/app/views/new_password/index.php +++ b/app/views/new_password/index.php @@ -6,7 +6,7 @@ </legend> <label> - <?= _('Geben sie die Mail-Adresse des Zugangs an, für den sie das Passwort zurücksetzen möchten') ?> + <?= _('Geben Sie die Mail-Adresse des Zugangs an, für den Sie das Passwort zurücksetzen möchten') ?> <input type="text" name="mail" placeholder="<?= _('Ihre Mail-Adresse') ?>" required> </label> </fieldset> diff --git a/db/migrations/5.5.16_add_tooltip_fields_for_login.php b/db/migrations/5.5.16_add_tooltip_fields_for_login.php new file mode 100644 index 00000000000..f4d536b41da --- /dev/null +++ b/db/migrations/5.5.16_add_tooltip_fields_for_login.php @@ -0,0 +1,71 @@ +<?php + + +class AddTooltipFieldsForLogin extends Migration +{ + public function description() + { + return 'Creates config for login username and password tooltip texts'; + } + + public function up() + { + $query = 'INSERT INTO `config` (`field`, `value`, `type`, `section`, `range`, `description`, `mkdate`, `chdate`) + VALUES (:name, :value, :type, :section, :range, :description, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())'; + $statement = DBManager::get()->prepare($query); + $statement->execute([ + 'name' => 'USERNAME_TOOLTIP_TEXT', + 'value' => 'Geben Sie hier Ihren Stud.IP-Benutzernamen ein.', + 'type' => 'i18n', + 'section' => 'Loginseite', + 'range' => 'global', + 'description' => 'Text für den Tooltip des Benutzernamens auf der Loginseite' + ]); + + $statement->execute([ + 'name' => 'PASSWORD_TOOLTIP_TEXT', + 'value' => 'Geben Sie hier Ihr Stud.IP-Passwort ein. Achten Sie bei der Eingabe auf Groß- und Kleinschreibung.', + 'type' => 'i18n', + 'section' => 'Loginseite', + 'range' => 'global', + 'description' => 'Text für den Tooltip des Benutzernamens auf der Loginseite' + ]); + + $statement->execute([ + 'name' => 'USERNAME_TOOLTIP_ACTIVATED', + 'value' => '1', + 'type' => 'boolean', + 'section' => 'Loginseite', + 'range' => 'global', + 'description' => 'Soll der Tooltip beim Benutzernamen auf der Loginseite sichtbar sein?' + ]); + + $statement->execute([ + 'name' => 'PASSWORD_TOOLTIP_ACTIVATED', + 'value' => '1', + 'type' => 'boolean', + 'section' => 'Loginseite', + 'range' => 'global', + 'description' => 'Soll der Tooltip beim Passwort auf der Loginseite sichtbar sein?' + ]); + + } + + public function down() + { + $query = "DELETE `config`, `config_values`, `i18n` + FROM `config` + LEFT JOIN `config_values` USING (`field`) + LEFT JOIN `i18n` + ON `table` = 'config' + AND `field` = 'value' + AND `object_id` = MD5(`config`.`field`) + WHERE `field` IN ( + 'USERNAME_TOOLTIP_TEXT', + 'PASSWORD_TOOLTIP_TEXT', + 'USERNAME_TOOLTIP_ACTIVATED', + 'PASSWORD_TOOLTIP_ACTIVATED' + )"; + DBManager::get()->exec($query); + } +} diff --git a/db/migrations/5.5.17_add_login_faq_table.php b/db/migrations/5.5.17_add_login_faq_table.php new file mode 100644 index 00000000000..2a999099ddf --- /dev/null +++ b/db/migrations/5.5.17_add_login_faq_table.php @@ -0,0 +1,29 @@ +<?php + +class AddLoginFaqTable extends Migration +{ + public function description() + { + return 'Create table for login page FAQ'; + } + + public function up() + { + $query = "CREATE TABLE IF NOT EXISTS `login_faq` ( + `faq_id` int(11) NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, + `description` text NOT NULL, + PRIMARY KEY (`faq_id`) + )"; + DBManager::get()->exec($query); + } + + public function down() + { + DBManager::get()->exec('DROP TABLE IF EXISTS `login_faq`'); + + $query = "DELETE FROM `i18n` + WHERE `table` = 'login_faq`"; + DBManager::get()->exec($query); + } +} diff --git a/db/migrations/5.5.18_add_login_faq_config.php b/db/migrations/5.5.18_add_login_faq_config.php new file mode 100644 index 00000000000..3196e34e6a4 --- /dev/null +++ b/db/migrations/5.5.18_add_login_faq_config.php @@ -0,0 +1,53 @@ +<?php + + +class AddLoginFaqConfig extends Migration +{ + public function description() + { + return 'Creates configs for login faq: Visibility and title (eg.: Hilfe zum Login)'; + } + + public function up() + { + $query = 'INSERT INTO `config` (`field`, `value`, `type`, `section`, `range`, `description`, `mkdate`, `chdate`) + VALUES (:name, :value, :type, :section, :range, :description, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())'; + $statement = DBManager::get()->prepare($query); + $statement->execute([ + 'name' => 'LOGIN_FAQ_TITLE', + 'value' => 'Hinweise zum Login', + 'type' => 'i18n', + 'section' => 'Loginseite', + 'range' => 'global', + 'description' => 'Überschrift für den FAQ-Bereich auf der Loginseite' + ]); + + $statement->execute([ + 'name' => 'LOGIN_FAQ_VISIBILITY', + 'value' => '1', + 'type' => 'boolean', + 'section' => 'Loginseite', + 'range' => 'global', + 'description' => 'Soll der FAQ-Bereich auf der Loginseite sichtbar sein?' + ]); + + } + + public function down() + { + $query = "DELETE `config`, `config_values`, `i18n` + FROM `config` + LEFT JOIN `config_values` USING (`field`) + LEFT JOIN `i18n` + ON `table` = 'config' + AND `field` = 'value' + AND `object_id` = MD5(`config`.`field`) + WHERE `field` IN ( + 'LOGIN_FAQ_TITLE', + 'LOGIN_FAQ_VISIBILITY', + 'USERNAME_TOOLTIP_ACTIVATED', + 'PASSWORD_TOOLTIP_ACTIVATED' + )"; + DBManager::get()->exec($query); + } +} diff --git a/lib/classes/auth_plugins/CASUserDataMapping.php b/lib/classes/auth_plugins/CASUserDataMapping.php index 03c9cd2beba..166df55fbf6 100644 --- a/lib/classes/auth_plugins/CASUserDataMapping.php +++ b/lib/classes/auth_plugins/CASUserDataMapping.php @@ -1,4 +1,4 @@ -<? +<?php # Lifter007: TODO # Lifter003: TODO # Lifter010: TODO @@ -10,4 +10,3 @@ interface CASUserDataMapping { // reads one attribute identified by a key of a given user function getUserData ($key, $username); } -?> \ No newline at end of file diff --git a/lib/classes/auth_plugins/StudipAuthAbstract.class.php b/lib/classes/auth_plugins/StudipAuthAbstract.class.php index d1bcfac6080..fd2d4e9aaff 100644 --- a/lib/classes/auth_plugins/StudipAuthAbstract.class.php +++ b/lib/classes/auth_plugins/StudipAuthAbstract.class.php @@ -90,6 +90,14 @@ class StudipAuthAbstract */ public $error_head; + /** + * toggles display of standard login + * + * + * @var bool $show_login + */ + public $show_login; + /** * @var $plugin_instances */ @@ -120,6 +128,38 @@ class StudipAuthAbstract return ($plugin_name) ? self::$plugin_instances[strtoupper($plugin_name)] : self::$plugin_instances; } + /** + * static method to check if SSO login is enabled + * + * @return bool + */ + public static function isSSOEnabled(): bool + { + self::getInstance(); + foreach (self::$plugin_instances as $auth_plugin) { + if ($auth_plugin instanceof StudipAuthSSO) { + return true; + } + } + return false; + } + + /** + * static method to check if standard login is enabled + * + * @return bool + */ + public static function isLoginEnabled(): bool + { + self::getInstance(); + foreach (self::$plugin_instances as $auth_plugin) { + if ($auth_plugin->show_login === true) { + return true; + } + } + return false; + } + /** * static method to check authentication in all plugins * diff --git a/lib/classes/auth_plugins/StudipAuthLdap.class.php b/lib/classes/auth_plugins/StudipAuthLdap.class.php index 5721cf44e87..7cb86860028 100644 --- a/lib/classes/auth_plugins/StudipAuthLdap.class.php +++ b/lib/classes/auth_plugins/StudipAuthLdap.class.php @@ -40,6 +40,7 @@ class StudipAuthLdap extends StudipAuthAbstract public $username_attribute = 'uid'; public $ldap_filter; public $bad_char_regex = '/[^0-9_a-zA-Z]/'; + public $show_login = true; public $conn = null; public $user_data = null; diff --git a/lib/classes/auth_plugins/StudipAuthStandard.class.php b/lib/classes/auth_plugins/StudipAuthStandard.class.php index 1a2c2bae556..5bb3e65f0db 100644 --- a/lib/classes/auth_plugins/StudipAuthStandard.class.php +++ b/lib/classes/auth_plugins/StudipAuthStandard.class.php @@ -37,6 +37,7 @@ class StudipAuthStandard extends StudipAuthAbstract { var $bad_char_regex = false; + public $show_login = true; /** * diff --git a/lib/models/LoginFaq.class.php b/lib/models/LoginFaq.class.php new file mode 100644 index 00000000000..036f0f73178 --- /dev/null +++ b/lib/models/LoginFaq.class.php @@ -0,0 +1,32 @@ +<?php +/** + * LoginFaq.class.php + * model class for table login_faq + * + * + * + * 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> + * @copyright 2023 data-quest + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @category Stud.IP + * @since 5.5 +*/ +class LoginFaq extends SimpleORMap +{ + /** + * @param array $config + */ + protected static function configure($config = []) + { + $config['db_table'] = 'login_faq'; + + $config['i18n'] = ['title', 'description']; + + parent::configure($config); + } +} diff --git a/lib/navigation/AdminNavigation.php b/lib/navigation/AdminNavigation.php index 12bf53adfe4..7d9cd2d391d 100644 --- a/lib/navigation/AdminNavigation.php +++ b/lib/navigation/AdminNavigation.php @@ -110,7 +110,6 @@ class AdminNavigation extends Navigation } $navigation->addSubNavigation('sem_classes', new Navigation(_('Veranstaltungskategorien'), 'dispatch.php/admin/sem_classes/overview')); - $navigation->addSubNavigation('loginstyle', new Navigation(_('Startbildschirm'), 'dispatch.php/admin/loginstyle')); $navigation->addSubNavigation( 'content_terms_of_use', new Navigation( @@ -157,7 +156,6 @@ class AdminNavigation extends Navigation 'dispatch.php/admin/accessibility_info_text/index' ) ); - } if ($GLOBALS['perm']->have_perm('admin')) { @@ -165,6 +163,17 @@ class AdminNavigation extends Navigation $navigation->addSubNavigation('stock_images', $pool); } + if ($perm->have_perm('root')) { + $navigation->addSubNavigation( + 'loginstyle', + new Navigation( + _('Startseite'), + 'dispatch.php/admin/login_style' + ) + ); + + } + $this->addSubNavigation('locations', $navigation); // global config / user administration diff --git a/lib/navigation/LoginNavigation.php b/lib/navigation/LoginNavigation.php index db212364ccc..96ec90ed897 100644 --- a/lib/navigation/LoginNavigation.php +++ b/lib/navigation/LoginNavigation.php @@ -23,13 +23,20 @@ class LoginNavigation extends Navigation { parent::initSubNavigation(); - $navigation = new Navigation(_('Login'), 'index.php?again=yes'); - $navigation->setDescription(_('für registrierte NutzerInnen')); - $this->addSubNavigation('login', $navigation); - + $standard_login_active = false; foreach (StudipAuthAbstract::getInstance() as $auth_plugin) { + if ($auth_plugin->show_login && !$standard_login_active) { + $navigation = new Navigation(_('Login'), ''); + $navigation->setDescription($auth_plugin->login_description ?: _('für registrierte Nutzende')); + $navigation->setLinkAttributes([ + 'id' => 'toggle-login' + ]); + $navigation->setURL('#login-form'); + $this->addSubNavigation('standard_login', $navigation); + $standard_login_active = true; + } if ($auth_plugin instanceof StudipAuthSSO && isset($auth_plugin->login_description)) { - $navigation = new Navigation($auth_plugin->plugin_fullname . ' ' . _('Login'), 'index.php?again=yes&sso=' . $auth_plugin->plugin_name); + $navigation = new Navigation($auth_plugin->plugin_fullname . ' ' . _('Login'), '?sso=' . $auth_plugin->plugin_name); $navigation->setDescription($auth_plugin->login_description); $this->addSubNavigation('login_' . $auth_plugin->plugin_name, $navigation); } @@ -37,7 +44,7 @@ class LoginNavigation extends Navigation if (Config::get()->ENABLE_SELF_REGISTRATION) { $navigation = new Navigation(_('Registrieren'), 'register1.php'); - $navigation->setDescription(_('um NutzerIn zu werden')); + $navigation->setDescription(_('um das System erstmalig zu nutzen')); $this->addSubNavigation('register', $navigation); } diff --git a/lib/navigation/StartNavigation.php b/lib/navigation/StartNavigation.php index 3d3719e818b..df94412420e 100644 --- a/lib/navigation/StartNavigation.php +++ b/lib/navigation/StartNavigation.php @@ -22,7 +22,7 @@ class StartNavigation extends Navigation { $url = (is_object($GLOBALS['user']) && $GLOBALS['user']->id != 'nobody') ? 'dispatch.php/start' - : 'index.php'; + : 'index.php?again=yes'; parent::__construct(_('Start'), $url); } diff --git a/lib/phplib/Seminar_Auth.class.php b/lib/phplib/Seminar_Auth.class.php index 30a6d467719..80f6ceaf019 100644 --- a/lib/phplib/Seminar_Auth.class.php +++ b/lib/phplib/Seminar_Auth.class.php @@ -301,6 +301,27 @@ class Seminar_Auth throw new AccessDeniedException(); } + // if desired, switch to high contrast stylesheet and store when user logs in + if (Request::get('unset_contrast')) { + unset($_SESSION['contrast']); + PageLayout::removeStylesheet('accessibility.css'); + + } + if (Request::get('set_contrast') ) { + $_SESSION['contrast'] = true; + PageLayout::addStylesheet('accessibility.css'); + + } + + // evaluate language clicks + // 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 (array_key_exists(Request::get('set_language'), $GLOBALS['INSTALLED_LANGUAGES'])) { + $_SESSION['forced_language'] = Request::get('set_language'); + $_SESSION['_language'] = Request::get('set_language'); + } + } + $this->check_environment(); PageLayout::setBodyElementId('login'); @@ -322,6 +343,13 @@ class Seminar_Auth $login_template->set_attribute('error_msg', $this->error_msg); $login_template->set_attribute('uname', (isset($this->auth["uname"]) ? $this->auth["uname"] : Request::username('loginname'))); $login_template->set_attribute('self_registration_activated', Config::get()->ENABLE_SELF_REGISTRATION); + + $query = "SHOW TABLES LIKE 'login_faq'"; + $result = DBManager::get()->query($query); + + if ($result && $result->rowCount() > 0) { + $login_template->set_attribute('faq_entries', LoginFaq::findBySQL("1")); + } } PageLayout::setHelpKeyword('Basis.AnmeldungLogin'); $header_template = $GLOBALS['template_factory']->open('header'); diff --git a/lib/seminar_open.php b/lib/seminar_open.php index c3e15c35fbc..0ca99918017 100644 --- a/lib/seminar_open.php +++ b/lib/seminar_open.php @@ -105,6 +105,13 @@ if ($auth->is_authenticated() && is_object($user) && $user->id != "nobody") { UserConfig::get($GLOBALS['user']->id)->store('USER_HIGH_CONTRAST', $_SESSION['contrast']); unset($_SESSION['contrast']); } + // store last language click + if (!empty($_SESSION['forced_language'])) { + User::findCurrent()->preferred_language = $_SESSION['forced_language']; + User::findCurrent()->store(); + $_SESSION['_language'] = $_SESSION['forced_language']; + } + $_SESSION['forced_language'] = null; $user_did_login = true; } diff --git a/locale/en/LC_MESSAGES/studip.po b/locale/en/LC_MESSAGES/studip.po index 0ff799a8531..29266c91991 100644 --- a/locale/en/LC_MESSAGES/studip.po +++ b/locale/en/LC_MESSAGES/studip.po @@ -41881,7 +41881,7 @@ msgid "Sie haben keine alten empfangenen Nachrichten" msgstr "You do not have any old received messages" #: lib/navigation/LoginNavigation.php:27 -msgid "für registrierte NutzerInnen" +msgid "für registrierte Nutzende" msgstr "for registered users" #: lib/navigation/LoginNavigation.php:40 diff --git a/public/index.php b/public/index.php index c32fe49d4c1..2a383556db8 100644 --- a/public/index.php +++ b/public/index.php @@ -59,51 +59,9 @@ if ($auth->is_authenticated() && $user->id != 'nobody') { closeObject(); include 'lib/seminar_open.php'; // initialise Stud.IP-Session +$auth->login_if($user->id === 'nobody'); // if new start page is in use, redirect there (if logged in) if ($auth->is_authenticated() && $user->id != 'nobody') { header('Location: ' . URLHelper::getURL('dispatch.php/start')); - die; } - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * L O G I N - P A G E ( N O B O D Y - U S E R ) * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -PageLayout::setHelpKeyword("Basis.Startseite"); // set keyword for new help -PageLayout::setTitle(_("Startseite")); -Navigation::activateItem('/start'); -PageLayout::setTabNavigation(NULL); // disable display of tabs - -// Start of Output -include 'lib/include/html_head.inc.php'; // Output of html head -include 'lib/include/header.php'; - -// Prüfen, ob PortalPlugins vorhanden sind. -// TODO: Remove for Stud.IP 6.0 -/** @deprecated */ -$portalplugins = PluginEngine::getPlugins('PortalPlugin'); -$layout = $GLOBALS['template_factory']->open('shared/index_box'); - -$plugin_contents = []; -foreach ($portalplugins as $portalplugin) { - $template = $portalplugin->getPortalTemplate(); - - if ($template) { - $plugin_contents[] = $template->render(NULL, $layout); - $layout->clear_attributes(); - } -} - - -$index_nobody_template = $GLOBALS['template_factory']->open('index_nobody'); -$index_nobody_template->set_attributes([ - 'plugin_contents' => $plugin_contents, - 'logout' => Request::bool('logout'), -]); - -echo $index_nobody_template->render(); - -page_close(); - -include 'lib/include/html_end.inc.php'; diff --git a/resources/assets/javascripts/bootstrap/application.js b/resources/assets/javascripts/bootstrap/application.js index 1629b9baec4..830f2c025f2 100644 --- a/resources/assets/javascripts/bootstrap/application.js +++ b/resources/assets/javascripts/bootstrap/application.js @@ -355,3 +355,68 @@ jQuery(document).on('click', 'a[data-behaviour~="ajax-toggle"]', function (event $('#open_variable').attr('value', $(this).parent('fieldset').data('open')); }); }(jQuery)); + +STUDIP.domReady(function () { + const loginForm = document.getElementById('login-form'); + if (!loginForm) { + return; + } + + const passwordInput = document.getElementById('password'); + const usernameInput = document.getElementById('loginname'); + const passwordCapsText = document.getElementById('password-caps'); + const iconPasswordVisible = document.getElementById('visible-password'); + const iconPasswordInVisible = document.getElementById('invisible-password'); + + [usernameInput, passwordInput].forEach((input) => { + input.addEventListener('keydown', (event) => { + if (event.getModifierState('CapsLock')) { + passwordCapsText.style.display = 'block'; + } else { + passwordCapsText.style.display = 'none'; + } + }); + }); + + const toggleLogin = document.getElementById('toggle-login'); + if (toggleLogin) { + loginForm.addEventListener('transitionend', (event) => { + if (event.propertyName !== 'max-height') { + return; + } + + if (!loginForm.classList.contains('hide')) { + usernameInput.scrollIntoView({ + behavior: 'smooth' + }); + usernameInput.focus(); + } else { + loginForm.setAttribute('hidden', ''); + } + }); + + toggleLogin.addEventListener('click', (event) => { + if (loginForm.classList.contains('hide')) { + loginForm.removeAttribute('hidden'); + } + + setTimeout(() => { + loginForm.classList.toggle('hide'); + }, 0); + + event.preventDefault(); + }); + } + + document.getElementById('password-toggle').addEventListener('click', () => { + if (passwordInput.type === 'password') { + passwordInput.type = 'text'; + iconPasswordVisible.style.display = 'none'; + iconPasswordInVisible.style.display = ''; + } else { + passwordInput.type = 'password'; + iconPasswordVisible.style.display = ''; + iconPasswordInVisible.style.display = 'none'; + } + }); +}); diff --git a/resources/assets/stylesheets/scss/index.scss b/resources/assets/stylesheets/scss/index.scss index 2ecda5c36d4..e9234de30b0 100644 --- a/resources/assets/stylesheets/scss/index.scss +++ b/resources/assets/stylesheets/scss/index.scss @@ -13,6 +13,16 @@ $gap-between-boxes: calc($login-page-margin / 2); #content { grid-column: 1 / 3; grid-row: 2 / 2; + + &.loginpage { + display: flex; + flex-direction: row; + flex-wrap: wrap; + column-gap: 20px; + row-gap: 20px; + align-items: flex-start; + flex-basis: 450px; + } } #background-desktop { @@ -36,14 +46,24 @@ $gap-between-boxes: calc($login-page-margin / 2); } } +#login_flex { + display: flex; + flex-direction: row; + column-gap: 20px; + flex-wrap: wrap; + row-gap: 20px; + align-items: flex-start; +} + #loginbox { - background-color: rgba(255, 255, 255, 0.8); - box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.5); + background-color: var(--white); + box-shadow: 0 0 8px rgba(0, 0, 0, 0.5); padding: 20px; width: 450px; + float: left; header { - margin: 10px 0 0 10px; + margin: 0 0 0 0; h1 { border-bottom: 0; @@ -56,7 +76,7 @@ $gap-between-boxes: calc($login-page-margin / 2); list-style-type: none; margin: 0; width: 450px; - padding: 0 10px; + padding-inline-start: 0; .login_link { display: inline-block; @@ -93,6 +113,10 @@ $gap-between-boxes: calc($login-page-margin / 2); } } + #contrast { + padding-bottom: 0; + } + div.login_info { border-top: 1px solid var(--light-gray-color); font-size: 0.8em; @@ -110,6 +134,38 @@ $gap-between-boxes: calc($login-page-margin / 2); margin-left: 12px; } } + + + input#loginname, + input#password { + display: initial; + } + + input#password { + padding-right: 28px; + } + + #password-toggle { + position: absolute; + right: 7px; + bottom: 0; + cursor: pointer; + + #visible-password, + #invisible-password { + } + } +} + +#faq_box { + background-color: var(--white); + box-shadow: 0 0 8px rgba(0, 0, 0, 0.5); + padding: 20px; + width: 450px; + float: left; + > header { + margin: 0 0 0 0; + } } #login-plugin-contents { @@ -128,3 +184,24 @@ $gap-between-boxes: calc($login-page-margin / 2); width: 418px; } } + +::-ms-reveal { + display: none; +} + + +#login-form { + max-height: 300px; + overflow: hidden; + transition: max-height var(--transition-duration-slow) linear; + + &.hide { + max-height: 0px; + } + + #submit_login { + margin-top: 0 !important; + float: left !important; + + } +} diff --git a/resources/assets/stylesheets/scss/responsive.scss b/resources/assets/stylesheets/scss/responsive.scss index a1f2a675079..01c74030178 100644 --- a/resources/assets/stylesheets/scss/responsive.scss +++ b/resources/assets/stylesheets/scss/responsive.scss @@ -897,7 +897,8 @@ html:not(.responsive-display):not(.fullscreen-mode) { left: 0; } - #loginbox { + #loginbox, + #faq_box { box-shadow: unset; margin: 0; width: calc(100vw - 40px); @@ -912,6 +913,10 @@ html:not(.responsive-display):not(.fullscreen-mode) { } } } + + #faq_box { + margin: -20px 0 0 0; + } } } diff --git a/resources/assets/stylesheets/scss/variables.scss b/resources/assets/stylesheets/scss/variables.scss index ef5329eb9b2..a915e766f6a 100644 --- a/resources/assets/stylesheets/scss/variables.scss +++ b/resources/assets/stylesheets/scss/variables.scss @@ -43,6 +43,7 @@ $drag_and_drop_z_index: 1000; $drag_and_drop_border: 1px solid $base-color; $transition-duration: .3s; +$transition-duration-slow: .5s; // Layout $page-margin: 15px; @@ -171,8 +172,10 @@ $grid-gap: 0; #{"--"}group-color-8: $brown; #{"--"}transition-duration: $transition-duration; + #{"--"}transition-duration-slow: $transition-duration-slow; @media (prefers-reduced-motion) { #{"--"}transition-duration: 0s; + #{"--"}transition-duration-slow: 0s; } } diff --git a/templates/_standard_loginform.php b/templates/_standard_loginform.php new file mode 100644 index 00000000000..0a8be593313 --- /dev/null +++ b/templates/_standard_loginform.php @@ -0,0 +1,76 @@ +<?php + +use Studip\Button; + +/** + * @var bool $hidden + * @var string $uname; + */ +?> + +<form class="default <?= $hidden ? 'hide' : '' ?>" + name="login_form" + id="login-form" + method="post" + action="<?= URLHelper::getLink(Request::url(), ['cancel_login' => null]) ?>" + <? if ($hidden) echo 'hidden'; ?> +> + <section> + <label> + <span class="required"><?= _('Benutzername') ?></span> + <? if (Config::get()->USERNAME_TOOLTIP_ACTIVATED) : ?> + <?= tooltipIcon(htmlReady((string)Config::get()->USERNAME_TOOLTIP_TEXT)) ?> + <? endif ?> + <input type="text" <?= (mb_strlen($uname) || $hidden) ? '' : 'autofocus' ?> + id="loginname" + name="loginname" + value="<?= htmlReady($uname) ?>" + size="20" + spellcheck="false" + autocapitalize="off" + title="<?= _('Der Benutzername entspricht nicht den Anforderungen') ?>" + required> + </label> + <label for="password" style="position: relative"> + <span class="required"><?= _('Passwort') ?></span> + <? if (Config::get()->PASSWORD_TOOLTIP_ACTIVATED) : ?> + <?= tooltipIcon(htmlReady((string)Config::get()->PASSWORD_TOOLTIP_TEXT)) ?> + <? endif ?> + <input type="password" <?= mb_strlen($uname) && !$hidden ? 'autofocus' : '' ?> + id="password" + name="password" + size="20" + required> + + <i id="password-toggle" tabindex="0" aria-role="button" class="enter-accessible"> + <?= Icon::create('visibility-checked')->asImg(20, [ + 'id ' => 'visible-password', + 'title' => _('Passwort anzeigen'), + ]) ?> + <?= Icon::create('visibility-invisible')->asImg(20, [ + 'id' => 'invisible-password', + 'style' => 'display: none', + 'title' => _('Passwort verstecken'), + ]) ?> + </i> + + </label> + <p id="password-caps" style="display: none"><?= _('Feststelltaste ist aktiviert!') ?></p> + </section> + + <?= CSRFProtection::tokenTag() ?> + <input type="hidden" name="login_ticket" value="<?= Seminar_Session::get_ticket() ?>"> + <input type="hidden" name="resolution" value=""> + + <div style="display: flex; align-items: flex-start; justify-content: space-between; height: 70px;"> + <?= Button::createAccept(_('Anmelden'), _('Login'), ['id' => 'submit_login']); ?> + + <? if (Config::get()->ENABLE_REQUEST_NEW_PASSWORD_BY_USER && in_array('Standard', $GLOBALS['STUDIP_AUTH_PLUGIN'])): ?> + <a style="line-height: 1 !important" href="<?= URLHelper::getLink('dispatch.php/new_password?cancel_login=1') ?>"> + <? else: ?> + <a style="line-height: 1 !important" href="mailto:<?= $GLOBALS['UNI_CONTACT'] ?>?subject=<?= rawurlencode('Stud.IP Passwort vergessen - '.Config::get()->UNI_NAME_CLEAN) ?>&body=<?= rawurlencode('Ich habe mein Passwort vergessen. Bitte senden Sie mir ein Neues.\nMein Nutzername: ' . htmlReady($uname) . "\n") ?>"> + <? endif; ?> + <?= _('Passwort vergessen?') ?> + </a> + </div> +</form> diff --git a/templates/loginform.php b/templates/loginform.php index 348cceea607..deb9768b05f 100644 --- a/templates/loginform.php +++ b/templates/loginform.php @@ -1,6 +1,8 @@ <?php -# Lifter010: TODO -use Studip\Button, Studip\LinkButton; +/** + * @var array $loginerror + * @var string $error_msg + */ // Get background images (this should be resolved differently since mobile // browsers might still download the desktop background) @@ -21,70 +23,122 @@ if (!match_route('web_migrate.php')) { $bg_desktop = URLHelper::getURL('pictures/loginbackgrounds/1.jpg'); $bg_mobile = URLHelper::getURL('pictures/loginbackgrounds/2.jpg'); } +$show_hidden_login = false; ?> -<main id="content"> +<main id="content" class="loginpage"> <div id="background-desktop" style="background: url(<?= $bg_desktop ?>) no-repeat top left/cover;"></div> <div id="background-mobile" style="background: url(<?= $bg_mobile ?>) no-repeat top left/cover;"></div> - <? if ($loginerror): ?> - <!-- failed login code --> - <?= MessageBox::error(_('Bei der Anmeldung trat ein Fehler auf!'), [ - $error_msg, - sprintf( - _('Bitte wenden Sie sich bei Problemen an: <a href="mailto:%1$s">%1$s</a>'), - $GLOBALS['UNI_CONTACT'] - ) - ]) ?> - <? endif; ?> - <?= implode('', PageLayout::getMessages()); ?> + <div id="login_flex"> + <? if ($loginerror): ?> + <!-- failed login code --> + <?= MessageBox::error(_('Bei der Anmeldung trat ein Fehler auf!'), [ + $error_msg, + sprintf( + _('Bitte wenden Sie sich bei Problemen an: <a href="mailto:%1$s">%1$s</a>'), + $GLOBALS['UNI_CONTACT'] + ) + ]) ?> + <? endif ?> - <div id="loginbox"> - <form class="default" name="login" method="post" action="<?= URLHelper::getLink(Request::url(), ['cancel_login' => NULL]) ?>"> + <?= implode('', PageLayout::getMessages()); ?> + + <div id="loginbox"> <header> - <h1 style="margin: 0; padding-bottom:10px;"> - <?=_('Herzlich willkommen!')?> - </h1> + <h1><?= htmlReady(Config::get()->UNI_NAME_CLEAN) ?></h1> </header> - <section> - <label> - <?= _('Benutzername:') ?> - <input type="text" <?= mb_strlen($uname) ? '' : 'autofocus' ?> - id="loginname" name="loginname" - value="<?= htmlReady($uname) ?>" - size="20" - autocorrect="off" autocapitalize="off"> - </label> - </section> - <section> - <label for="password"> - <?= _('Passwort:') ?> - <input type="password" <?= mb_strlen($uname) ? 'autofocus' : '' ?> - id="password" name="password" size="20"> - </label> - </section> - <?= CSRFProtection::tokenTag() ?> - <input type="hidden" name="login_ticket" value="<?=Seminar_Session::get_ticket();?>"> - <input type="hidden" name="resolution" value=""> - <?= Button::createAccept(_('Anmelden'), _('Login')); ?> - <?= LinkButton::create(_('Abbrechen'), URLHelper::getURL('index.php', ['cancel_login' => 1], true)) ?> - </form> - <div> - <? if (Config::get()->ENABLE_REQUEST_NEW_PASSWORD_BY_USER && in_array('Standard', $GLOBALS['STUDIP_AUTH_PLUGIN'])): ?> - <a href="<?= URLHelper::getLink('dispatch.php/new_password?cancel_login=1') ?>"> - <? else: ?> - <a href="mailto:<?= $GLOBALS['UNI_CONTACT'] ?>?subject=<?= rawurlencode('Stud.IP Passwort vergessen - '.Config::get()->UNI_NAME_CLEAN) ?>&body=<?= rawurlencode('Ich habe mein Passwort vergessen. Bitte senden Sie mir ein Neues.\nMein Nutzername: ' . htmlReady($uname) . "\n") ?>"> - <? endif; ?> - <?= _('Passwort vergessen') ?> - </a> - <? if ($self_registration_activated): ?> - / - <a href="<?= URLHelper::getLink('register1.php?cancel_login=1') ?>"> - <?= _('Registrieren') ?> - </a> - <? endif; ?> + <? if (count($GLOBALS['STUDIP_AUTH_PLUGIN']) === 1 && StudipAuthAbstract::isLoginEnabled()) : ?> + <?= $this->render_partial('_standard_loginform', [ + 'hidden' => false, + ]) ?> + <? endif ?> + <nav> + <ul> + <? foreach (Navigation::getItem('/login') as $key => $nav) : ?> + <? if ($nav->isVisible()) : ?> + <? if ($key === 'standard_login') { + if (count($GLOBALS['STUDIP_AUTH_PLUGIN']) === 1 && StudipAuthAbstract::isLoginEnabled()) { + continue; + } else { + $show_hidden_login = true; + } + } + ?> + <? $name_and_title = explode(' - ', $nav->getTitle()) ?> + <li class="login_link"> + <? if (is_internal_url($url = $nav->getURL())) : ?> + <? SkipLinks::addLink($name_and_title[0], $url) ?> + <a href="<?= URLHelper::getLink($url, ['cancel_login' => 1]) ?>" id="<?= $nav->getLinkAttributes()['id'] ?>"> + <? else : ?> + <a href="<?= htmlReady($url) ?>" target="_blank" rel="noopener noreferrer"> + <? endif ?> + <?= htmlReady($name_and_title[0]) ?> + <p> + <?= htmlReady(!empty($name_and_title[1]) ? $name_and_title[1] : $nav->getDescription()) ?> + </p> + </a> + </li> + <? endif ?> + <? endforeach ?> + </ul> + </nav> + + <? if ($show_hidden_login) : ?> + <?= $this->render_partial('_standard_loginform', [ + 'hidden' => empty($loginerror), + ]) ?> + <? endif ?> + + <footer> + <? if ($GLOBALS['UNI_LOGIN_ADD']) : ?> + <div class="uni_login_add"> + <?= $GLOBALS['UNI_LOGIN_ADD'] ?> + </div> + <? endif ?> + + <div id="languages"> + <? foreach ($GLOBALS['INSTALLED_LANGUAGES'] as $temp_language_key => $temp_language): ?> + <?= Assets::img('languages/' . $temp_language['picture'], ['alt' => $temp_language['name'], 'size' => '24']) ?> + <a href="<?= URLHelper::getLink('index.php', ['set_language' =>$temp_language_key ]) ?>"> + <?= htmlReady($temp_language['name']) ?> + </a> + <? endforeach ?> + </div> + + <div id="contrast"> + <? if (isset($_SESSION['contrast'])) : ?> + <?= Icon::create('accessibility')->asImg(24) ?> + <a href="<?= URLHelper::getLink('index.php', ['unset_contrast' => 1, 'cancel_login' => 1]) ?>"><?= _('Normalen Kontrast aktivieren') ?></a> + <?= tooltipIcon(_('Aktiviert standardmäßige, nicht barrierefreie Kontraste.')); ?> + <? else : ?> + <?= Icon::create('accessibility')->asImg(24) ?> + <a href="<?= URLHelper::getLink('index.php', ['set_contrast' => 1, 'cancel_login' => 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> + + </footer> </div> + <? if (Config::get()->LOGIN_FAQ_VISIBILITY && count($faq_entries) > 0) : ?> + <div id="faq_box"> + <header><h1><?= htmlReady(Config::get()->LOGIN_FAQ_TITLE) ?></h1></header> + <? foreach ($faq_entries as $entry) : ?> + <article class="studip toggle"> + <header> + <h1><a href="#"><?= htmlReady($entry->title) ?></a></h1> + </header> + <section><?= formatReady($entry->description) ?> + </section> + </article> + <? endforeach ?> + </div> + <? endif ?> + </div> + + </main> <script type="text/javascript" language="javascript"> @@ -92,6 +146,7 @@ if (!match_route('web_migrate.php')) { $(function () { $('form[name=login]').submit(function () { $('input[name=resolution]', this).val( screen.width + 'x' + screen.height ); + $('input[name=device_pixel_ratio]').val(window.devicePixelRatio || 1); }); }); // --> diff --git a/tests/e2e/login.spec.ts b/tests/e2e/login.spec.ts index 95e44a6bf14..2e859dd1bba 100644 --- a/tests/e2e/login.spec.ts +++ b/tests/e2e/login.spec.ts @@ -8,7 +8,7 @@ test.describe('Loggin In - HTML Web Form @auth', () => { await expect(page.locator('#loginbox')).toBeVisible(); - const loginLink = page.getByRole('link', { name: 'Login für registrierte NutzerInnen' }); + const loginLink = page.getByRole('link', { name: 'Login für registrierte Nutzende' }); await expect(loginLink).toBeVisible(); await loginLink.click(); -- GitLab