aboutsummaryrefslogtreecommitdiffstats
path: root/core/src
diff options
context:
space:
mode:
authorCarl Schwan <carl@carlschwan.eu>2022-07-18 17:58:05 +0200
committerCarl Schwan <carl@carlschwan.eu>2022-07-27 10:43:21 +0200
commit253118298dbac78d13c5333279def8bbd3ad555c (patch)
treed684b43694cf5cdfa70fa528fbeaba89e65f5b66 /core/src
parent5a236762b8e6b5f09a0c65f5840416bbad2d159a (diff)
downloadnextcloud-server-253118298dbac78d13c5333279def8bbd3ad555c.tar.gz
nextcloud-server-253118298dbac78d13c5333279def8bbd3ad555c.zip
Redesign guest pages for better accessibility
- Use white box and put content on it - Improve focus indicator Signed-off-by: Carl Schwan <carl@carlschwan.eu>
Diffstat (limited to 'core/src')
-rw-r--r--core/src/components/login/LoginButton.vue54
-rw-r--r--core/src/components/login/ResetPassword.vue9
-rw-r--r--core/src/components/setup/InstallButton.vue43
-rw-r--r--core/src/components/setup/RecommendedApps.vue40
-rw-r--r--core/src/install.js13
-rw-r--r--core/src/views/Login.vue191
6 files changed, 166 insertions, 184 deletions
diff --git a/core/src/components/login/LoginButton.vue b/core/src/components/login/LoginButton.vue
index e99398ba09a..0b9afd58685 100644
--- a/core/src/components/login/LoginButton.vue
+++ b/core/src/components/login/LoginButton.vue
@@ -20,23 +20,27 @@
-->
<template>
- <div class="submit-wrapper" @click="$emit('click')">
- <input type="submit"
- class="submit-wrapper__input primary"
- title=""
- :value="!loading ? value : valueLoading">
- <div v-if="loading" class="submit-wrapper__icon icon-loading-small-dark" />
- <ArrowRight v-else class="submit-wrapper__icon" />
- </div>
+ <Button type="primary"
+ native-type="submit"
+ :wide="true"
+ @click="$emit('click')">
+ {{ !loading ? value : valueLoading }}
+ <template #icon>
+ <div v-if="loading" class="submit-wrapper__icon icon-loading-small-dark" />
+ <ArrowRight v-else class="submit-wrapper__icon" />
+ </template>
+ </Button>
</template>
<script>
+import Button from '@nextcloud/vue/dist/Components/Button'
import ArrowRight from 'vue-material-design-icons/ArrowRight.vue'
export default {
name: 'LoginButton',
components: {
ArrowRight,
+ Button,
},
props: {
value: {
@@ -58,37 +62,3 @@ export default {
},
}
</script>
-
-<style scoped lang="scss">
-.submit-wrapper {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 10px 5px;
- position: relative;
- margin: 0 auto;
-
- &__input {
- width: 260px;
- height: 50px;
- }
-
- &__icon {
- display: flex;
- position: absolute;
- right: 24px;
- transition: right 100ms ease-in-out;
- /* The submit icon is positioned on the submit button.
- From the user point of view the icon is part of the
- button, so the clicks on the icon have to be
- applied to the button instead. */
- pointer-events: none;
- }
-
- &__input:hover + &__icon:not(.icon-loading-small-dark),
- &__input:focus + &__icon:not(.icon-loading-small-dark),
- &__input:active + &__icon:not(.icon-loading-small-dark) {
- right: 20px;
- }
-}
-</style>
diff --git a/core/src/components/login/ResetPassword.vue b/core/src/components/login/ResetPassword.vue
index 7a499baa2f0..aadf7863dfe 100644
--- a/core/src/components/login/ResetPassword.vue
+++ b/core/src/components/login/ResetPassword.vue
@@ -40,22 +40,19 @@
<LoginButton :value="t('core', 'Reset password')" />
</div>
<p v-if="message === 'send-success'"
- class="update">
+ class="notecard 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="update warning">
+ class="notecard error">
{{ t('core', 'Couldn\'t send reset email. Please contact your administrator.') }}
</p>
<p v-else-if="message === 'reset-error'"
- class="update warning">
+ class="notecard error">
{{ t('core', 'Password cannot be changed. Please contact your administrator.') }}
</p>
- <p v-else-if="message"
- class="update"
- :class="{warning: error}" />
<a href="#"
@click.prevent="$emit('abort')">
diff --git a/core/src/components/setup/InstallButton.vue b/core/src/components/setup/InstallButton.vue
deleted file mode 100644
index 52d2d622a76..00000000000
--- a/core/src/components/setup/InstallButton.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
- - @copyright 2022 Christopher Ng <chrng8@gmail.com>
- -
- - @author Christopher Ng <chrng8@gmail.com>
- -
- - @license GNU AGPL version 3 or any later version
- -
- - This program is free software: you can redistribute it and/or modify
- - it under the terms of the GNU Affero General Public License as
- - published by the Free Software Foundation, either version 3 of the
- - License, or (at your option) any later version.
- -
- - This program is distributed in the hope that it will be useful,
- - but WITHOUT ANY WARRANTY; without even the implied warranty of
- - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- - GNU Affero General Public License for more details.
- -
- - You should have received a copy of the GNU Affero General Public License
- - along with this program. If not, see <http://www.gnu.org/licenses/>.
- -
--->
-
-<template>
- <button class="primary"
- :autofocus="true"
- v-on="$listeners">
- {{ t('core', 'Install recommended apps') }}
- </button>
-</template>
-
-<script>
-export default {
- name: 'InstallButton',
-}
-</script>
-
-<style lang="scss" scoped>
- button {
- margin: 24px auto 10px auto;
- padding: 10px 20px;
- font-size: 20px;
- }
-</style>
diff --git a/core/src/components/setup/RecommendedApps.vue b/core/src/components/setup/RecommendedApps.vue
index cd39929f14b..5bd5bb0bbae 100644
--- a/core/src/components/setup/RecommendedApps.vue
+++ b/core/src/components/setup/RecommendedApps.vue
@@ -20,7 +20,7 @@
-->
<template>
- <div class="body-login-container">
+ <div class="guest-box">
<h2>{{ t('core', 'Recommended apps') }}</h2>
<p v-if="loadingApps" class="loading text-center">
{{ t('core', 'Loading apps …') }}
@@ -53,12 +53,21 @@
</div>
</div>
- <InstallButton v-if="showInstallButton"
- @click.stop.prevent="installApps" />
-
- <p class="text-center">
- <a :href="defaultPageUrl">{{ t('core', 'Cancel') }}</a>
- </p>
+ <div class="dialog-row">
+ <Button v-if="showInstallButton"
+ type="tertiary"
+ role="link"
+ href="defaultPageUrl"
+ @click="goTo(defaultPageUrl)">
+ {{ t('core', 'Cancel') }}
+ </Button>
+
+ <Button v-if="showInstallButton"
+ type="primary"
+ @click.stop.prevent="installApps">
+ {{ t('core', 'Install recommended apps') }}
+ </Button>
+ </div>
</div>
</template>
@@ -69,8 +78,7 @@ import { loadState } from '@nextcloud/initial-state'
import pLimit from 'p-limit'
import { translate as t } from '@nextcloud/l10n'
-// TODO replace with Button component when @nextcloud/vue is upgraded to v5
-import InstallButton from './InstallButton'
+import Button from '@nextcloud/vue/dist/Components/Button'
import logger from '../../logger'
@@ -106,7 +114,7 @@ const defaultPageUrl = loadState('core', 'defaultPageUrl')
export default {
name: 'RecommendedApps',
components: {
- InstallButton,
+ Button,
},
data() {
return {
@@ -184,13 +192,18 @@ export default {
}
return recommended[appId].description
},
+ goTo(href) {
+ window.location.href = href
+ },
},
}
</script>
<style lang="scss" scoped>
-.body-login-container {
-
+.dialog-row {
+ display: flex;
+ justify-content: end;
+ margin-top: 8px;
}
p {
@@ -215,7 +228,7 @@ p {
img {
height: 50px;
width: 50px;
- filter: invert(1);
+ filter: var(--background-invert-if-dark);
}
img, .info {
@@ -228,7 +241,6 @@ p {
}
h3 {
- color: #fff;
margin-top: 0;
}
diff --git a/core/src/install.js b/core/src/install.js
index 51358e6f70d..6dbae452d31 100644
--- a/core/src/install.js
+++ b/core/src/install.js
@@ -95,14 +95,13 @@ window.addEventListener('DOMContentLoaded', function() {
$('.float-spinner').show(250)
// Disable inputs
- $(':submit', this).attr('disabled', 'disabled').val($(':submit', this).data('finishing'))
+ $('input[type="submit"]').attr('disabled', 'disabled').val($('input[type="submit"]').data('finishing'))
$('input', this).addClass('ui-state-disabled').attr('disabled', 'disabled')
// only disable buttons if they are present
if ($('#selectDbType').find('.ui-button').length > 0) {
$('#selectDbType').buttonset('disable')
}
$('.strengthify-wrapper, .tipsy')
- .css('-ms-filter', '"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"')
.css('filter', 'alpha(opacity=30)')
.css('opacity', 0.3)
@@ -165,5 +164,13 @@ window.addEventListener('DOMContentLoaded', function() {
})
$('#dbpass').showPassword().keyup()
- $('#adminpass').showPassword().keyup()
+ $('.toggle-password').click(function(event) {
+ event.preventDefault()
+ const currentValue = $(this).parent().children('input').attr('type')
+ if (currentValue === 'password') {
+ $(this).parent().children('input').attr('type', 'text')
+ } else {
+ $(this).parent().children('input').attr('type', 'password')
+ }
+ })
})
diff --git a/core/src/views/Login.vue b/core/src/views/Login.vue
index e2ca484126c..262c9e2973f 100644
--- a/core/src/views/Login.vue
+++ b/core/src/views/Login.vue
@@ -20,87 +20,102 @@
-->
<template>
- <div v-if="!hideLoginForm || directLogin">
- <transition name="fade" mode="out-in">
- <div v-if="!passwordlessLogin && !resetPassword && resetPasswordTarget === ''"
- key="login">
- <LoginForm :username.sync="user"
- :redirect-url="redirectUrl"
- :direct-login="directLogin"
- :messages="messages"
- :errors="errors"
- :throttle-delay="throttleDelay"
- :auto-complete-allowed="autoCompleteAllowed"
- @submit="loading = true" />
- <a v-if="canResetPassword && resetPasswordLink !== ''"
- id="lost-password"
- :href="resetPasswordLink">
- {{ t('core', 'Forgot password?') }}
- </a>
- <a v-else-if="canResetPassword && !resetPassword"
- id="lost-password"
- :href="resetPasswordLink"
- @click.prevent="resetPassword = true">
- {{ t('core', 'Forgot password?') }}
- </a>
- <br>
- <template v-if="hasPasswordless">
- <div v-if="countAlternativeLogins"
- class="alternative-logins">
- <a v-if="hasPasswordless"
- class="button"
- :class="{ 'single-alt-login-option': countAlternativeLogins }"
+ <div id="login" class="guest-box">
+ <div v-if="!hideLoginForm || directLogin">
+ <transition name="fade" mode="out-in">
+ <div v-if="!passwordlessLogin && !resetPassword && resetPasswordTarget === ''">
+ <LoginForm :username.sync="user"
+ :redirect-url="redirectUrl"
+ :direct-login="directLogin"
+ :messages="messages"
+ :errors="errors"
+ :throttle-delay="throttleDelay"
+ :auto-complete-allowed="autoCompleteAllowed"
+ @submit="loading = true" />
+ <a v-if="canResetPassword && resetPasswordLink !== ''"
+ id="lost-password"
+ :href="resetPasswordLink">
+ {{ t('core', 'Forgot password?') }}
+ </a>
+ <a v-else-if="canResetPassword && !resetPassword"
+ id="lost-password"
+ :href="resetPasswordLink"
+ @click.prevent="resetPassword = true">
+ {{ t('core', 'Forgot password?') }}
+ </a>
+ <br>
+ <template v-if="hasPasswordless">
+ <div v-if="countAlternativeLogins"
+ class="alternative-logins">
+ <a v-if="hasPasswordless"
+ class="button"
+ :class="{ 'single-alt-login-option': countAlternativeLogins }"
+ href="#"
+ @click.prevent="passwordlessLogin = true">
+ {{ t('core', 'Log in with a device') }}
+ </a>
+ </div>
+ <a v-else
href="#"
@click.prevent="passwordlessLogin = true">
{{ t('core', 'Log in with a device') }}
</a>
- </div>
- <a v-else
- href="#"
- @click.prevent="passwordlessLogin = true">
- {{ t('core', 'Log in with a device') }}
+ </template>
+ </div>
+ <div v-else-if="!loading && passwordlessLogin"
+ key="reset"
+ class="login-additional">
+ <PasswordLessLoginForm :username.sync="user"
+ :redirect-url="redirectUrl"
+ :auto-complete-allowed="autoCompleteAllowed"
+ :is-https="isHttps"
+ :is-localhost="isLocalhost"
+ :has-public-key-credential="hasPublicKeyCredential"
+ @submit="loading = true" />
+ <a href="#" @click.prevent="passwordlessLogin = false">
+ {{ t('core', 'Back') }}
</a>
- </template>
- </div>
- <div v-else-if="!loading && passwordlessLogin"
- key="reset"
- class="login-additional">
- <PasswordLessLoginForm :username.sync="user"
- :redirect-url="redirectUrl"
- :auto-complete-allowed="autoCompleteAllowed"
- :is-https="isHttps"
- :is-localhost="isLocalhost"
- :has-public-key-credential="hasPublicKeyCredential"
- @submit="loading = true" />
- <a href="#" @click.prevent="passwordlessLogin = false">
- {{ t('core', 'Back') }}
- </a>
- </div>
- <div v-else-if="!loading && canResetPassword"
- key="reset"
- class="login-additional">
- <div class="lost-password-container">
- <ResetPassword v-if="resetPassword"
- :username.sync="user"
- :reset-password-link="resetPasswordLink"
- @abort="resetPassword = false" />
</div>
- </div>
- <div v-else-if="resetPasswordTarget !== ''">
- <UpdatePassword :username.sync="user"
- :reset-password-target="resetPasswordTarget"
- @done="passwordResetFinished" />
- </div>
- </transition>
- </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>
- </transition>
+ <div v-else-if="!loading && canResetPassword"
+ key="reset"
+ class="login-additional">
+ <div class="lost-password-container">
+ <ResetPassword v-if="resetPassword"
+ :username.sync="user"
+ :reset-password-link="resetPasswordLink"
+ @abort="resetPassword = false" />
+ </div>
+ </div>
+ <div v-else-if="resetPasswordTarget !== ''">
+ <UpdatePassword :username.sync="user"
+ :reset-password-target="resetPasswordTarget"
+ @done="passwordResetFinished" />
+ </div>
+ </transition>
+ </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>
+ </transition>
+ </div>
+
+ <div id="alternative-logins" class="alternative-logins">
+ <Button v-for="(alternativeLogin, index) in alternativeLogins"
+ :key="index"
+ type="primary"
+ :wide="true"
+ :class="[alternativeLogin.class]"
+ role="link"
+ :href="alternativeLogin.href"
+ @click="goTo(alternativeLogin.href)">
+ {{ alternativeLogin.name }}
+ </Button>
+ </div>
</div>
</template>
@@ -112,6 +127,7 @@ 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 Button from '@nextcloud/vue/dist/Components/Button'
const query = queryString.parse(location.search)
if (query.clear === '1') {
@@ -132,6 +148,7 @@ export default {
PasswordLessLoginForm,
ResetPassword,
UpdatePassword,
+ Button,
},
data() {
@@ -154,6 +171,7 @@ export default {
directLogin: query.direct === '1',
hasPasswordless: loadState('core', 'webauthn-available', false),
countAlternativeLogins: loadState('core', 'countAlternativeLogins', false),
+ alternativeLogins: loadState('core', 'alternativeLogins', []),
isHttps: window.location.protocol === 'https:',
isLocalhost: window.location.hostname === 'localhost',
hasPublicKeyCredential: typeof (window.PublicKeyCredential) !== 'undefined',
@@ -166,15 +184,36 @@ export default {
this.resetPasswordTarget = ''
this.directLogin = true
},
+ goTo(href) {
+ window.location.href = href
+ },
},
}
</script>
-<style>
+<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;
}
+
+ #lost-password {
+ padding: 4px;
+ margin: 8px;
+ border-radius: var(--border-radius);
+ }
+
+ .alternative-logins button {
+ margin-top: 12px;
+ margin-bottom: 12px;
+ &:first-child {
+ margin-top: 0;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
</style>