mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 15:45:55 +08:00
This commit is contained in:
parent
65b508364a
commit
1a82dde49b
|
@ -48,14 +48,6 @@ export class AbstractApp {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} token
|
||||
*/
|
||||
setClientSideToken(token) {
|
||||
rl.hash.set();
|
||||
Settings.set('AuthAccountHash', token);
|
||||
}
|
||||
|
||||
logoutReload(close = false) {
|
||||
const url = logoutLink();
|
||||
|
||||
|
|
|
@ -98,10 +98,10 @@ export const Notification = {
|
|||
AccountTwoFactorAuthRequired: 120,
|
||||
AccountTwoFactorAuthError: 121,
|
||||
|
||||
// CurrentPasswordIncorrect: 131,
|
||||
// NewPasswordShort: 132,
|
||||
// NewPasswordWeak: 133,
|
||||
// NewPasswordForbidden: 134,
|
||||
CouldNotSaveNewPassword: 130,
|
||||
CurrentPasswordIncorrect: 131,
|
||||
NewPasswordShort: 132,
|
||||
NewPasswordWeak: 133,
|
||||
|
||||
ContactsSyncError: 140,
|
||||
|
||||
|
|
|
@ -594,6 +594,18 @@ class RemoteUserFetch extends AbstractFetchRemote {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} prevPassword
|
||||
* @param {string} newPassword
|
||||
*/
|
||||
changePassword(fCallback, prevPassword, newPassword) {
|
||||
this.defaultRequest(fCallback, 'ChangePassword', {
|
||||
'PrevPassword': prevPassword,
|
||||
'NewPassword': newPassword
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sFolderFullNameRaw
|
||||
|
|
|
@ -18,6 +18,7 @@ import { TemplatesUserSettings } from 'Settings/User/Templates';
|
|||
import { FoldersUserSettings } from 'Settings/User/Folders';
|
||||
import { ThemesUserSettings } from 'Settings/User/Themes';
|
||||
import { OpenPgpUserSettings } from 'Settings/User/OpenPgp';
|
||||
import { ChangePasswordUserSettings } from 'Settings/User/ChangePassword';
|
||||
|
||||
import { SystemDropDownSettingsUserView } from 'View/User/Settings/SystemDropDown';
|
||||
import { MenuSettingsUserView } from 'View/User/Settings/Menu';
|
||||
|
@ -77,6 +78,15 @@ export class SettingsUserScreen extends AbstractSettingsScreen {
|
|||
);
|
||||
}
|
||||
|
||||
if (Settings.get('ChangePasswordIsAllowed')) {
|
||||
settingsAddViewModel(
|
||||
ChangePasswordUserSettings,
|
||||
'SettingsChangePassword',
|
||||
'GLOBAL/PASSWORD',
|
||||
'change-password'
|
||||
);
|
||||
}
|
||||
|
||||
if (Settings.capa(Capa.Folders)) {
|
||||
settingsAddViewModel(FoldersUserSettings, 'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders');
|
||||
}
|
||||
|
|
90
dev/Settings/User/ChangePassword.js
Normal file
90
dev/Settings/User/ChangePassword.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { StorageResultType, Notification } from 'Common/Enums';
|
||||
import { getNotificationFromResponse, i18n } from 'Common/Translator';
|
||||
import { Settings } from 'Common/Globals';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { decorateKoCommands } from 'Knoin/Knoin';
|
||||
|
||||
export class ChangePasswordUserSettings {
|
||||
constructor() {
|
||||
ko.addObservablesTo(this, {
|
||||
changeProcess: false,
|
||||
|
||||
errorDescription: '',
|
||||
passwordMismatch: false,
|
||||
passwordUpdateError: false,
|
||||
passwordUpdateSuccess: false,
|
||||
|
||||
currentPassword: '',
|
||||
currentPasswordError: false,
|
||||
newPassword: '',
|
||||
newPassword2: '',
|
||||
});
|
||||
|
||||
this.currentPassword.subscribe(() => this.resetUpdate(true));
|
||||
this.newPassword.subscribe(() => this.resetUpdate());
|
||||
this.newPassword2.subscribe(() => this.resetUpdate());
|
||||
|
||||
decorateKoCommands(this, {
|
||||
saveNewPasswordCommand: self => !self.changeProcess()
|
||||
&& '' !== self.currentPassword()
|
||||
&& '' !== self.newPassword()
|
||||
&& '' !== self.newPassword2()
|
||||
});
|
||||
}
|
||||
|
||||
saveNewPasswordCommand() {
|
||||
if (this.newPassword() !== this.newPassword2()) {
|
||||
this.passwordMismatch(true);
|
||||
this.errorDescription(i18n('SETTINGS_CHANGE_PASSWORD/ERROR_PASSWORD_MISMATCH'));
|
||||
} else {
|
||||
this.reset(true);
|
||||
Remote.changePassword(this.onChangePasswordResponse.bind(this), this.currentPassword(), this.newPassword());
|
||||
}
|
||||
}
|
||||
|
||||
reset(change) {
|
||||
this.changeProcess(change);
|
||||
this.resetUpdate();
|
||||
this.currentPasswordError(false);
|
||||
this.errorDescription('');
|
||||
}
|
||||
|
||||
resetUpdate(current) {
|
||||
this.passwordUpdateError(false);
|
||||
this.passwordUpdateSuccess(false);
|
||||
current ? this.currentPasswordError(false) : this.passwordMismatch(false);
|
||||
}
|
||||
|
||||
onHide() {
|
||||
this.reset(false);
|
||||
this.currentPassword('');
|
||||
this.newPassword('');
|
||||
this.newPassword2('');
|
||||
}
|
||||
|
||||
onChangePasswordResponse(result, data) {
|
||||
this.reset(false);
|
||||
|
||||
if (StorageResultType.Success === result && data && data.Result) {
|
||||
this.currentPassword('');
|
||||
this.newPassword('');
|
||||
this.newPassword2('');
|
||||
|
||||
this.passwordUpdateSuccess(true);
|
||||
|
||||
rl.hash.set();
|
||||
Settings.set('AuthAccountHash', data.Result);
|
||||
} else {
|
||||
if (data && Notification.CurrentPasswordIncorrect === data.ErrorCode) {
|
||||
this.currentPasswordError(true);
|
||||
}
|
||||
|
||||
this.passwordUpdateError(true);
|
||||
this.errorDescription(getNotificationFromResponse(data, Notification.CouldNotSaveNewPassword));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
class ChangePasswordExampleDriver implements \RainLoop\Providers\ChangePassword\ChangePasswordInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sAllowedEmails = '';
|
||||
|
||||
/**
|
||||
* @param string $sAllowedEmails
|
||||
*
|
||||
* @return \ChangePasswordExampleDriver
|
||||
*/
|
||||
public function SetAllowedEmails($sAllowedEmails)
|
||||
{
|
||||
$this->sAllowedEmails = $sAllowedEmails;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isPossible(\RainLoop\Model\Account $oAccount) : bool
|
||||
{
|
||||
$sFoundedValue = '';
|
||||
return $oAccount && $oAccount->Email() &&
|
||||
\RainLoop\Plugins\Helper::ValidateWildcardValues($oAccount->Email(), $this->sAllowedEmails, $sFoundedValue);
|
||||
}
|
||||
|
||||
public function ChangePassword(\RainLoop\Model\Account $oAccount, string $sPrevPassword, string $sNewPassword) : bool
|
||||
{
|
||||
$bResult = false;
|
||||
|
||||
// TODO
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
}
|
20
plugins/change-password-example/LICENSE
Normal file
20
plugins/change-password-example/LICENSE
Normal file
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 RainLoop Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
49
plugins/change-password-example/index.php
Normal file
49
plugins/change-password-example/index.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
class ChangePasswordExamplePlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
{
|
||||
const
|
||||
NAME = 'Change Password Example',
|
||||
VERSION = '2.0',
|
||||
RELEASE = '2021-03-01',
|
||||
REQUIRED = '2.3.4',
|
||||
CATEGORY = 'Security',
|
||||
DESCRIPTION = 'Plugin that adds functionality to change the email account password';
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
$this->addHook('main.fabrica', 'MainFabrica');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sName
|
||||
* @param mixed $oProvider
|
||||
*/
|
||||
public function MainFabrica($sName, &$oProvider)
|
||||
{
|
||||
switch ($sName)
|
||||
{
|
||||
case 'change-password':
|
||||
|
||||
include_once __DIR__.'/ChangePasswordExampleDriver.php';
|
||||
|
||||
$oProvider = new ChangePasswordExampleDriver();
|
||||
$oProvider->SetAllowedEmails(\strtolower(\trim($this->Config()->Get('plugin', 'allowed_emails', ''))));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function configMapping() : array
|
||||
{
|
||||
return array(
|
||||
\RainLoop\Plugins\Property::NewInstance('allowed_emails')->SetLabel('Allowed emails')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT)
|
||||
->SetDescription('Allowed emails, space as delimiter, wildcard supported. Example: user1@domain1.net user2@domain1.net *@domain2.net')
|
||||
->SetDefaultValue('*')
|
||||
);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ class Actions
|
|||
use Actions\User;
|
||||
use Actions\Raw;
|
||||
use Actions\Response;
|
||||
use Actions\ChangePassword;
|
||||
|
||||
const AUTH_TFA_SIGN_ME_TOKEN_KEY = 'rltfasmauth';
|
||||
const AUTH_SIGN_ME_TOKEN_KEY = 'rlsmauth';
|
||||
|
@ -1064,6 +1065,7 @@ class Actions
|
|||
'StartupUrl' => \trim(\ltrim(\trim($oConfig->Get('labs', 'startup_url', '')), '#/')),
|
||||
'SieveAllowFileintoInbox' => (bool)$oConfig->Get('labs', 'sieve_allow_fileinto_inbox', false),
|
||||
'ContactsIsAllowed' => false,
|
||||
'ChangePasswordIsAllowed' => false,
|
||||
'RequireTwoFactor' => false,
|
||||
'Admin' => array(),
|
||||
'Capa' => array(),
|
||||
|
@ -1115,6 +1117,7 @@ class Actions
|
|||
$aResult['OutLogin'] = $oAccount->OutLogin();
|
||||
$aResult['AccountHash'] = $oAccount->Hash();
|
||||
$aResult['AccountSignMe'] = $oAccount->SignMe();
|
||||
$aResult['ChangePasswordIsAllowed'] = $this->ChangePasswordProvider()->isPossible($oAccount);
|
||||
$aResult['ContactsIsAllowed'] = $oAddressBookProvider->IsActive();
|
||||
$aResult['ContactsSyncIsAllowed'] = (bool)$oConfig->Get('contacts', 'allow_sync', false);
|
||||
$aResult['ContactsSyncInterval'] = (int)$oConfig->Get('contacts', 'sync_interval', 20);
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace RainLoop\Actions;
|
||||
|
||||
trait ChangePassword
|
||||
{
|
||||
/**
|
||||
* @var \RainLoop\Providers\ChangePassword
|
||||
*/
|
||||
private $oChangePasswordProvider;
|
||||
|
||||
public function ChangePasswordProvider() : \RainLoop\Providers\ChangePassword
|
||||
{
|
||||
if (!$this->oChangePasswordProvider) {
|
||||
$this->oChangePasswordProvider = new \RainLoop\Providers\ChangePassword(
|
||||
$this, $this->fabrica('change-password'), !!$this->Config()->Get('labs', 'check_new_password_strength')
|
||||
);
|
||||
}
|
||||
|
||||
return $this->oChangePasswordProvider;
|
||||
}
|
||||
|
||||
public function DoChangePassword() : array
|
||||
{
|
||||
$mResult = false;
|
||||
|
||||
if ($oAccount = $this->getAccountFromToken()) {
|
||||
try
|
||||
{
|
||||
$mResult = $this->ChangePasswordProvider()->ChangePassword(
|
||||
$oAccount,
|
||||
$this->GetActionParam('PrevPassword', ''),
|
||||
$this->GetActionParam('NewPassword', '')
|
||||
);
|
||||
}
|
||||
catch (\Throwable $oException)
|
||||
{
|
||||
$this->loginErrorDelay();
|
||||
$this->Logger()->Write('Error: Can\'t change password for '.$oAccount->Email().' account.', \MailSo\Log\Enumerations\Type::NOTICE);
|
||||
|
||||
throw $oException;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__, $mResult);
|
||||
}
|
||||
|
||||
}
|
|
@ -13,10 +13,10 @@ class Notifications
|
|||
const AccountTwoFactorAuthRequired = 120;
|
||||
const AccountTwoFactorAuthError = 121;
|
||||
|
||||
// const CurrentPasswordIncorrect = 131;
|
||||
// const NewPasswordShort = 132;
|
||||
// const NewPasswordWeak = 133;
|
||||
// const NewPasswordForbidden = 134;
|
||||
const CouldNotSaveNewPassword = 130;
|
||||
const CurrentPasswordIncorrect = 131;
|
||||
const NewPasswordShort = 132;
|
||||
const NewPasswordWeak = 133;
|
||||
|
||||
const ContactsSyncError = 140;
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace RainLoop\Providers;
|
||||
|
||||
use \RainLoop\Model\Account;
|
||||
use \RainLoop\Exceptions\ClientException;
|
||||
use \RainLoop\Notifications;
|
||||
|
||||
class ChangePassword extends AbstractProvider
|
||||
{
|
||||
/**
|
||||
* @var \RainLoop\Actions
|
||||
*/
|
||||
private $oActions;
|
||||
|
||||
/**
|
||||
* @var \RainLoop\Providers\ChangePassword\ChangePasswordInterface
|
||||
*/
|
||||
private $oDriver;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bCheckWeak;
|
||||
|
||||
public function __construct(\RainLoop\Actions $oActions, ?ChangePassword\ChangePasswordInterface $oDriver = null, bool $bCheckWeak = true)
|
||||
{
|
||||
$this->oActions = $oActions;
|
||||
$this->oDriver = $oDriver;
|
||||
$this->bCheckWeak = $bCheckWeak;
|
||||
}
|
||||
|
||||
public function isPossible(Account $oAccount) : bool
|
||||
{
|
||||
return $this->IsActive() && $this->oDriver->isPossible($oAccount);
|
||||
}
|
||||
|
||||
public function ChangePassword(Account $oAccount, string $sPrevPassword, string $sNewPassword) : bool
|
||||
{
|
||||
if (!$this->isPossible($oAccount)) {
|
||||
throw new ClientException(Notifications::CouldNotSaveNewPassword);
|
||||
}
|
||||
|
||||
if ($sPrevPassword !== $oAccount->Password()) {
|
||||
throw new ClientException(Notifications::CurrentPasswordIncorrect);
|
||||
}
|
||||
|
||||
$sPasswordForCheck = \trim($sNewPassword);
|
||||
if (10 > \strlen($sPasswordForCheck)) {
|
||||
throw new ClientException(Notifications::NewPasswordShort);
|
||||
}
|
||||
|
||||
if ($this->bCheckWeak && !\MailSo\Base\Utils::PasswordWeaknessCheck($sPasswordForCheck)) {
|
||||
throw new ClientException(Notifications::NewPasswordWeak);
|
||||
}
|
||||
|
||||
if (!$this->oDriver->ChangePassword($oAccount, $sPrevPassword, $sNewPassword)) {
|
||||
throw new ClientException(Notifications::CouldNotSaveNewPassword);
|
||||
}
|
||||
|
||||
$oAccount->SetPassword($sNewPassword);
|
||||
$this->oActions->SetAuthToken($oAccount);
|
||||
|
||||
return $this->oActions->GetSpecAuthToken();
|
||||
}
|
||||
|
||||
public function IsActive() : bool
|
||||
{
|
||||
return $this->oDriver instanceof ChangePassword\ChangePasswordInterface;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace RainLoop\Providers\ChangePassword;
|
||||
|
||||
interface ChangePasswordInterface
|
||||
{
|
||||
public function isPossible(\RainLoop\Model\Account $oAccount) : bool;
|
||||
|
||||
public function ChangePassword(\RainLoop\Model\Account $oAccount, string $sPrevPassword, string $sNewPassword) : bool;
|
||||
}
|
|
@ -118,6 +118,10 @@ class ServiceActions
|
|||
case 'DoAccountAdd':
|
||||
$this->Logger()->AddSecret($this->oActions->GetActionParam('Password', ''));
|
||||
break;
|
||||
case 'DoChangePassword':
|
||||
$this->Logger()->AddSecret($this->oActions->GetActionParam('PrevPassword', ''));
|
||||
$this->Logger()->AddSecret($this->oActions->GetActionParam('NewPassword', ''));
|
||||
break;
|
||||
}
|
||||
|
||||
$this->Logger()->Write(\MailSo\Base\Utils::Php2js($aPost, $this->Logger()),
|
||||
|
|
|
@ -174,7 +174,6 @@ en:
|
|||
CURRENT_PASSWORD_INCORRECT: "Current password incorrect"
|
||||
NEW_PASSWORD_SHORT: "Password is too short"
|
||||
NEW_PASSWORD_WEAK: "Password is too easy"
|
||||
NEW_PASSWORD_FORBIDDEN: "Password contains forbidden characters"
|
||||
CONTACTS_SYNC_ERROR: "Contacts synchronization error"
|
||||
CANT_GET_MESSAGE_LIST: "Can't get message list"
|
||||
CANT_GET_MESSAGE: "Can't get message"
|
||||
|
|
|
@ -170,7 +170,6 @@ de_DE:
|
|||
CURRENT_PASSWORD_INCORRECT: "Aktuelles Passwort falsch"
|
||||
NEW_PASSWORD_SHORT: "Passwort ist zu kurz"
|
||||
NEW_PASSWORD_WEAK: "Passwort ist zu einfach"
|
||||
NEW_PASSWORD_FORBIDDEN: "Passwort enthält unzulässige Zeichen"
|
||||
CONTACTS_SYNC_ERROR: "Fehler bei Kontakte-Synchronisierung"
|
||||
CANT_GET_MESSAGE_LIST: "Die Nachrichtenliste ist nicht verfügbar"
|
||||
CANT_GET_MESSAGE: "Diese Nachricht ist nicht verfügbar"
|
||||
|
|
|
@ -171,7 +171,6 @@ en_US:
|
|||
CURRENT_PASSWORD_INCORRECT: "Current password incorrect"
|
||||
NEW_PASSWORD_SHORT: "Password is too short"
|
||||
NEW_PASSWORD_WEAK: "Password is too easy"
|
||||
NEW_PASSWORD_FORBIDDEN: "Password contains forbidden characters"
|
||||
CONTACTS_SYNC_ERROR: "Contacts synchronization error"
|
||||
CANT_GET_MESSAGE_LIST: "Can't get message list"
|
||||
CANT_GET_MESSAGE: "Can't get message"
|
||||
|
|
|
@ -171,7 +171,6 @@ es_ES:
|
|||
CURRENT_PASSWORD_INCORRECT: "La contraseña actual es incorrecta"
|
||||
NEW_PASSWORD_SHORT: "La contraseña es muy corta"
|
||||
NEW_PASSWORD_WEAK: "La contraseña es muy fácil"
|
||||
NEW_PASSWORD_FORBIDDEN: "La contraseña contiene caracteres prohibidos"
|
||||
CONTACTS_SYNC_ERROR: "Error de sincronización de Contactos"
|
||||
CANT_GET_MESSAGE_LIST: "No se puede obtener la lista de mensajes"
|
||||
CANT_GET_MESSAGE: "No se puede obtener el mensaje"
|
||||
|
|
|
@ -171,7 +171,6 @@ fr_FR:
|
|||
CURRENT_PASSWORD_INCORRECT: "Le mot de passe actuel est incorrect"
|
||||
NEW_PASSWORD_SHORT: "Le mot de passe est trop court"
|
||||
NEW_PASSWORD_WEAK: "Le mot de passe n'est pas assez fort"
|
||||
NEW_PASSWORD_FORBIDDEN: "Le mot de passe contient des caractères invalides"
|
||||
CONTACTS_SYNC_ERROR: "Erreur de synchronisation des contacts"
|
||||
CANT_GET_MESSAGE_LIST: "Impossible d'obtenir la liste des messages"
|
||||
CANT_GET_MESSAGE: "Impossible d'obtenir le message"
|
||||
|
|
|
@ -170,7 +170,6 @@ nl_NL:
|
|||
CURRENT_PASSWORD_INCORRECT: "Huidig wachtwoord is onjuist"
|
||||
NEW_PASSWORD_SHORT: "Wachtwoord is te kort"
|
||||
NEW_PASSWORD_WEAK: "Wachtwoord is te gemakkelijk"
|
||||
NEW_PASSWORD_FORBIDDEN: "Wachtwoord bevat verboden karakters"
|
||||
CONTACTS_SYNC_ERROR: "Contactpersonen synchronisatie fout"
|
||||
CANT_GET_MESSAGE_LIST: "Kan berichtenlijst niet ophalen"
|
||||
CANT_GET_MESSAGE: "Kan bericht niet ophalen"
|
||||
|
|
|
@ -171,7 +171,6 @@ zh_CN:
|
|||
CURRENT_PASSWORD_INCORRECT: "当前密码不正确"
|
||||
NEW_PASSWORD_SHORT: "密码太短"
|
||||
NEW_PASSWORD_WEAK: "密码过于简单"
|
||||
NEW_PASSWORD_FORBIDDEN: "密码包含禁止使用的字符"
|
||||
CONTACTS_SYNC_ERROR: "联系人同步错误"
|
||||
CANT_GET_MESSAGE_LIST: "无法获取邮件列表"
|
||||
CANT_GET_MESSAGE: "无法获取邮件"
|
||||
|
|
|
@ -515,7 +515,6 @@ en:
|
|||
CURRENT_PASSWORD_INCORRECT: "Current password incorrect"
|
||||
NEW_PASSWORD_SHORT: "Password is too short"
|
||||
NEW_PASSWORD_WEAK: "Password is too easy"
|
||||
NEW_PASSWORD_FORBIDDEN: "Password contains forbidden characters"
|
||||
CONTACTS_SYNC_ERROR: "Contacts synchronization error"
|
||||
CANT_GET_MESSAGE_LIST: "Can't get message list"
|
||||
CANT_GET_MESSAGE: "Can't get message"
|
||||
|
|
|
@ -516,7 +516,6 @@ de_DE:
|
|||
CURRENT_PASSWORD_INCORRECT: "Aktuelles Passwort falsch"
|
||||
NEW_PASSWORD_SHORT: "Passwort ist zu kurz"
|
||||
NEW_PASSWORD_WEAK: "Passwort ist zu einfach"
|
||||
NEW_PASSWORD_FORBIDDEN: "Passwort enthält unzulässige Zeichen"
|
||||
CONTACTS_SYNC_ERROR: "Fehler bei Kontakte-Synchronisierung"
|
||||
CANT_GET_MESSAGE_LIST: "Die Nachrichtenliste ist nicht verfügbar"
|
||||
CANT_GET_MESSAGE: "Diese Nachricht ist nicht verfügbar"
|
||||
|
|
|
@ -515,7 +515,6 @@ en_GB:
|
|||
CURRENT_PASSWORD_INCORRECT: "Current password incorrect"
|
||||
NEW_PASSWORD_SHORT: "Password is too short"
|
||||
NEW_PASSWORD_WEAK: "Password is too easy"
|
||||
NEW_PASSWORD_FORBIDDEN: "Password contains forbidden characters"
|
||||
CONTACTS_SYNC_ERROR: "Contacts synchronization error"
|
||||
CANT_GET_MESSAGE_LIST: "Can't get message list"
|
||||
CANT_GET_MESSAGE: "Can't get message"
|
||||
|
|
|
@ -515,7 +515,6 @@ en_US:
|
|||
CURRENT_PASSWORD_INCORRECT: "Current password incorrect"
|
||||
NEW_PASSWORD_SHORT: "Password is too short"
|
||||
NEW_PASSWORD_WEAK: "Password is too easy"
|
||||
NEW_PASSWORD_FORBIDDEN: "Password contains forbidden characters"
|
||||
CONTACTS_SYNC_ERROR: "Contacts synchronization error"
|
||||
CANT_GET_MESSAGE_LIST: "Can't get message list"
|
||||
CANT_GET_MESSAGE: "Can't get message"
|
||||
|
|
|
@ -517,7 +517,6 @@ es_ES:
|
|||
CURRENT_PASSWORD_INCORRECT: "La contraseña actual es incorrecta"
|
||||
NEW_PASSWORD_SHORT: "La contraseña es muy corta"
|
||||
NEW_PASSWORD_WEAK: "La contraseña es muy fácil"
|
||||
NEW_PASSWORD_FORBIDDEN: "La contraseña contiene caracteres prohibidos"
|
||||
CONTACTS_SYNC_ERROR: "Error de sincronización de Contactos"
|
||||
CANT_GET_MESSAGE_LIST: "No se puede obtener la lista de mensajes"
|
||||
CANT_GET_MESSAGE: "No se puede obtener el mensaje"
|
||||
|
|
|
@ -516,7 +516,6 @@ fr_FR:
|
|||
CURRENT_PASSWORD_INCORRECT: "Le mot de passe actuel est incorrect"
|
||||
NEW_PASSWORD_SHORT: "Le mot de passe est trop court"
|
||||
NEW_PASSWORD_WEAK: "Le mot de passe n'est pas assez fort"
|
||||
NEW_PASSWORD_FORBIDDEN: "Le mot de passe contient des caractères invalides"
|
||||
CONTACTS_SYNC_ERROR: "Erreur de synchronisation des contacts"
|
||||
CANT_GET_MESSAGE_LIST: "Impossible d'obtenir la liste des messages"
|
||||
CANT_GET_MESSAGE: "Impossible d'obtenir le message"
|
||||
|
|
|
@ -515,7 +515,6 @@ nl_NL:
|
|||
CURRENT_PASSWORD_INCORRECT: "Huidig wachtwoord onjuist"
|
||||
NEW_PASSWORD_SHORT: "Wachtwoord is te kort"
|
||||
NEW_PASSWORD_WEAK: "Wachtwoord is te makkelijk"
|
||||
NEW_PASSWORD_FORBIDDEN: "Wachtwoord bevat verboden tekens"
|
||||
CONTACTS_SYNC_ERROR: "Contactpersonen synchronisatie fout"
|
||||
CANT_GET_MESSAGE_LIST: "Berichtenlijst kan niet worden opgehaald"
|
||||
CANT_GET_MESSAGE: "Bericht kan niet worden opgehaald"
|
||||
|
|
|
@ -513,7 +513,6 @@ zh_CN:
|
|||
CURRENT_PASSWORD_INCORRECT: "当前密码不正确"
|
||||
NEW_PASSWORD_SHORT: "密码太短"
|
||||
NEW_PASSWORD_WEAK: "密码过于简单"
|
||||
NEW_PASSWORD_FORBIDDEN: "密码包含禁止使用的字符"
|
||||
CONTACTS_SYNC_ERROR: "联系人同步错误"
|
||||
CANT_GET_MESSAGE_LIST: "无法获取邮件列表"
|
||||
CANT_GET_MESSAGE: "无法获取邮件"
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<div class="b-settings-general g-ui-user-select-none">
|
||||
<div class="form-horizontal long-label">
|
||||
<div class="legend" data-i18n="SETTINGS_CHANGE_PASSWORD/LEGEND_CHANGE_PASSWORD"></div>
|
||||
<div class="row">
|
||||
<div class="span6">
|
||||
<div class="control-group" data-bind="css: {'error': currentPasswordError}">
|
||||
<label class="control-label" data-i18n="SETTINGS_CHANGE_PASSWORD/LABEL_CURRENT_PASSWORD"></label>
|
||||
<div class="controls">
|
||||
<input type="password" autocomplete="current-password" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="textInput: currentPassword" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="css: {'error': passwordMismatch}">
|
||||
<label class="control-label" data-i18n="SETTINGS_CHANGE_PASSWORD/LABEL_NEW_PASSWORD"></label>
|
||||
<div class="controls">
|
||||
<input type="password" autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
minlength="10"
|
||||
data-bind="textInput: newPassword" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="css: {'error': passwordMismatch}">
|
||||
<label class="control-label" data-i18n="SETTINGS_CHANGE_PASSWORD/LABEL_REPEAT_PASSWORD"></label>
|
||||
<div class="controls">
|
||||
<input type="password" autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="textInput: newPassword2" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<a class="btn" data-bind="command: saveNewPasswordCommand, css: { 'btn-success': passwordUpdateSuccess, 'btn-danger': passwordUpdateError }">
|
||||
<i class="fontastic" data-bind="css: {'icon-spinner': changeProcess()}">🔑</i>
|
||||
<span class="i18n" data-i18n="SETTINGS_CHANGE_PASSWORD/BUTTON_UPDATE_PASSWORD"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span4 alert alert-error alert-null-left-margin" data-bind="visible: '' !== errorDescription(), text: errorDescription"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in a new issue