aboutsummaryrefslogtreecommitdiff
path: root/resources
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2025-02-14 11:15:04 +0000
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2025-02-14 11:15:04 +0000
commit3592861804c484d3386c1be5eb050c1761892c59 (patch)
tree901ade7b1397ff8675e6955b182d3bc8f085486b /resources
parent461e5c491da6fe3b2bb3b7400fb09303b54d1aa0 (diff)
implement generic methods to allow the user to display a password in a password input, fixes #5186
Closes #5186 Merge request studip/studip!3876
Diffstat (limited to 'resources')
-rw-r--r--resources/assets/javascripts/bootstrap/application.js21
-rw-r--r--resources/assets/stylesheets/studip.scss20
-rw-r--r--resources/vue/base-directives.js2
-rw-r--r--resources/vue/components/admission/PasswordAdmission.vue18
-rw-r--r--resources/vue/directives/allow-plaintext-toggle.ts52
5 files changed, 90 insertions, 23 deletions
diff --git a/resources/assets/javascripts/bootstrap/application.js b/resources/assets/javascripts/bootstrap/application.js
index 4e5eccc..7fdb316 100644
--- a/resources/assets/javascripts/bootstrap/application.js
+++ b/resources/assets/javascripts/bootstrap/application.js
@@ -1,5 +1,7 @@
import { $gettext } from '../lib/gettext';
import eventBus from "../lib/event-bus.ts";
+import allowPlaintextToggle from "../../../vue/directives/allow-plaintext-toggle";
+
/* ------------------------------------------------------------------------
* application.js
@@ -365,8 +367,6 @@ STUDIP.domReady(function () {
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) => {
@@ -391,16 +391,11 @@ STUDIP.domReady(function () {
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';
- }
- });
+/* Allow password inputs to display the password as plain text */
+STUDIP.ready(() => {
+ document.querySelectorAll('input[type="password"].allow-plaintext-toggle').forEach((node) => {
+ allowPlaintextToggle(node);
+ })
});
diff --git a/resources/assets/stylesheets/studip.scss b/resources/assets/stylesheets/studip.scss
index 9183081..42ab072 100644
--- a/resources/assets/stylesheets/studip.scss
+++ b/resources/assets/stylesheets/studip.scss
@@ -627,3 +627,23 @@ mark {
width: var(--icon-size-inline);
height: var(--icon-size-inline);
}
+
+/** Password plain toggle **/
+input.allow-plaintext-toggle {
+ padding-right: 28px !important;
+
+ +.password-display-toggle {
+ @include hide-text;
+ @extend .studip-icon;
+
+ cursor: pointer;
+ position: absolute;
+ transform: translateY(-50%);
+
+ @include background-icon(visibility-invisible);
+
+ &.password-is-hidden {
+ @include background-icon(visibility-checked);
+ }
+ }
+}
diff --git a/resources/vue/base-directives.js b/resources/vue/base-directives.js
index 8f46b03..c7b8509 100644
--- a/resources/vue/base-directives.js
+++ b/resources/vue/base-directives.js
@@ -1,7 +1,9 @@
import autofocus from './directives/autofocus';
+import allowPlaintextToggle from "./directives/allow-plaintext-toggle";
const BaseDirectives = {
autofocus,
+ allowPlaintextToggle,
};
export default BaseDirectives;
diff --git a/resources/vue/components/admission/PasswordAdmission.vue b/resources/vue/components/admission/PasswordAdmission.vue
index 3f0ecf4..5d87e4d 100644
--- a/resources/vue/components/admission/PasswordAdmission.vue
+++ b/resources/vue/components/admission/PasswordAdmission.vue
@@ -12,15 +12,17 @@
</studip-message-box>
<label>
{{ $gettext('Zugangspasswort') }}
- <input :type="passwordVisible ? 'text' : 'password'" v-model="password1" ref="password1">
- <studip-icon class="password-visibility" @click="togglePasswordVisible"
- :shape="passwordVisible ? 'visibility-invisible' : 'visibility-visible'"></studip-icon>
+ <input type="password"
+ v-model="password1"
+ v-allow-plaintext-toggle
+ >
</label>
<label>
{{ $gettext('Passwort wiederholen') }}
- <input :type="passwordVisible ? 'text' : 'password'" v-model="password2" ref="password2">
- <studip-icon class="password-visibility" @click="togglePasswordVisible"
- :shape="passwordVisible ? 'visibility-invisible' : 'visibility-visible'"></studip-icon>
+ <input type="password"
+ v-model="password2"
+ v-allow-plaintext-toggle
+ >
</label>
</section>
</form>
@@ -43,14 +45,10 @@ export default {
messageText: this.message || this.$gettext('Für die Anmeldung ist ein Passwort erforderlich.'),
password1: '',
password2: '',
- passwordVisible: false,
passwordSet: this.hasPassword
}
},
methods: {
- togglePasswordVisible() {
- this.passwordVisible = !this.passwordVisible;
- },
setRuleData(data) {
if (data.attributes.payload.password !== '') {
this.passwordSet = true;
diff --git a/resources/vue/directives/allow-plaintext-toggle.ts b/resources/vue/directives/allow-plaintext-toggle.ts
new file mode 100644
index 0000000..7969c49
--- /dev/null
+++ b/resources/vue/directives/allow-plaintext-toggle.ts
@@ -0,0 +1,52 @@
+import {$gettext} from "../../assets/javascripts/lib/gettext";
+import {DirectiveBinding, nextTick} from "vue";
+
+const messages = {
+ 0: $gettext('Passwort verstecken'),
+ 1: $gettext('Passwort anzeigen'),
+};
+
+function initialize(el: HTMLElement): void {
+ if (!el.parentElement) {
+ return;
+ }
+
+ el.classList.add('allow-plaintext-toggle');
+
+ el.parentElement.style.position = 'relative';
+
+ const bbox = el.getBoundingClientRect();
+ const parentBbox = el.parentElement.getBoundingClientRect();
+
+ const x = (bbox.x - parentBbox.x) + bbox.width - 26;
+ const y = (bbox.y - parentBbox.y) + bbox.height / 2;
+
+ const toggle = document.createElement('button');
+ toggle.classList.add('as-link', 'password-display-toggle', 'password-is-hidden');
+ toggle.setAttribute('title', messages[1]);
+ toggle.innerText = messages[1];
+ toggle.style.top = `${y}px`;
+ toggle.style.left = `${x}px`;
+
+ toggle.addEventListener('click', (event) => {
+ const isHidden = el.getAttribute('type') === 'text';
+ toggle.classList.toggle('password-is-hidden', isHidden);
+ toggle.setAttribute('title', messages[isHidden ? 1 : 0]);
+ toggle.innerText = messages[isHidden ? 1 : 0];
+
+ el.setAttribute('type', isHidden ? 'password' : 'text');
+
+ event.preventDefault();
+ });
+
+ el.after(toggle);
+}
+
+export default (el: HTMLElement, binding?: DirectiveBinding | undefined): void => {
+ if (el.nextElementSibling?.matches('button.password-display-toggle')) {
+ return;
+ }
+
+ const promise = binding ? nextTick() : Promise.resolve();
+ promise.then(() => initialize(el))
+}