Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorCarl Schwan <carl@carlschwan.eu>2022-09-06 17:54:33 +0300
committerGitHub <noreply@github.com>2022-09-06 17:54:33 +0300
commit36b2d3dc2a7978a3f19b9888bb03ecd2f566f59e (patch)
treebc8a81dcbf67b5a2c72f6d4bc988b24a8c4c6ccb /core
parent5104ee9dd494ff1916593139937d02e3affc8918 (diff)
parent43d1aa38df776a6767723db262af9e099a88fdfc (diff)
Merge pull request #33781 from nextcloud/port/login-form/vue-password-components
Use new vue components in login form
Diffstat (limited to 'core')
-rw-r--r--core/css/guest.css81
-rw-r--r--core/css/lostpassword/resetpassword.css7
-rw-r--r--core/src/components/login/LoginButton.vue6
-rw-r--r--core/src/components/login/LoginForm.vue177
-rw-r--r--core/src/components/login/ResetPassword.vue80
-rw-r--r--core/src/views/Login.vue67
6 files changed, 176 insertions, 242 deletions
diff --git a/core/css/guest.css b/core/css/guest.css
index 813e04db213..7408d5485cc 100644
--- a/core/css/guest.css
+++ b/core/css/guest.css
@@ -108,14 +108,6 @@ form {
margin: auto;
padding: 0;
}
-form fieldset {
- width: 260px;
- margin-top: 8px;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
form #sqliteInformation {
margin-top: 0px;
margin-bottom: 20px;
@@ -146,8 +138,7 @@ form #datadirField legend {
}
/* Buttons and input */
-#submit-wrapper,
-#reset-password-wrapper {
+#submit-wrapper {
display: flex;
align-items: center;
justify-content: center;
@@ -160,14 +151,10 @@ form #datadirField legend {
.wrapper {
margin-top: 0;
}
- .alternative-logins {
- margin: auto;
- }
}
-#submit-wrapper .submit-icon,
-#reset-password-wrapper .submit-icon {
+#submit-wrapper .submit-icon {
position: absolute;
right: 24px;
transition: right 100ms ease-in-out;
@@ -187,12 +174,6 @@ form #datadirField legend {
right: 20px;
}
-#reset-password-submit {
- padding: 10px;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
#submit-wrapper .icon-loading-small {
position: absolute;
top: 22px;
@@ -262,9 +243,6 @@ input[type='email'] {
color: var(--color-text-lighter);
cursor: text;
font-family: inherit;
- -webkit-appearance: textfield;
- -moz-appearance: textfield;
- box-sizing: content-box;
font-weight: normal;
margin-left: 0;
margin-right: 0;
@@ -471,53 +449,6 @@ form .warning input[type='checkbox']+label {
#remember_login {
margin: 18px 5px 0 16px !important;
}
-.lost-password-container {
- display: inline-block;
- margin: 10px 0;
- text-align: center;
- width: 100%;
-}
-#lost-password,
-#lost-password-back,
-#reset-password-wrapper + a {
- display: inline-block;
- font-weight: normal !important;
- padding: 12px 12px 0 12px;
- cursor: pointer;
-}
-#forgot-password {
- padding: 11px;
- float: right;
- color: var(--color-primary-text);
-}
-
-/* Alternative Logins */
-.alternative-logins legend {
- margin-bottom: 10px;
-}
-.alternative-logins li {
- height: 40px;
- white-space: nowrap;
- padding: 05px;
-}
-.alternative-logins a.button,
-.alternative-logins li a {
- width: 100%;
- display: inline-block;
- text-align: center;
- box-sizing: border-box;
- border: 2px solid var(--color-primary-text);
- background-color: var(--color-primary);
- color: var(--color-primary-text);
- border-radius: 100px; /* --border-radius-pill */
-}
-
-.alternative-logins a.button:focus,
-.alternative-logins li a:focus {
- border: 2px solid var(--color-primary-hover);
- background-image: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
- background-position: initial;
-}
/* fixes for update page TODO should be fixed some time in a proper way */
/* this is just for an error while updating the ownCloud instance */
@@ -792,24 +723,22 @@ a.legal {
}
.notecard.success {
- --note-background: rgba(var(--color-success-rgb), 0.2);
+ --note-background: rgba(var(--color-success-rgb), 0.1);
--note-theme: var(--color-success);
}
.notecard.error {
- --note-background: rgba(var(--color-error-rgb), 0.2);
+ --note-background: rgba(var(--color-error-rgb), 0.1);
--note-theme: var(--color-error);
}
.notecard.warning {
- --note-background: rgba(var(--color-warning-rgb), 0.2);
+ --note-background: rgba(var(--color-warning-rgb), 0.1);
--note-theme: var(--color-warning);
}
.notecard {
color: var(--color-text-light);
background-color: var(--note-background);
- border: 1px solid var(--color-border);
border-left: 4px solid var(--note-theme);
border-radius: var(--border-radius);
- box-shadow: rgba(43, 42, 51, 0.05) 0px 1px 2px 0px;
margin: 1rem 0;
margin-top: 1rem;
padding: 1rem;
diff --git a/core/css/lostpassword/resetpassword.css b/core/css/lostpassword/resetpassword.css
deleted file mode 100644
index 0b3c251e8d7..00000000000
--- a/core/css/lostpassword/resetpassword.css
+++ /dev/null
@@ -1,7 +0,0 @@
-#reset-password p {
- position: relative;
-}
-
-.text-center {
- text-align: center;
-}
diff --git a/core/src/components/login/LoginButton.vue b/core/src/components/login/LoginButton.vue
index c9aaa2522c6..3d3ac25de6d 100644
--- a/core/src/components/login/LoginButton.vue
+++ b/core/src/components/login/LoginButton.vue
@@ -62,3 +62,9 @@ export default {
},
}
</script>
+
+<style lang="scss" scoped>
+.button-vue {
+ margin-top: .5rem;
+}
+</style>
diff --git a/core/src/components/login/LoginForm.vue b/core/src/components/login/LoginForm.vue
index 88a465d1aa5..b794a2c02ce 100644
--- a/core/src/components/login/LoginForm.vue
+++ b/core/src/components/login/LoginForm.vue
@@ -21,28 +21,28 @@
<template>
<form ref="loginForm"
+ class="login-form"
method="post"
name="login"
:action="loginActionUrl"
@submit="submit">
- <fieldset>
- <div v-if="apacheAuthFailed"
- class="warning">
- {{ t('core', 'Server side authentication failed!') }}<br>
- <small>{{ t('core', 'Please contact your administrator.') }}
- </small>
- </div>
- <div v-for="(message, index) in messages"
- :key="index"
- class="warning">
- {{ message }}<br>
- </div>
- <div v-if="internalException"
- class="warning">
- {{ t('core', 'An internal error occurred.') }}<br>
- <small>{{ t('core', 'Please try again or contact your administrator.') }}
- </small>
- </div>
+ <fieldset class="login-form__fieldset">
+ <NcNoteCard v-if="apacheAuthFailed"
+ :title="t('core', 'Server side authentication failed!')"
+ type="warning">
+ {{ t('core', 'Please contact your administrator.') }}
+ </NcNoteCard>
+ <NcNoteCard v-if="messages.length > 0">
+ <div v-for="(message, index) in messages"
+ :key="index">
+ {{ message }}<br>
+ </div>
+ </NcNoteCard>
+ <NcNoteCard v-if="internalException"
+ :class="t('core', 'An internal error occurred.')"
+ type="warning">
+ {{ t('core', 'Please try again or contact your administrator.') }}
+ </NcNoteCard>
<div id="message"
class="hidden">
<img class="float-spinner"
@@ -52,65 +52,35 @@
<!-- the following div ensures that the spinner is always inside the #message div -->
<div style="clear: both;" />
</div>
- <p class="grouptop"
- :class="{shake: invalidPassword}">
- <input id="user"
- ref="user"
- v-model="user"
- type="text"
- name="user"
- autocapitalize="none"
- autocorrect="off"
- :autocomplete="autoCompleteAllowed ? 'on' : 'off'"
- :placeholder="t('core', 'Username or email')"
- :aria-label="t('core', 'Username or email')"
- required
- @change="updateUsername">
- <label for="user" class="infield">{{ t('core', 'Username or email') }}</label>
- </p>
-
- <p class="groupbottom"
- :class="{shake: invalidPassword}">
- <input id="password"
- ref="password"
- :type="passwordInputType"
- class="password-with-toggle"
- name="password"
- autocorrect="off"
- autocapitalize="none"
- :autocomplete="autoCompleteAllowed ? 'current-password' : 'off'"
- :placeholder="t('core', 'Password')"
- :aria-label="t('core', 'Password')"
- required>
- <label for="password"
- class="infield">{{ t('core', 'Password') }}</label>
- <NcButton class="toggle-password"
- type="tertiary-no-background"
- :aria-label="isPasswordHidden ? t('core', 'Show password') : t('core', 'Hide password')"
- @click.stop.prevent="togglePassword">
- <template #icon>
- <Eye v-if="isPasswordHidden" :size="20" />
- <EyeOff v-else :size="20" />
- </template>
- </NcButton>
- </p>
+ <NcTextField id="user"
+ ref="user"
+ :label="t('core', 'Account name or email')"
+ :label-visible="true"
+ name="user"
+ :value.sync="user"
+ :class="{shake: invalidPassword}"
+ autocapitalize="none"
+ :spellchecking="false"
+ :autocomplete="autoCompleteAllowed ? 'username' : 'off'"
+ required
+ @change="updateUsername" />
+
+ <NcPasswordField id="password"
+ ref="password"
+ name="password"
+ :label-visible="true"
+ :class="{shake: invalidPassword}"
+ :value="password"
+ :spellchecking="false"
+ autocapitalize="none"
+ :autocomplete="autoCompleteAllowed ? 'current-password' : 'off'"
+ :label="t('core', 'Password')"
+ :helper-text="errorLabel"
+ :error="isError"
+ required />
<LoginButton :loading="loading" />
- <p v-if="invalidPassword"
- class="warning wrongPasswordMsg">
- {{ t('core', 'Wrong username or password.') }}
- </p>
- <p v-else-if="userDisabled"
- class="warning userDisabledMsg">
- {{ t('core', 'User disabled') }}
- </p>
-
- <p v-if="throttleDelay && throttleDelay > 5000"
- class="warning throttledMsg">
- {{ t('core', 'We have detected multiple invalid login attempts from your IP. Therefore your next login is throttled up to 30 seconds.') }}
- </p>
-
<input v-if="redirectUrl"
type="hidden"
name="redirect_url"
@@ -136,20 +106,20 @@
import jstz from 'jstimezonedetect'
import { generateUrl, imagePath } from '@nextcloud/router'
-import NcButton from '@nextcloud/vue/dist/Components/NcButton'
-import Eye from 'vue-material-design-icons/Eye'
-import EyeOff from 'vue-material-design-icons/EyeOff'
+import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js'
+import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
+import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
-import LoginButton from './LoginButton'
+import LoginButton from './LoginButton.vue'
export default {
name: 'LoginForm',
components: {
- NcButton,
- Eye,
- EyeOff,
LoginButton,
+ NcPasswordField,
+ NcTextField,
+ NcNoteCard,
},
props: {
@@ -188,13 +158,28 @@ export default {
loading: false,
timezone: jstz.determine().name(),
timezoneOffset: (-new Date().getTimezoneOffset() / 60),
- user: this.username,
+ user: '',
password: '',
- passwordInputType: 'password',
}
},
computed: {
+ isError() {
+ return this.invalidPassword || this.userDisabled
+ || (this.throttleDelay && this.throttleDelay > 5000)
+ },
+ errorLabel() {
+ if (this.invalidPassword) {
+ return t('core', 'Wrong username or password.')
+ }
+ if (this.userDisabled) {
+ return t('core', 'User disabled')
+ }
+ if (this.throttleDelay && this.throttleDelay > 5000) {
+ return t('core', 'We have detected multiple invalid login attempts from your IP. Therefore your next login is throttled up to 30 seconds.')
+ }
+ return undefined
+ },
apacheAuthFailed() {
return this.errors.indexOf('apacheAuthFailed') !== -1
},
@@ -213,27 +198,18 @@ export default {
loginActionUrl() {
return generateUrl('login')
},
- isPasswordHidden() {
- return this.passwordInputType === 'password'
- },
},
mounted() {
if (this.username === '') {
this.$refs.user.focus()
} else {
+ this.user = this.username
this.$refs.password.focus()
}
},
methods: {
- togglePassword() {
- if (this.passwordInputType === 'password') {
- this.passwordInputType = 'text'
- } else {
- this.passwordInputType = 'password'
- }
- },
updateUsername() {
this.$emit('update:username', this.user)
},
@@ -246,10 +222,15 @@ export default {
</script>
<style lang="scss" scoped>
-.toggle-password {
- position: absolute;
- top: 2px;
- right: 10px;
- color: var(--color-text-lighter);
+.login-form {
+ text-align: left;
+ font-size: 1rem;
+
+ &__fieldset {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: .5rem;
+ }
}
</style>
diff --git a/core/src/components/login/ResetPassword.vue b/core/src/components/login/ResetPassword.vue
index aadf7863dfe..72ad7ebd812 100644
--- a/core/src/components/login/ResetPassword.vue
+++ b/core/src/components/login/ResetPassword.vue
@@ -20,41 +20,37 @@
-->
<template>
- <form @submit.prevent="submit">
- <fieldset>
- <p>
- <input id="user"
- v-model="user"
- type="text"
- name="user"
- autocapitalize="off"
- :placeholder="t('core', 'Username or email')"
- :aria-label="t('core', 'Username or email')"
- required
- @change="updateUsername">
- <!--<?php p($_['user_autofocus'] ? 'autofocus' : ''); ?>
+ <form class="login-form" @submit.prevent="submit">
+ <fieldset class="login-form__fieldset">
+ <NcTextField id="user"
+ :value.sync="user"
+ name="user"
+ autocapitalize="off"
+ :label="t('core', 'Account name or email')"
+ :label-visible="true"
+ required
+ @change="updateUsername" />
+ <!--<?php p($_['user_autofocus'] ? 'autofocus' : ''); ?>
autocomplete="<?php p($_['login_form_autocomplete']); ?>" autocapitalize="none" autocorrect="off"-->
- <label for="user" class="infield">{{ t('core', 'Username or email') }}</label>
- </p>
- <div id="reset-password-wrapper">
- <LoginButton :value="t('core', 'Reset password')" />
- </div>
- <p v-if="message === 'send-success'"
- class="notecard success">
+ <LoginButton :value="t('core', 'Reset password')" />
+
+ <NcNoteCard v-if="message === 'send-success'"
+ type="success">
{{ t('core', 'A password reset message has been sent to the email address of this account. If you do not receive it, check your spam/junk folders or ask your local administrator for help.') }}
<br>
{{ t('core', 'If it is not there ask your local administrator.') }}
- </p>
- <p v-else-if="message === 'send-error'"
- class="notecard error">
+ </NcNoteCard>
+ <NcNoteCard v-else-if="message === 'send-error'"
+ type="error">
{{ t('core', 'Couldn\'t send reset email. Please contact your administrator.') }}
- </p>
- <p v-else-if="message === 'reset-error'"
- class="notecard error">
+ </NcNoteCard>
+ <NcNoteCard v-else-if="message === 'reset-error'"
+ type="error">
{{ t('core', 'Password cannot be changed. Please contact your administrator.') }}
- </p>
+ </NcNoteCard>
- <a href="#"
+ <a class="login-form__link"
+ href="#"
@click.prevent="$emit('abort')">
{{ t('core', 'Back to login') }}
</a>
@@ -66,11 +62,15 @@
import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
import LoginButton from './LoginButton.vue'
+import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
+import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
export default {
name: 'ResetPassword',
components: {
LoginButton,
+ NcNoteCard,
+ NcTextField,
},
props: {
username: {
@@ -130,8 +130,26 @@ export default {
}
</script>
-<style scoped>
- .update {
- width: auto;
+<style lang="scss" scoped>
+.login-form {
+ text-align: left;
+ font-size: 1rem;
+
+ &__fieldset {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: .5rem;
}
+
+ &__link {
+ display: block;
+ font-weight: normal !important;
+ padding-bottom: 1rem;
+ cursor: pointer;
+ font-size: var(--default-font-size);
+ text-align: center;
+ padding: .5rem 1rem 1rem 1rem;
+ }
+}
</style>
diff --git a/core/src/views/Login.vue b/core/src/views/Login.vue
index 0cc8dc39cca..588369e5408 100644
--- a/core/src/views/Login.vue
+++ b/core/src/views/Login.vue
@@ -20,7 +20,7 @@
-->
<template>
- <div id="login" class="guest-box">
+ <div class="guest-box login-box">
<div v-if="!hideLoginForm || directLogin">
<transition name="fade" mode="out-in">
<div v-if="!passwordlessLogin && !resetPassword && resetPasswordTarget === ''">
@@ -34,16 +34,17 @@
@submit="loading = true" />
<a v-if="canResetPassword && resetPasswordLink !== ''"
id="lost-password"
+ class="login-box__link"
:href="resetPasswordLink">
{{ t('core', 'Forgot password?') }}
</a>
<a v-else-if="canResetPassword && !resetPassword"
id="lost-password"
+ class="login-box__link"
:href="resetPasswordLink"
@click.prevent="resetPassword = true">
{{ t('core', 'Forgot password?') }}
</a>
- <br>
<template v-if="hasPasswordless">
<div v-if="countAlternativeLogins"
class="alternative-logins">
@@ -72,7 +73,7 @@
:is-localhost="isLocalhost"
:has-public-key-credential="hasPublicKeyCredential"
@submit="loading = true" />
- <a href="#" @click.prevent="passwordlessLogin = false">
+ <a href="#" class="login-box__link" @click.prevent="passwordlessLogin = false">
{{ t('core', 'Back') }}
</a>
</div>
@@ -95,19 +96,16 @@
</div>
<div v-else>
<transition name="fade" mode="out-in">
- <div class="warning">
- {{ t('core', 'Login form is disabled.') }}<br>
- <small>
- {{ t('core', 'Please contact your administrator.') }}
- </small>
- </div>
+ <NcNoteCard type="warning" :title="t('core', 'Login form is disabled.')">
+ {{ t('core', 'Please contact your administrator.') }}
+ </NcNoteCard>
</transition>
</div>
<div id="alternative-logins" class="alternative-logins">
<NcButton v-for="(alternativeLogin, index) in alternativeLogins"
:key="index"
- type="primary"
+ type="secondary"
:wide="true"
:class="[alternativeLogin.class]"
role="link"
@@ -127,7 +125,8 @@ import LoginForm from '../components/login/LoginForm.vue'
import PasswordLessLoginForm from '../components/login/PasswordLessLoginForm.vue'
import ResetPassword from '../components/login/ResetPassword.vue'
import UpdatePassword from '../components/login/UpdatePassword.vue'
-import NcButton from '@nextcloud/vue/dist/Components/NcButton'
+import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
+import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
const query = queryString.parse(location.search)
if (query.clear === '1') {
@@ -149,6 +148,7 @@ export default {
ResetPassword,
UpdatePassword,
NcButton,
+ NcNoteCard,
},
data() {
@@ -192,28 +192,35 @@ export default {
</script>
<style lang="scss">
- .fade-enter-active, .fade-leave-active {
- transition: opacity .3s;
- }
- .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
- opacity: 0;
- }
+body {
+ font-size: var(--default-font-size);
+}
+
+.login-box {
+ width: 300px;
- #lost-password {
- padding: 4px;
- margin: 8px;
- border-radius: var(--border-radius);
+ &__link {
+ display: block;
+ padding: 1rem;
+ font-size: var(--default-font-size);
+ text-align: center;
+ font-weight: normal !important;
}
+}
+.fade-enter-active, .fade-leave-active {
+ transition: opacity .3s;
+}
+.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
+ opacity: 0;
+}
- .alternative-logins button {
- margin-top: 12px;
- margin-bottom: 12px;
- &:first-child {
- margin-top: 0;
- }
+.alternative-logins {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
- &:last-child {
- margin-bottom: 0;
- }
+ .button-vue {
+ box-sizing: border-box;
}
+}
</style>