Files
cloudron-box/dashboard/public/views/profile.html
2024-10-15 18:46:51 +02:00

543 lines
37 KiB
HTML

<!-- Modal change avatar -->
<div class="modal fade" id="avatarChangeModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.changeAvatar.title' | tr }}</h4>
</div>
<div class="modal-body settings-avatar-selector">
<div style="margin: auto; text-align: left">
<div class="radio">
<label>
<input type="radio" name="avatarType" ng-model="avatarChange.type" value="">
{{ 'profile.changeAvatar.noAvatar' | tr }}
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="avatarType" ng-model="avatarChange.type" value="gravatar">
<span ng-bind-html="'profile.changeAvatar.useGravatar' | tr:{ gravatarLink: 'https://gravatar.com/' }"></span>
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="avatarType" ng-model="avatarChange.type" value="custom">
{{ 'profile.changeAvatar.useCustomPicture' | tr }}
</label>
</div>
</div>
<div ng-show="avatarChange.type === 'custom'" class="preview-avatar">
<img id="previewAvatar" width="128" height="128" class="copy" ng-click="avatarChange.showCustomAvatarSelector()"/>
<input type="file" id="avatarFileInput" style="display: none" accept="image/*"/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="avatarChange.doChangeAvatar()" ng-disabled="avatarChange.busy || (avatarChange.typeOrig === avatarChange.type && !avatarChange.pictureChanged) || (avatarChange.type === 'custom' && !avatarChange.pictureChanged)"><i class="fa fa-circle-notch fa-spin" ng-show="avatarChange.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal change backgroundImage -->
<div class="modal fade" id="backgroundImageChangeModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.changeBackgroundImage.title' | tr }}</h4>
</div>
<div class="modal-body settings-avatar-selector">
<div class="preview-avatar">
<img id="previewBackgroundImage" width="100%" height="400px" class="copy" style="object-fit: cover;" ng-click="backgroundImageChange.showCustomBackgroundImageSelector()"/>
<input type="file" id="backgroundImageFileInput" style="display: none" accept="image/png, image/jpeg"/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger pull-left" ng-click="backgroundImageChange.unset()" ng-disabled="backgroundImageChange.busy">Remove Background Image</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="backgroundImageChange.submit()" ng-disabled="backgroundImageChange.busy || !backgroundImageChange.pictureChanged"><i class="fa fa-circle-notch fa-spin" ng-show="backgroundImageChange.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal change password -->
<div class="modal fade" id="passwordChangeModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.changePassword.title' | tr }}</h4>
</div>
<div class="modal-body">
<form name="passwordChangeForm" role="form" novalidate ng-submit="passwordchange.submit()" autocomplete="off">
<input type="password" style="display: none;">
<div class="form-group" ng-class="{ 'has-error': (!passwordChangeForm.password.$dirty && passwordchange.error.password) || (passwordChangeForm.password.$dirty && passwordChangeForm.password.$invalid) }">
<label class="control-label" for="inputPasswordChangePassword">{{ 'profile.changePassword.currentPassword' | tr }}</label>
<div class="control-label" ng-show="(!passwordChangeForm.password.$dirty && passwordchange.error.password) || (passwordChangeForm.password.$dirty && passwordChangeForm.password.$invalid)">
<small ng-show="!passwordChangeForm.password.$dirty && passwordchange.error.password">Wrong password</small>
<small ng-show="passwordChangeForm.password.$dirty && passwordChangeForm.password.$error.required">A password is required</small>
</div>
<input type="password" class="form-control" ng-model="passwordchange.password" id="inputPasswordChangePassword" name="password" required autofocus password-reveal>
</div>
<div class="form-group" ng-class="{ 'has-error': (!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword) || (passwordChangeForm.newPassword.$dirty && passwordChangeForm.newPassword.$invalid) }">
<label class="control-label" for="inputPasswordChangeNewPassword">{{ 'profile.changePassword.newPassword' | tr }}</label>
<div class="control-label" ng-show="(!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword) || (passwordChangeForm.newPassword.$dirty && passwordChangeForm.newPassword.$invalid)">
<small ng-show="!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword">{{ passwordchange.error.newPassword }}<br/><br/></small>
<small ng-show=" passwordChangeForm.newPassword.$dirty && passwordChangeForm.newPassword.$invalid">{{ 'profile.changePassword.errorPasswordInvalid' | tr }}</small>
</div>
<input type="password" class="form-control" ng-model="passwordchange.newPassword" id="inputPasswordChangeNewPassword" name="newPassword" ng-minlength="8" ng-maxlength="256" required autofocus password-reveal>
</div>
<div class="form-group" ng-class="{ 'has-error': (!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword) || (passwordChangeForm.newPasswordRepeat.$dirty && passwordChangeForm.newPasswordRepeat.$error.required) || (passwordChangeForm.newPasswordRepeat.$dirty && passwordchange.newPassword !== passwordchange.newPasswordRepeat) }">
<label class="control-label" for="inputPasswordChangeNewPasswordRepeat">{{ 'profile.changePassword.newPasswordRepeat' | tr }}</label>
<div class="control-label" ng-show="(!passwordChangeForm.newPassword.$dirty && passwordchange.error.newPassword) || (passwordChangeForm.newPasswordRepeat.$dirty && passwordChangeForm.newPasswordRepeat.$error.required) || (passwordChangeForm.newPasswordRepeat.$dirty && passwordchange.newPassword !== passwordchange.newPasswordRepeat)">
<small ng-show="passwordChangeForm.newPasswordRepeat.$dirty && passwordChangeForm.newPasswordRepeat.$error.required">{{ 'profile.changePassword.errorPasswordRequired' | tr }}</small>
<small ng-show="passwordChangeForm.newPasswordRepeat.$dirty && passwordchange.newPassword !== passwordchange.newPasswordRepeat && passwordchange.newPasswordRepeat">{{ 'profile.changePassword.errorPasswordsDontMatch' | tr }}</small>
</div>
<input type="password" class="form-control" ng-model="passwordchange.newPasswordRepeat" id="inputPasswordChangeNewPasswordRepeat" name="newPasswordRepeat" required autofocus password-reveal>
</div>
<input class="ng-hide" type="submit" ng-disabled="passwordChangeForm.$invalid"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="passwordchange.submit()" ng-disabled="passwordChangeForm.$invalid || passwordchange.busy"><i class="fa fa-circle-notch fa-spin" ng-show="passwordchange.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal change email -->
<div class="modal fade" id="emailChangeModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.changeEmail.title' | tr }}</h4>
</div>
<div class="modal-body">
<form name="emailChangeForm" role="form" novalidate ng-submit="emailChange.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (emailChangeForm.email.$dirty && emailChangeForm.email.$invalid) || (!emailChangeForm.email.$dirty && emailChange.error.email)}">
<label class="control-label" for="inputEmailChangeEmail">{{ 'profile.changeEmail.email' | tr }}</label>
<input type="email" class="form-control" ng-model="emailChange.email" id="inputEmailChangeEmail" name="email" required autofocus>
<div class="control-label" ng-show="(!emailChangeForm.email.$dirty && emailChange.error.email) || (emailChangeForm.email.$dirty && emailChangeForm.email.$invalid)">
<small ng-show="emailChangeForm.email.$error.required">{{ 'profile.changeEmail.errorEmailRequired' | tr }}</small>
<small ng-show="(emailChangeForm.email.$dirty && emailChangeForm.email.$invalid) && !emailChangeForm.email.$error.required">{{ 'profile.changeEmail.errorEmailInvalid' | tr }}</small>
<small ng-show="!emailChangeForm.email.$dirty && emailChange.error.email">{{ emailChange.error.email }}</small>
</div>
</div>
<div class="form-group" ng-class="{ 'has-error': (emailChange.error.password && !emailChangeForm.password.$dirty) }">
<label class="control-label" for="inputEmailChangePassword">{{ 'profile.changeEmail.password' | tr }}</label>
<input type="password" class="form-control" ng-model="emailChange.password" id="inputEmailChangePassword" name="password" required autofocus password-reveal>
<div class="control-label" ng-show="emailChange.error.password && !emailChangeForm.password.$dirty">
<small ng-show="emailChange.error.password">{{ 'profile.changeEmail.errorWrongPassword' | tr }}</small>
</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="emailChangeForm.$invalid"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="emailChange.submit()" ng-disabled="emailChangeForm.$invalid || emailChange.busy"><i class="fa fa-circle-notch fa-spin" ng-show="emailChange.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal change fallback email -->
<div class="modal fade" id="fallbackEmailChangeModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.changeFallbackEmail.title' | tr }}</h4>
</div>
<div class="modal-body">
<form name="fallbackEmailChangeForm" role="form" novalidate ng-submit="fallbackEmailChange.submit()" autocomplete="off">
<input type="password" style="display: none;">
<div class="form-group" ng-class="{ 'has-error': fallbackEmailChange.error.generic || (fallbackEmailChangeForm.email.$dirty && fallbackEmailChangeForm.email.$invalid) || (!fallbackEmailChangeForm.email.$dirty && fallbackEmailChange.error.email)}">
<label class="control-label" for="inputFallbackEmailChangeEmail">{{ 'profile.changeFallbackEmail.email' | tr }}</label>
<input type="email" class="form-control" ng-model="fallbackEmailChange.email" id="inputFallbackEmailChangeEmail" name="email" required autofocus>
<div class="control-label" ng-show="fallbackEmailChange.error.generic || (!fallbackEmailChangeForm.email.$dirty && fallbackEmailChange.error.email) || (fallbackEmailChangeForm.email.$dirty && fallbackEmailChangeForm.email.$invalid)">
<small ng-show="fallbackEmailChangeForm.email.$error.required">{{ 'profile.changeFallbackEmail.errorEmailRequired' | tr }}</small>
<small ng-show="(fallbackEmailChangeForm.email.$dirty && fallbackEmailChangeForm.email.$invalid) && !fallbackEmailChangeForm.email.$error.required">{{ 'profile.changeFallbackEmail.errorEmailInvalid' | tr }}</small>
<small ng-show="!fallbackEmailChangeForm.email.$dirty && fallbackEmailChange.error.email">{{ fallbackEmailChange.error.email }}</small>
<small ng-show="fallbackEmailChange.error.generic">{{ fallbackEmailChange.error.generic }}</small>
</div>
</div>
<div class="form-group" ng-class="{ 'has-error': (!fallbackEmailChangeForm.password.$dirty && fallbackEmailChange.error.password) || (fallbackEmailChangeForm.password.$dirty && fallbackEmailChangeForm.password.$invalid) }">
<label class="control-label" for="inputFallbackEmailChangePassword">{{ 'profile.changeFallbackEmail.password' | tr }}</label>
<input type="password" class="form-control" ng-model="fallbackEmailChange.password" id="inputFallbackEmailChangePassword" name="password" required autofocus password-reveal>
<div class="control-label" ng-show="(!fallbackEmailChangeForm.password.$dirty && fallbackEmailChange.error.password) || (fallbackEmailChangeForm.password.$dirty && fallbackEmailChangeForm.password.$invalid)">
<small ng-show="!fallbackEmailChangeForm.password.$dirty && fallbackEmailChange.error.password">{{ 'profile.changeFallbackEmail.errorWrongPassword' | tr }}</small>
<small ng-show="fallbackEmailChangeForm.password.$dirty && fallbackEmailChangeForm.password.$error.required">{{ 'profile.changeFallbackEmail.errorPasswordRequired' | tr }}</small>
</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="fallbackEmailChangeForm.$invalid"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="fallbackEmailChange.submit()" ng-disabled="fallbackEmailChangeForm.$invalid || fallbackEmailChange.busy"><i class="fa fa-circle-notch fa-spin" ng-show="fallbackEmailChange.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal change displayName -->
<div class="modal fade" id="displayNameChangeModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.changeDisplayName.title' | tr }}</h4>
</div>
<div class="modal-body">
<form name="displayNameChangeForm" role="form" novalidate ng-submit="displayNameChange.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (displayNameChangeForm.displayName.$dirty && displayNameChangeForm.displayName.$invalid) || (!displayNameChangeForm.displayName.$dirty && displayNameChange.error.displayName)}">
<input type="text" class="form-control" ng-model="displayNameChange.displayName" id="inputDisplayNameChangeDisplayName" name="displayName" required autofocus>
<div class="control-label" ng-show="(!displayNameChangeForm.displayName.$dirty && displayNameChange.error.displayName) || (displayNameChangeForm.displayName.$dirty && displayNameChangeForm.displayName.$invalid)">
<small ng-show="displayNameChangeForm.displayName.$error.required">{{ 'profile.changeDisplayName.errorDisplayNameRequired' | tr }}</small>
<small ng-show="(displayNameChangeForm.displayName.$dirty && displayNameChangeForm.displayName.$invalid) && !displayNameChangeForm.displayName.$error.required">{{ 'profile.changeDisplayName.errorNameInvalid' | tr }}</small>
<small ng-show="!displayNameChangeForm.email.$dirty && displayNameChange.error.displayName">{{ displayNameChange.error.displayName }}</small>
</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="displayNameChangeForm.$invalid"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="displayNameChange.submit()" ng-disabled="displayNameChangeForm.$invalid || displayNameChange.busy"><i class="fa fa-circle-notch fa-spin" ng-show="displayNameChange.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal enable twofactor authentication -->
<div class="modal fade" id="twoFactorAuthenticationEnableModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.enable2FA.title' | tr }}</h4>
</div>
<p class="modal-body" ng-show="twoFactorAuthentication.mandatory2FAHelp && !twoFactorAuthentication.secret">{{ 'profile.enable2FA.description' | tr }}</p>
<div class="modal-body text-center" ng-show="!twoFactorAuthentication.mandatory2FAHelp && !twoFactorAuthentication.secret && !twoFactorAuthentication.notSupportedError">
<h2><i class="fa fa-circle-notch fa-spin"></i></h2>
</div>
<div class="modal-body" ng-show="twoFactorAuthentication.notSupportedError">
<p class="text-danger">{{ twoFactorAuthentication.notSupportedError }}</p>
</div>
<div class="modal-body" ng-show="twoFactorAuthentication.secret">
<p ng-bind-html="'profile.enable2FA.authenticatorAppDescription' | tr:{ googleAuthenticatorPlayStoreLink: 'https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2', googleAuthenticatorITunesLink: 'https://itunes.apple.com/us/app/google-authenticator/id388497605', freeOTPPlayStoreLink: 'https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp', freeOTPITunesLink: 'https://itunes.apple.com/us/app/freeotp-authenticator/id872559395' }"></p>
<center>
<img ng-src="{{ twoFactorAuthentication.qrcode }}"/>
<p>{{ twoFactorAuthentication.secret }}</p>
</center>
<br/>
<form name="twoFactorAuthenticationEnableForm" role="form" novalidate ng-submit="twoFactorAuthentication.enable()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (!twoFactorAuthenticationEnableForm.totpToken.$dirty && twoFactorAuthentication.error) || (twoFactorAuthenticationEnableForm.totpToken.$dirty && twoFactorAuthenticationEnableForm.totpToken.$invalid) }">
<label class="control-label">{{ 'profile.enable2FA.token' | tr }}</label>
<div class="control-label" ng-show="(!twoFactorAuthenticationEnableForm.totpToken.$dirty && twoFactorAuthentication.error) || (twoFactorAuthenticationEnableForm.totpToken.$dirty && twoFactorAuthenticationEnableForm.totpToken.$invalid)">
<small>{{ twoFactorAuthentication.error }}</small>
</div>
<input type="text" class="form-control" ng-model="twoFactorAuthentication.totpToken" id="twoFactorAuthenticationTotpTokenInput" name="totpToken" required autofocus>
</div>
<input class="ng-hide" type="submit" ng-disabled="twoFactorAuthenticationEnableForm.$invalid"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-if="!twoFactorAuthentication.mandatory2FA">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="twoFactorAuthentication.enable()" ng-show="twoFactorAuthentication.secret" ng-disabled="twoFactorAuthenticationEnableForm.$invalid || twoFactorAuthentication.busy"><i class="fa fa-circle-notch fa-spin" ng-show="twoFactorAuthentication.busy"></i> {{ 'profile.enable2FA.enable' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="twoFactorAuthentication.getSecret()" ng-show="twoFactorAuthentication.mandatory2FAHelp" >{{ 'profile.enable2FA.setup2FA' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal disable twofactor authentication -->
<div class="modal fade" id="twoFactorAuthenticationDisableModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.disable2FA.title' | tr }}</h4>
</div>
<div class="modal-body">
<form name="twoFactorAuthenticationDisableForm" role="form" novalidate ng-submit="twoFactorAuthentication.disable()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (!twoFactorAuthenticationDisableForm.password.$dirty && twoFactorAuthentication.error) || (twoFactorAuthenticationDisableForm.password.$dirty && twoFactorAuthenticationDisableForm.password.$invalid) }">
<label class="control-label">{{ 'profile.disable2FA.password' | tr }}</label>
<div class="control-label" ng-show="(!twoFactorAuthenticationDisableForm.password.$dirty && twoFactorAuthentication.error) || (twoFactorAuthenticationDisableForm.password.$dirty && twoFactorAuthenticationDisableForm.password.$invalid)">
<small>{{ twoFactorAuthentication.error }}</small>
</div>
<input type="password" class="form-control" ng-model="twoFactorAuthentication.password" id="twoFactorAuthenticationPasswordInput" name="password" required autofocus password-reveal>
</div>
<input class="ng-hide" type="submit" ng-disabled="twoFactorAuthenticationDisableForm.$invalid"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="twoFactorAuthentication.disable()" ng-disabled="twoFactorAuthenticationDisableForm.$invalid || twoFactorAuthentication.busy"><i class="fa fa-circle-notch fa-spin" ng-show="twoFactorAuthentication.busy"></i> {{ 'profile.disable2FA.disable' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal add app password -->
<div class="modal fade" id="appPasswordAddModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.createAppPassword.title' | tr }}</h4>
</div>
<div class="modal-body">
<div ng-hide="appPasswordAdd.password">
<form name="appPasswordAddForm" role="form" novalidate ng-submit="appPasswordAdd.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (appPasswordAddForm.name.$dirty && appPasswordAddForm.name.$invalid) || (!appPasswordAddForm.name.$dirty && appPasswordAdd.error.name)}">
<label class="control-label">{{ 'profile.createAppPassword.name' | tr }}</label>
<div class="control-label" ng-show="(!appPasswordAddForm.name.$dirty && appPasswordAdd.error.name) || (appPasswordAddForm.name.$dirty && appPasswordAddForm.name.$invalid)">
<small ng-show="appPasswordAddForm.name.$error.required">{{ 'profile.createAppPassword.errorNameRequired' | tr }}</small>
<small ng-show="appPasswordAdd.error.name">{{ appPasswordAdd.error.name }}</small>
</div>
<input type="text" class="form-control" ng-model="appPasswordAdd.name" id="inputAppPasswordAddName" name="name" required autofocus>
</div>
<div class="form-group" ng-class="{ 'has-error': (appPasswordAddForm.identifier.$dirty && appPasswordAddForm.identifier.$invalid) || (!appPasswordAddForm.identifier.$dirty && appPasswordAdd.error.identifier)}">
<label class="control-label">{{ 'profile.createAppPassword.app' | tr }}</label>
<select class="form-control" ng-model="appPasswordAdd.identifier" ng-options="a.id as a.label for a in appPassword.identifiers" required></select>
</div>
<input class="ng-hide" type="submit" ng-disabled="appPasswordAddForm.$invalid"/>
</form>
</div>
<div ng-show="appPasswordAdd.password">
{{ 'profile.createAppPassword.description' | tr }}
<br/>
<br/>
<div class="input-group">
<input type="text" id="newAppPassword" class="form-control" name="appPassword" ng-model="appPasswordAdd.password.password" required readonly/>
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="newAppPasswordClipboardButton" data-clipboard-target="#newAppPassword"><i class="fa fa-clipboard"></i></button>
</span>
</div>
<br/>
<p>{{ 'profile.createAppPassword.copyNow' | tr }}</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="appPasswordAdd.submit()" ng-hide="appPasswordAdd.password" ng-disabled="appPasswordAddForm.$invalid || appPasswordAdd.busy">
<i class="fa fa-circle-notch fa-spin" ng-show="appPasswordAdd.busy"></i> {{ 'profile.createAppPassword.generatePassword' | tr }}
</button>
</div>
</div>
</div>
</div>
<!-- Modal add api token -->
<div class="modal fade" id="apiTokenAddModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'profile.createApiToken.title' | tr }}</h4>
</div>
<div class="modal-body">
<div ng-hide="tokens.add.accessToken">
<form name="apiTokenAddForm" role="form" novalidate ng-submit="tokens.add.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (apiTokenAddForm.name.$dirty && apiTokenAddForm.name.$invalid) || (!apiTokenAddForm.name.$dirty && tokens.add.error)}">
<label class="control-label">{{ 'profile.createApiToken.name' | tr }}</label>
<div class="control-label" ng-show="(!apiTokenAddForm.name.$dirty && tokens.add.error) || (apiTokenAddForm.name.$dirty && apiTokenAddForm.name.$invalid)">
<small ng-show="apiTokenAddForm.name.$error.required">{{ 'profile.createApiToken.errorNameRequired' | tr }}</small>
<small ng-show="tokens.add.error.name">{{ tokens.add.error }}</small>
</div>
<input type="text" class="form-control" id="inputApiTokenName" ng-model="tokens.add.name" name="name" required autofocus>
</div>
<div class="form-group">
<label class="control-label">{{ 'profile.createApiToken.access' | tr }}</label>
<div class="radio">
<label><input type="radio" ng-model="tokens.add.scope" value="r"> {{ 'profile.apiTokens.readonly' | tr }}</label>
</div>
<div class="radio">
<label><input type="radio" ng-model="tokens.add.scope" value="rw"> {{ 'profile.apiTokens.readwrite' | tr }}</label>
</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="apiTokenAddForm.$invalid"/>
</form>
</div>
<div ng-show="tokens.add.accessToken">
{{ 'profile.createApiToken.description' | tr }}
<br/>
<br/>
<div class="input-group">
<input type="text" id="accessTokenToken" class="form-control" name="accessToken" ng-model="tokens.add.accessToken" required readonly/>
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="newAccessTokenClipboardButton" data-clipboard-target="#accessTokenToken"><i class="fa fa-clipboard"></i></button>
</span>
</div>
<br/>
<p>{{ 'profile.createApiToken.copyNow' | tr }}</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="tokens.add.submit()" ng-hide="tokens.add.accessToken" ng-disabled="apiTokenAddForm.$invalid || tokens.add.busy">
<i class="fa fa-circle-notch fa-spin" ng-show="tokens.add.busy"></i> {{ 'profile.createApiToken.generateToken' | tr }}
</button>
</div>
</div>
</div>
</div>
<div class="content">
<h1 class="section-header">{{ 'profile.title' | tr }}</h1>
<div class="card">
<div style="display: flex; flex-wrap: wrap;">
<div style="width: 150px;">
<div class="settings-avatar" style="background-image: url('{{ user.avatarUrl }}');" ng-click="avatarChange.showChangeAvatar()">
<i class="picture-edit-indicator fa fa-pencil-alt"></i>
</div>
</div>
<div style="flex-grow: 1;">
<table style="width: 100%;">
<tr>
<td class="text-muted" style="vertical-align: top;">{{ 'main.username' | tr }}</td>
<td class="text-right" style="vertical-align: top;">
{{ user.username }} &nbsp;&nbsp;&nbsp;
</td>
</tr>
<tr>
<td class="text-muted" style="vertical-align: top;">{{ 'main.displayName' | tr }}</td>
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
{{ user.displayName }} <a href="" ng-click="displayNameChange.show()" ng-hide="user.source || config.profileLocked"><i class="fa fa-edit text-small"></i></a>
</td>
</tr>
<tr>
<td class="text-muted" style="vertical-align: top;">{{ 'profile.primaryEmail' | tr }}</td>
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
{{ user.email }} <a href="" ng-click="emailChange.show()" ng-hide="user.source || config.profileLocked"><i class="fa fa-edit text-small"></i></a>
</td>
</tr>
<tr>
<td class="text-muted" style="vertical-align: top;">{{ 'profile.passwordRecoveryEmail' | tr }}</td>
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
{{ user.fallbackEmail }} <a href="" ng-click="fallbackEmailChange.show()" ng-hide="user.source || config.profileLocked"><i class="fa fa-edit text-small"></i></a>
</td>
</tr>
<tr ng-hide="user.source">
<td colspan="2" class="text-right">
<a href="" ng-click="sendPasswordReset()">{{ 'profile.passwordResetAction' | tr }}</a>
</td>
</tr>
<tr><td colspan="2">&nbsp;</td></tr>
<tr>
<td class="text-muted" style="vertical-align: middle;">{{ 'profile.language' | tr }}</td>
<td class="text-right" style="vertical-align: middle;">
<multiselect ng-model="language" options="lang.display for lang in languages" data-multiple="false" filter-after-rows="5" scroll-after-rows="10"></multiselect>
</td>
</tr>
</table>
</div>
</div>
<br/>
<div style="display: flex; flex-wrap: wrap; gap: 5px;">
<button class="btn btn-default" ng-click="backgroundImageChange.show()">Set Background Image</button>
<div style="flex-grow: 1;"></div>
<button class="btn btn-primary" ng-click="passwordchange.show()" ng-hide="user.source">{{ 'profile.changePasswordAction' | tr }}</button>
<button class="btn" uib-tooltip="{{ (user.source && config.external2FA) ? ('profile.enable2FANotAvailable' | tr) : '' }}" ng-disabled="user.source && config.external2FA" ng-class="user.twoFactorAuthenticationEnabled ? 'btn-danger' : 'btn-success'" ng-click="twoFactorAuthentication.show()">{{ user.twoFactorAuthenticationEnabled ? 'profile.disable2FAAction' : 'profile.enable2FAAction' | tr }}</button>
</div>
</div>
<h3 class="section-header">{{ 'profile.appPasswords.title' | tr }}<button class="btn btn-primary btn-sm pull-right" ng-click="appPasswordAdd.show()"><i class="fa fa-plus"></i> {{ 'profile.appPasswords.newPassword' | tr }}</button></h3>
<div class="card">
<div class="grid-item-top">
<div class="row">
<div class="col-xs-12">
<p>{{ 'profile.appPasswords.description' | tr }}</p>
<table class="table table-hover">
<thead>
<tr>
<th style="width: 45%">{{ 'profile.appPasswords.name' | tr }}</th>
<th style="width: 45%">{{ 'profile.appPasswords.app' | tr }}</th>
<th style="width: 10%" class="text-right">{{ 'main.actions' | tr }}</th>
</tr>
</thead>
<tbody>
<tr ng-show="appPassword.passwords.length === 0">
<td colspan="3" class="text-center">{{ 'profile.appPasswords.noPasswordsPlaceholder' | tr }}</td>
</tr>
<tr ng-repeat="password in appPassword.passwords">
<td class="text-left elide-table-cell">
<span uib-tooltip="{{ password.creationTime | prettyLongDate }}" class="arrow">{{ password.name }}</span>
</td>
<td class="text-left elide-table-cell">
<span class="arrow">{{ password.label }}</span>
</td>
<td class="text-right no-wrap" style="vertical-align: bottom">
<button class="btn btn-xs btn-danger pull-right" ng-click="appPassword.del(password.id)" uib-tooltip="{{ 'profile.appPasswords.deletePasswordTooltip' | tr }}"><i class="far fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<h3 class="section-header" ng-show="user.isAtLeastAdmin">{{ 'profile.apiTokens.title' | tr }} <button class="btn btn-primary btn-sm pull-right" ng-click="tokens.add.show()"><i class="fa fa-plus"></i> {{ 'profile.apiTokens.newApiToken' | tr }}</button></h3>
<div class="card" ng-show="user.isAtLeastAdmin">
<div class="grid-item-top">
<div class="row">
<div class="col-xs-12">
<p ng-bind-html="'profile.apiTokens.description' | tr:{ apiDocsLink: 'https://docs.cloudron.io/api.html' }"></p>
<table class="table table-hover" style="margin: 0;">
<thead>
<tr>
<th>{{ 'profile.apiTokens.name' | tr }}</th>
<th class="hide-mobile">{{ 'profile.apiTokens.lastUsed' | tr }}</th>
<th>{{ 'profile.apiTokens.scope' | tr }}</th>
<th class="text-right">{{ 'main.actions' | tr }}</th>
</tr>
</thead>
<tbody>
<tr ng-show="tokens.apiTokens.length === 0">
<td colspan="3" class="text-center">{{ 'profile.apiTokens.noTokensPlaceholder' | tr }}</td>
</tr>
<tr ng-repeat="token in tokens.apiTokens">
<td>
{{ token.name || 'unnamed' }}
</td>
<td class="hide-mobile">
<span ng-show="token.lastUsedTime">{{ token.lastUsedTime | prettyLongDate }}</span>
<span ng-show="!token.lastUsedTime">{{ 'profile.apiTokens.neverUsed' | tr }}</span>
</td>
<td>
<span ng-show="token.scope['*'] === 'rw'">{{ 'profile.apiTokens.readwrite' | tr }}</span>
<span ng-hide="token.scope['*'] === 'rw'">{{ 'profile.apiTokens.readonly' | tr }}</span>
</td>
<td class="text-right">
<button class="btn btn-xs btn-danger" ng-click="tokens.revokeToken(token)" uib-tooltip="{{ 'profile.apiTokens.revokeTokenTooltip' | tr }}"><i class="far fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<h3 class="section-header">{{ 'profile.loginTokens.title' | tr }}</h3>
<div class="card">
<div class="grid-item-top">
<div class="row">
<div class="col-xs-12">
<p>{{ 'profile.loginTokens.description' | tr:{ webadminTokenCount: tokens.webadminTokens.length, cliTokenCount: tokens.cliTokens.length } }}</p>
<button class="btn btn-outline btn-danger pull-right" ng-click="tokens.revokeAllWebAndCliTokens()" ng-disabled="tokens.busy"><i class="fa fa-circle-notch fa-spin" ng-show="tokens.busy"></i> {{ 'profile.loginTokens.logoutAll' | tr }}</button>
</div>
</div>
</div>
</div>
</div>