UX: improve layout and styles for solo preferences (#21094)
This commit is contained in:
parent
1ee87cbfa3
commit
4eb7d2d79b
|
@ -6,23 +6,25 @@
|
|||
readonly
|
||||
>{{this.formattedBackupCodes}}</textarea>
|
||||
|
||||
<DButton
|
||||
@action={{action "copyToClipboard"}}
|
||||
@class="backup-codes-copy-btn"
|
||||
@icon="copy"
|
||||
@aria-label="user.second_factor_backup.copy_to_clipboard"
|
||||
@title="user.second_factor_backup.copy_to_clipboard"
|
||||
/>
|
||||
<div class="controls">
|
||||
<DButton
|
||||
@action={{action "copyToClipboard"}}
|
||||
@class="btn-default backup-codes-copy-btn"
|
||||
@icon="copy"
|
||||
@aria-label="user.second_factor_backup.copy_to_clipboard"
|
||||
@title="user.second_factor_backup.copy_to_clipboard"
|
||||
/>
|
||||
|
||||
<a
|
||||
download="{{this.siteTitleSlug}}-backup-codes.txt"
|
||||
class="btn no-text btn-icon backup-codes-download-btn"
|
||||
aria-label={{i18n "user.second_factor_backup.download_backup_codes"}}
|
||||
title={{i18n "user.second_factor_backup.download_backup_codes"}}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="data:application/octet-stream;charset=utf-8;base64,{{this.base64BackupCode}}"
|
||||
>
|
||||
{{d-icon "download"}}
|
||||
</a>
|
||||
<a
|
||||
download="{{this.siteTitleSlug}}-backup-codes.txt"
|
||||
class="btn btn-default no-text btn-icon backup-codes-download-btn"
|
||||
aria-label={{i18n "user.second_factor_backup.download_backup_codes"}}
|
||||
title={{i18n "user.second_factor_backup.download_backup_codes"}}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="data:application/octet-stream;charset=utf-8;base64,{{this.base64BackupCode}}"
|
||||
>
|
||||
{{d-icon "download"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,43 @@
|
|||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
import I18n from "I18n";
|
||||
import { computed } from "@ember/object";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: ["security-key-dropdown"],
|
||||
|
||||
selectKitOptions: {
|
||||
icon: "wrench",
|
||||
showFullTitle: false,
|
||||
},
|
||||
|
||||
content: computed(function () {
|
||||
const content = [];
|
||||
|
||||
content.push({
|
||||
id: "edit",
|
||||
icon: "pencil-alt",
|
||||
name: I18n.t("user.second_factor.edit"),
|
||||
});
|
||||
|
||||
content.push({
|
||||
id: "disable",
|
||||
icon: "trash-alt",
|
||||
name: I18n.t("user.second_factor.disable"),
|
||||
});
|
||||
|
||||
return content;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onChange(id) {
|
||||
switch (id) {
|
||||
case "edit":
|
||||
this.editSecurityKey(this.securityKey);
|
||||
break;
|
||||
case "disable":
|
||||
this.disableSingleSecondFactor(this.securityKey);
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
import I18n from "I18n";
|
||||
import { computed } from "@ember/object";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: ["token-based-auth-dropdown"],
|
||||
|
||||
selectKitOptions: {
|
||||
icon: "wrench",
|
||||
showFullTitle: false,
|
||||
},
|
||||
|
||||
content: computed(function () {
|
||||
return [
|
||||
{
|
||||
id: "edit",
|
||||
icon: "pencil-alt",
|
||||
name: I18n.t("user.second_factor.edit"),
|
||||
},
|
||||
{
|
||||
id: "disable",
|
||||
icon: "trash-alt",
|
||||
name: I18n.t("user.second_factor.disable"),
|
||||
},
|
||||
];
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onChange(id) {
|
||||
switch (id) {
|
||||
case "edit":
|
||||
this.editSecondFactor(this.totp);
|
||||
break;
|
||||
case "disable":
|
||||
this.disableSingleSecondFactor(this.totp);
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
import I18n from "I18n";
|
||||
import { computed } from "@ember/object";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: ["two-factor-backup-dropdown"],
|
||||
|
||||
selectKitOptions: {
|
||||
icon: "wrench",
|
||||
showFullTitle: false,
|
||||
},
|
||||
|
||||
content: computed(function () {
|
||||
const content = [];
|
||||
|
||||
content.push({
|
||||
id: "edit",
|
||||
icon: "pencil-alt",
|
||||
name: I18n.t("user.second_factor.edit"),
|
||||
});
|
||||
|
||||
if (this.secondFactorBackupEnabled) {
|
||||
content.push({
|
||||
id: "disable",
|
||||
icon: "trash-alt",
|
||||
name: I18n.t("user.second_factor.disable"),
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onChange(id) {
|
||||
switch (id) {
|
||||
case "edit":
|
||||
this.editSecondFactorBackup();
|
||||
break;
|
||||
case "disable":
|
||||
this.disableSecondFactorBackup();
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,64 +1,61 @@
|
|||
<DModalBody>
|
||||
<section
|
||||
class="user-content user-preferences solo-preference second-factor-backup-preferences"
|
||||
>
|
||||
<form class="form-horizontal">
|
||||
{{#if this.successMessage}}
|
||||
<div class="alert alert-success">
|
||||
{{this.successMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if this.successMessage}}
|
||||
<div class="alert alert-success">
|
||||
{{this.successMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.errorMessage}}
|
||||
<div class="alert alert-error">
|
||||
{{this.errorMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if this.errorMessage}}
|
||||
<div class="alert alert-error">
|
||||
{{this.errorMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.backupEnabled}}
|
||||
{{html-safe
|
||||
(i18n
|
||||
"user.second_factor_backup.remaining_codes"
|
||||
count=this.remainingCodes
|
||||
)
|
||||
}}
|
||||
{{/if}}
|
||||
<ConditionalLoadingSection @isLoading={{this.loading}}>
|
||||
{{#if this.backupCodes}}
|
||||
<h3>{{i18n "user.second_factor_backup.codes.title"}}</h3>
|
||||
|
||||
<div class="actions">
|
||||
{{#if this.backupEnabled}}
|
||||
<DButton
|
||||
@class="btn-primary"
|
||||
@icon="redo"
|
||||
@action={{action "generateSecondFactorCodes"}}
|
||||
@type="submit"
|
||||
@isLoading={{this.loading}}
|
||||
@label="user.second_factor_backup.regenerate"
|
||||
/>
|
||||
{{else}}
|
||||
<DButton
|
||||
@class="btn-primary"
|
||||
@action={{action "generateSecondFactorCodes"}}
|
||||
@type="submit"
|
||||
@disabled={{this.loading}}
|
||||
@label="user.second_factor_backup.enable"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
<p>
|
||||
{{i18n "user.second_factor_backup.codes.description"}}
|
||||
</p>
|
||||
|
||||
<ConditionalLoadingSection @isLoading={{this.loading}}>
|
||||
{{#if this.backupCodes}}
|
||||
<h3>{{i18n "user.second_factor_backup.codes.title"}}</h3>
|
||||
<BackupCodes
|
||||
@copyBackupCode={{action "copyBackupCode"}}
|
||||
@backupCodes={{this.backupCodes}}
|
||||
/>
|
||||
{{/if}}
|
||||
</ConditionalLoadingSection>
|
||||
|
||||
<p>
|
||||
{{i18n "user.second_factor_backup.codes.description"}}
|
||||
</p>
|
||||
{{#if this.backupEnabled}}
|
||||
{{html-safe
|
||||
(i18n
|
||||
"user.second_factor_backup.remaining_codes" count=this.remainingCodes
|
||||
)
|
||||
}}
|
||||
{{else}}
|
||||
{{html-safe (i18n "user.second_factor_backup.not_enabled")}}
|
||||
{{/if}}
|
||||
</DModalBody>
|
||||
|
||||
<BackupCodes
|
||||
@copyBackupCode={{action "copyBackupCode"}}
|
||||
@backupCodes={{this.backupCodes}}
|
||||
/>
|
||||
{{/if}}
|
||||
</ConditionalLoadingSection>
|
||||
</form>
|
||||
</section>
|
||||
</DModalBody>
|
||||
<div class="modal-footer">
|
||||
<div class="actions">
|
||||
{{#if this.backupEnabled}}
|
||||
<DButton
|
||||
@class="btn-primary"
|
||||
@icon="redo"
|
||||
@action={{action "generateSecondFactorCodes"}}
|
||||
@type="submit"
|
||||
@isLoading={{this.loading}}
|
||||
@label="user.second_factor_backup.regenerate"
|
||||
/>
|
||||
{{else}}
|
||||
<DButton
|
||||
@class="btn-primary"
|
||||
@action={{action "generateSecondFactorCodes"}}
|
||||
@type="submit"
|
||||
@disabled={{this.loading}}
|
||||
@label="user.second_factor_backup.enable"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,16 +1,10 @@
|
|||
<DModalBody>
|
||||
<form class="form-horizontal">
|
||||
<div class="input-group">
|
||||
<label for="authenticator-name">{{i18n
|
||||
"user.second_factor.edit_description"
|
||||
}}</label>
|
||||
<Input
|
||||
name="authenticator-name"
|
||||
@type="text"
|
||||
@value={{this.model.name}}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div class="input-group">
|
||||
<label for="authenticator-name">{{i18n
|
||||
"user.second_factor.edit_description"
|
||||
}}</label>
|
||||
<Input name="authenticator-name" @type="text" @value={{this.model.name}} />
|
||||
</div>
|
||||
</DModalBody>
|
||||
|
||||
<div class="modal-footer">
|
||||
|
|
|
@ -1,34 +1,23 @@
|
|||
<DSection @pageClass="user-preferences" @tagName="">
|
||||
<section class="user-content user-preferences solo-preference">
|
||||
<form class="form-horizontal">
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<h3>{{i18n
|
||||
(if this.new "user.add_email.title" "user.change_email.title")
|
||||
}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form-vertical">
|
||||
{{#if this.success}}
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<div class="instructions">
|
||||
<p>{{this.successMessage}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-success">{{this.successMessage}}</div>
|
||||
<LinkTo @route="preferences.account" class="success-back">
|
||||
{{d-icon "arrow-left"}}
|
||||
{{i18n "user.change_email.back_to_preferences"}}
|
||||
</LinkTo>
|
||||
|
||||
{{else}}
|
||||
{{#if this.error}}
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<div class="alert alert-error">{{this.errorMessage}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-error">{{this.errorMessage}}</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{i18n "user.email.title"}}</label>
|
||||
<label class="control-label">
|
||||
{{i18n
|
||||
(if this.new "user.add_email.title" "user.change_email.title")
|
||||
}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<TextField
|
||||
@value={{this.newEmail}}
|
||||
|
@ -36,9 +25,6 @@
|
|||
@classNames="input-xxlarge"
|
||||
@autofocus="autofocus"
|
||||
/>
|
||||
<InputTip @validation={{this.emailValidation}} />
|
||||
</div>
|
||||
<div class="controls">
|
||||
<div class="instructions">
|
||||
{{#if this.taken}}
|
||||
{{i18n "user.change_email.taken"}}
|
||||
|
@ -46,22 +32,23 @@
|
|||
{{i18n "user.email.instructions"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<InputTip @validation={{this.emailValidation}} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<DButton
|
||||
@class="btn-primary"
|
||||
@action={{action "saveEmail"}}
|
||||
@type="submit"
|
||||
@disabled={{this.saveDisabled}}
|
||||
@translatedLabel={{this.saveButtonText}}
|
||||
/>
|
||||
</div>
|
||||
<div class="controls save-button">
|
||||
<DButton
|
||||
@class="btn-primary"
|
||||
@action={{action "saveEmail"}}
|
||||
@type="submit"
|
||||
@disabled={{this.saveDisabled}}
|
||||
@translatedLabel={{this.saveButtonText}}
|
||||
/>
|
||||
<CancelLink
|
||||
@route="preferences.account"
|
||||
@args={{this.model.username}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
</form>
|
||||
</section>
|
||||
</DSection>
|
|
@ -1,31 +1,21 @@
|
|||
<DSection @pageClass="user-preferences" @tagName="">
|
||||
<section class="user-content user-preferences solo-preference second-factor">
|
||||
<ConditionalLoadingSpinner @condition={{this.loading}}>
|
||||
<form class="form-horizontal">
|
||||
<form class="form-vertical">
|
||||
{{#if this.showEnforcedNotice}}
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<div class="alert alert-error">{{i18n
|
||||
"user.second_factor.enforced_notice"
|
||||
}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-error">{{i18n
|
||||
"user.second_factor.enforced_notice"
|
||||
}}</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.displayOAuthWarning}}
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{i18n "user.second_factor.oauth_enabled_warning"}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-warning">{{i18n
|
||||
"user.second_factor.oauth_enabled_warning"
|
||||
}}</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.errorMessage}}
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<div class="alert alert-error">{{this.errorMessage}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-error">{{this.errorMessage}}</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.loaded}}
|
||||
|
@ -41,23 +31,14 @@
|
|||
{{i18n "user.second_factor.totp.default_name"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.isCurrentUser}}
|
||||
<div class="actions">
|
||||
<DButton
|
||||
@action={{action "editSecondFactor" totp}}
|
||||
@class="btn-default btn-flat btn-small btn-icon pad-left no-text edit"
|
||||
@disabled={{this.loading}}
|
||||
@icon="pencil-alt"
|
||||
@aria-label="user.second_factor.edit"
|
||||
@title="user.second_factor.edit"
|
||||
/>
|
||||
<DButton
|
||||
@action={{action "disableSingleSecondFactor" totp}}
|
||||
@class="btn-danger btn-flat no-text"
|
||||
@icon="trash-alt"
|
||||
@aria-label="user.second_factor.disable"
|
||||
@title="user.second_factor.disable"
|
||||
<TokenBasedAuthDropdown
|
||||
@totp={{totp}}
|
||||
@editSecondFactor={{action "editSecondFactor"}}
|
||||
@disableSingleSecondFactor={{action
|
||||
"disableSingleSecondFactor"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@ -65,7 +46,7 @@
|
|||
{{/each}}
|
||||
<DButton
|
||||
@action={{action "createTotp"}}
|
||||
@class="btn-primary new-totp"
|
||||
@class="btn-default new-totp"
|
||||
@icon="plus"
|
||||
@disabled={{this.loading}}
|
||||
@label="user.second_factor.totp.add"
|
||||
|
@ -88,23 +69,12 @@
|
|||
|
||||
{{#if this.isCurrentUser}}
|
||||
<div class="actions">
|
||||
<DButton
|
||||
@action={{action "editSecurityKey" security_key}}
|
||||
@class="btn-default btn-flat btn-small btn-icon pad-left no-text edit"
|
||||
@disabled={{this.loading}}
|
||||
@icon="pencil-alt"
|
||||
@aria-label="user.second_factor.edit"
|
||||
@title="user.second_factor.edit"
|
||||
/>
|
||||
<DButton
|
||||
@action={{action
|
||||
<SecurityKeyDropdown
|
||||
@securityKey={{security_key}}
|
||||
@editSecurityKey={{action "editSecurityKey"}}
|
||||
@disableSingleSecondFactor={{action
|
||||
"disableSingleSecondFactor"
|
||||
security_key
|
||||
}}
|
||||
@class="btn-danger btn-flat no-text"
|
||||
@icon="trash-alt"
|
||||
@aria-label="user.second_factor.disable"
|
||||
@title="user.second_factor.disable"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@ -112,7 +82,7 @@
|
|||
{{/each}}
|
||||
<DButton
|
||||
@action={{action "createSecurityKey"}}
|
||||
@class="btn-primary new-security-key"
|
||||
@class="btn-default new-security-key"
|
||||
@icon="plus"
|
||||
@disabled={{this.loading}}
|
||||
@label="user.second_factor.security_key.add"
|
||||
|
@ -134,32 +104,34 @@
|
|||
)
|
||||
}}
|
||||
{{else}}
|
||||
{{i18n "user.second_factor_backup.enable_long"}}
|
||||
<DButton
|
||||
@action={{action "editSecondFactorBackup"}}
|
||||
@class="btn-default new-second-factor-backup"
|
||||
@icon="plus"
|
||||
@disabled={{this.loading}}
|
||||
@label="user.second_factor_backup.enable_long"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.isCurrentUser}}
|
||||
{{#if
|
||||
(and
|
||||
this.model.second_factor_backup_enabled this.isCurrentUser
|
||||
)
|
||||
}}
|
||||
<div class="actions">
|
||||
<DButton
|
||||
@action={{action "editSecondFactorBackup"}}
|
||||
@class="btn-default btn-flat btn-small btn-icon pad-left no-text edit edit-2fa-backup"
|
||||
@disabled={{this.loading}}
|
||||
@icon="pencil-alt"
|
||||
@aria-label="user.second_factor.edit"
|
||||
@title="user.second_factor.edit"
|
||||
<TwoFactorBackupDropdown
|
||||
@secondFactorBackupEnabled={{this.model.second_factor_backup_enabled}}
|
||||
@editSecondFactorBackup={{action
|
||||
"editSecondFactorBackup"
|
||||
}}
|
||||
@disableSecondFactorBackup={{action
|
||||
"disableSecondFactorBackup"
|
||||
}}
|
||||
/>
|
||||
|
||||
{{#if this.model.second_factor_backup_enabled}}
|
||||
<DButton
|
||||
@action={{action "disableSecondFactorBackup"}}
|
||||
@class="btn-danger btn-flat no-text"
|
||||
@icon="trash-alt"
|
||||
@aria-label="user.second_factor.disable"
|
||||
@title="user.second_factor.disable"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{else}}
|
||||
{{i18n "user.second_factor_backup.enable_prerequisites"}}
|
||||
{{/if}}
|
||||
|
@ -170,7 +142,7 @@
|
|||
{{#if this.model.second_factor_enabled}}
|
||||
{{#unless this.showEnforcedNotice}}
|
||||
<div class="control-group pref-second-factor-disable-all">
|
||||
<div class="controls">
|
||||
<div class="controls -actions">
|
||||
<DButton
|
||||
@class="btn-danger"
|
||||
@icon="ban"
|
||||
|
@ -178,6 +150,10 @@
|
|||
@disabled={{this.loading}}
|
||||
@label="user.second_factor.disable_all"
|
||||
/>
|
||||
<CancelLink
|
||||
@route="preferences.security"
|
||||
@args={{this.model.username}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
|
|
@ -32,14 +32,14 @@ acceptance("User Preferences - Second Factor Backup", function (needs) {
|
|||
test("second factor backup", async function (assert) {
|
||||
updateCurrentUser({ second_factor_enabled: true });
|
||||
await visit("/u/eviltrout/preferences/second-factor");
|
||||
await click(".edit-2fa-backup");
|
||||
await click(".new-second-factor-backup");
|
||||
|
||||
assert.ok(
|
||||
exists(".second-factor-backup-preferences"),
|
||||
exists(".second-factor-backup-edit-modal"),
|
||||
"shows the 2fa backup panel"
|
||||
);
|
||||
|
||||
await click(".second-factor-backup-preferences .btn-primary");
|
||||
await click(".second-factor-backup-edit-modal .btn-primary");
|
||||
|
||||
assert.ok(exists(".backup-codes-area"), "shows backup codes");
|
||||
});
|
||||
|
@ -47,10 +47,16 @@ acceptance("User Preferences - Second Factor Backup", function (needs) {
|
|||
test("delete backup codes", async function (assert) {
|
||||
updateCurrentUser({ second_factor_enabled: true });
|
||||
await visit("/u/eviltrout/preferences/second-factor");
|
||||
await click(".edit-2fa-backup");
|
||||
await click(".second-factor-backup-preferences .btn-primary");
|
||||
await click(".modal-close");
|
||||
await click(".pref-second-factor-backup .btn-danger");
|
||||
|
||||
if (exists(".new-second-factor-backup")) {
|
||||
// if codes don't exist yet, create them
|
||||
await click(".new-second-factor-backup");
|
||||
await click(".second-factor-backup-edit-modal .btn-primary");
|
||||
await click(".modal-close");
|
||||
}
|
||||
|
||||
await click(".two-factor-backup-dropdown .select-kit-header");
|
||||
await click("li[data-name='Disable'");
|
||||
assert.strictEqual(
|
||||
query("#dialog-title").innerText.trim(),
|
||||
"Deleting backup codes"
|
||||
|
|
|
@ -108,7 +108,9 @@ acceptance("User Preferences - Second Factor", function (needs) {
|
|||
|
||||
await fillIn("#password", "secrets");
|
||||
await click(".user-preferences .btn-primary");
|
||||
await click(".totp .btn-danger");
|
||||
await click(".token-based-auth-dropdown .select-kit-header");
|
||||
await click("li[data-name='Disable']");
|
||||
|
||||
assert.strictEqual(
|
||||
query("#dialog-title").innerText.trim(),
|
||||
"Deleting an authenticator"
|
||||
|
@ -120,7 +122,9 @@ acceptance("User Preferences - Second Factor", function (needs) {
|
|||
"User has a physical security key"
|
||||
);
|
||||
|
||||
await click(".security-key .btn-danger");
|
||||
await click(".security-key-dropdown .select-kit-header");
|
||||
await click("li[data-name='Disable'");
|
||||
|
||||
assert.strictEqual(
|
||||
query("#dialog-title").innerText.trim(),
|
||||
"Deleting an authenticator"
|
||||
|
|
|
@ -570,6 +570,7 @@ table {
|
|||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
flex: 0 0 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
|
|
|
@ -580,59 +580,6 @@
|
|||
.second-factor-token-input {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.form-horizontal {
|
||||
.instructions {
|
||||
margin-left: 0;
|
||||
}
|
||||
.actions {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.backup-codes {
|
||||
margin: 2em 0;
|
||||
|
||||
.wrapper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--primary-low);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.backup-codes-area {
|
||||
resize: none;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background: var(--secondary);
|
||||
border: 0;
|
||||
cursor: auto;
|
||||
outline: none;
|
||||
font-family: monospace;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
border-color: var(--primary-low);
|
||||
}
|
||||
}
|
||||
|
||||
.backup-codes-copy-btn,
|
||||
.backup-codes-download-btn {
|
||||
right: 5px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.backup-codes-copy-btn {
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.backup-codes-download-btn {
|
||||
top: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pref-associated-accounts table {
|
||||
|
@ -661,9 +608,16 @@
|
|||
}
|
||||
|
||||
.instructions {
|
||||
clear: both;
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
display: block;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.success-back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.d-icon {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin inactiveMode() {
|
||||
|
@ -687,6 +641,11 @@
|
|||
.undo-preview {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.paginated-topics-list {
|
||||
|
@ -729,30 +688,77 @@
|
|||
}
|
||||
|
||||
.second-factor {
|
||||
&.instructions {
|
||||
color: var(--primary-medium);
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
font-size: var(--font-down-1);
|
||||
}
|
||||
.second-factor-item {
|
||||
margin-top: 0.75em;
|
||||
width: 500px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 5px 0px;
|
||||
border-top: 1px solid var(--primary-low);
|
||||
margin: 0.25em 0;
|
||||
padding: 0.25em 0;
|
||||
align-items: center;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.btn-danger .d-icon {
|
||||
color: var(--danger);
|
||||
.select-kit {
|
||||
.select-kit-header {
|
||||
background: transparent;
|
||||
&:hover .d-icon {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-expanded {
|
||||
.select-kit-header .d-icon {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn.edit {
|
||||
min-height: auto;
|
||||
|
||||
.-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.d-modal[class*="second-factor-"] {
|
||||
.modal-inner-container {
|
||||
max-width: 24em;
|
||||
}
|
||||
}
|
||||
|
||||
.backup-codes {
|
||||
margin: 1em 0;
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
border: 1px solid var(--primary-low);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea.backup-codes-area {
|
||||
flex: 1 1 100%;
|
||||
height: 100%;
|
||||
resize: none;
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
height: auto;
|
||||
background: var(--secondary);
|
||||
border: 0;
|
||||
cursor: auto;
|
||||
outline: none;
|
||||
font-family: monospace;
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
border-color: var(--primary-low);
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
padding: 0.5em;
|
||||
flex: 1 1 2em;
|
||||
margin-left: auto;
|
||||
.btn {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,11 +156,6 @@ input {
|
|||
|
||||
.controls {
|
||||
margin-left: 160px;
|
||||
|
||||
&.-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -330,6 +330,10 @@
|
|||
.apps .controls button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#change-email {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.user-right {
|
||||
|
|
|
@ -1419,11 +1419,12 @@ en:
|
|||
title: "Two-Factor Backup Codes"
|
||||
regenerate: "Regenerate"
|
||||
disable: "Disable"
|
||||
enable: "Enable"
|
||||
enable_long: "Enable backup codes"
|
||||
enable: "Create backup codes"
|
||||
enable_long: "Add backup codes"
|
||||
not_enabled: "You haven't created any backup codes yet."
|
||||
manage:
|
||||
one: "Manage backup codes. You have <strong>%{count}</strong> backup code remaining."
|
||||
other: "Manage backup codes. You have <strong>%{count}</strong> backup codes remaining."
|
||||
one: "You have <strong>%{count}</strong> backup code remaining."
|
||||
other: "You have <strong>%{count}</strong> backup codes remaining."
|
||||
copy_to_clipboard: "Copy to Clipboard"
|
||||
copy_to_clipboard_error: "Error copying data to Clipboard"
|
||||
copied_to_clipboard: "Copied to Clipboard"
|
||||
|
@ -1512,6 +1513,7 @@ en:
|
|||
success: "We've sent an email to that address. Please follow the confirmation instructions."
|
||||
success_via_admin: "We've sent an email to that address. The user will need to follow the confirmation instructions in the email."
|
||||
success_staff: "We've sent an email to your current address. Please follow the confirmation instructions."
|
||||
back_to_preferences: "Back to preferences"
|
||||
|
||||
change_avatar:
|
||||
title: "Change your profile picture"
|
||||
|
|
Loading…
Reference in New Issue