mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Improved additional account management
This commit is contained in:
parent
b057c4083e
commit
6b8020b8f5
15 changed files with 161 additions and 161 deletions
|
|
@ -420,10 +420,10 @@ class AppUser extends AbstractApp {
|
|||
|
||||
if (!iError) {
|
||||
const counts = {},
|
||||
sAccountEmail = AccountUserStore.email();
|
||||
let parentEmail = SettingsGet('ParentEmail') || sAccountEmail;
|
||||
accounts = oData.Result.Accounts,
|
||||
mainEmail = SettingsGet('MainEmail');
|
||||
|
||||
if (isArray(oData.Result.Accounts)) {
|
||||
if (isArray(accounts)) {
|
||||
AccountUserStore.accounts.forEach(oAccount =>
|
||||
counts[oAccount.email] = oAccount.count()
|
||||
);
|
||||
|
|
@ -431,10 +431,12 @@ class AppUser extends AbstractApp {
|
|||
delegateRunOnDestroy(AccountUserStore.accounts());
|
||||
|
||||
AccountUserStore.accounts(
|
||||
oData.Result.Accounts.map(
|
||||
sValue => new AccountModel(sValue, sValue !== parentEmail, counts[sValue] || 0)
|
||||
accounts.map(
|
||||
sValue => new AccountModel(sValue, counts[sValue])
|
||||
)
|
||||
);
|
||||
// accounts.length &&
|
||||
AccountUserStore.accounts.unshift(new AccountModel(mainEmail, counts[mainEmail], false));
|
||||
}
|
||||
|
||||
if (isArray(oData.Result.Identities)) {
|
||||
|
|
@ -442,9 +444,10 @@ class AppUser extends AbstractApp {
|
|||
|
||||
IdentityUserStore(
|
||||
oData.Result.Identities.map(identityData => {
|
||||
const id = pString(identityData.Id),
|
||||
email = pString(identityData.Email),
|
||||
identity = new IdentityModel(id, email);
|
||||
const identity = new IdentityModel(
|
||||
pString(identityData.Id),
|
||||
pString(identityData.Email)
|
||||
);
|
||||
|
||||
identity.name(pString(identityData.Name));
|
||||
identity.replyTo(pString(identityData.ReplyTo));
|
||||
|
|
@ -715,7 +718,6 @@ class AppUser extends AbstractApp {
|
|||
NotificationUserStore.enableDesktopNotification(!!SettingsGet('DesktopNotifications'));
|
||||
|
||||
AccountUserStore.email(SettingsGet('Email'));
|
||||
AccountUserStore.parentEmail(SettingsGet('ParentEmail'));
|
||||
|
||||
this.foldersReload(value => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -51,12 +51,6 @@ export const
|
|||
*/
|
||||
serverRequest = type => SERVER_PREFIX + '/' + type + '/' + SUB_QUERY_PREFIX + '/0/',
|
||||
|
||||
/**
|
||||
* @param {string} email
|
||||
* @returns {string}
|
||||
*/
|
||||
change = email => serverRequest('Change') + encodeURIComponent(email) + '/',
|
||||
|
||||
/**
|
||||
* @param {string} lang
|
||||
* @param {boolean} isAdmin
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { change } from 'Common/Links';
|
||||
|
||||
import { AbstractModel } from 'Knoin/AbstractModel';
|
||||
|
||||
export class AccountModel extends AbstractModel {
|
||||
|
|
@ -8,23 +6,16 @@ export class AccountModel extends AbstractModel {
|
|||
* @param {boolean=} canBeDelete = true
|
||||
* @param {number=} count = 0
|
||||
*/
|
||||
constructor(email, canBeDelete = true, count = 0) {
|
||||
constructor(email, count = 0, isAdditional = true) {
|
||||
super();
|
||||
|
||||
this.email = email;
|
||||
|
||||
this.addObservables({
|
||||
count: count,
|
||||
count: count || 0,
|
||||
deleteAccess: false,
|
||||
canBeDeleted: !!canBeDelete
|
||||
isAdditional: isAdditional
|
||||
});
|
||||
this.canBeEdit = this.canBeDeleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
changeAccountLink() {
|
||||
return change(this.email);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { Capa } from 'Common/Enums';
|
||||
import { Settings } from 'Common/Globals';
|
||||
import { Settings, SettingsGet } from 'Common/Globals';
|
||||
|
||||
import { AccountUserStore } from 'Stores/User/Account';
|
||||
import { IdentityUserStore } from 'Stores/User/Identity';
|
||||
|
|
@ -20,6 +20,7 @@ export class AccountsUserSettings /*extends AbstractViewSettings*/ {
|
|||
this.accounts = AccountUserStore.accounts;
|
||||
this.loading = AccountUserStore.loading;
|
||||
this.identities = IdentityUserStore;
|
||||
this.mainEmail = SettingsGet('MainEmail');
|
||||
|
||||
this.accountForDeletion = ko.observable(null).deleteAccessHelper();
|
||||
this.identityForDeletion = ko.observable(null).deleteAccessHelper();
|
||||
|
|
@ -30,7 +31,7 @@ export class AccountsUserSettings /*extends AbstractViewSettings*/ {
|
|||
}
|
||||
|
||||
editAccount(account) {
|
||||
if (account && account.canBeEdit()) {
|
||||
if (account && account.isAdditional()) {
|
||||
showScreenPopup(AccountPopupView, [account]);
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +82,10 @@ export class AccountsUserSettings /*extends AbstractViewSettings*/ {
|
|||
}
|
||||
|
||||
accountsAndIdentitiesAfterMove() {
|
||||
Remote.accountsAndIdentitiesSortOrder(null, AccountUserStore.getEmailAddresses(), IdentityUserStore.getIDS());
|
||||
Remote.accountsAndIdentitiesSortOrder(null,
|
||||
AccountUserStore.getEmailAddresses().filter(v => v != SettingsGet('MainEmail')),
|
||||
IdentityUserStore.getIDS()
|
||||
);
|
||||
}
|
||||
|
||||
onBuild(oDom) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export const AccountUserStore = {
|
|||
accounts: ko.observableArray(),
|
||||
loading: ko.observable(false).extend({ debounce: 100 }),
|
||||
|
||||
getEmailAddresses: () => AccountUserStore.accounts.map(item => item ? item.email : null).filter(v => v),
|
||||
getEmailAddresses: () => AccountUserStore.accounts.map(item => item.email),
|
||||
|
||||
accountsUnreadCount: ko.computed(() => 0),
|
||||
// accountsUnreadCount: ko.computed(() => {
|
||||
|
|
@ -21,6 +21,5 @@ export const AccountUserStore = {
|
|||
|
||||
addObservablesTo(AccountUserStore, {
|
||||
email: '',
|
||||
parentEmail: '',
|
||||
signature: ''
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,10 +25,11 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td + td {
|
||||
td + td + td {
|
||||
text-align: right;
|
||||
}
|
||||
td + td + td {
|
||||
td:first-child,
|
||||
td:last-child, {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class AccountPopupView extends AbstractViewPopup {
|
|||
}
|
||||
|
||||
onShow(account) {
|
||||
if (account && account.canBeEdit()) {
|
||||
if (account && account.isAdditional()) {
|
||||
this.isNew(false);
|
||||
this.email(account.email);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export class SystemDropDownUserView extends AbstractViewRight {
|
|||
if (iError) {
|
||||
AccountUserStore.loading(false);
|
||||
alert(getNotification(iError).replace('%EMAIL%', account.email));
|
||||
if (account.canBeEdit()) {
|
||||
if (account.isAdditional()) {
|
||||
showScreenPopup(AccountPopupView, [account]);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -852,7 +852,7 @@ class Actions
|
|||
'ReplySameFolder' => (bool) $oConfig->Get('defaults', 'mail_reply_same_folder', false),
|
||||
'ContactsAutosave' => (bool) $oConfig->Get('defaults', 'contacts_autosave', true),
|
||||
'HideUnsubscribed' => (bool) $oConfig->Get('labs', 'use_imap_list_subscribe', true),
|
||||
'ParentEmail' => '',
|
||||
'MainEmail' => '',
|
||||
'InterfaceAnimation' => true,
|
||||
'UserBackgroundName' => '',
|
||||
'UserBackgroundHash' => ''
|
||||
|
|
@ -920,9 +920,7 @@ class Actions
|
|||
$aResult['StartupUrl'] = $this->compileLogParams($aResult['StartupUrl'], $oAccount, true);
|
||||
}
|
||||
|
||||
if ($oAccount instanceof \RainLoop\Model\AdditionalAccount) {
|
||||
$aResult['ParentEmail'] = $oAccount->ParentEmail();
|
||||
}
|
||||
$aResult['MainEmail'] = \MailSo\Base\Utils::IdnToUtf8($this->getMainAccountFromToken()->Email());
|
||||
|
||||
$oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount);
|
||||
|
||||
|
|
@ -1048,7 +1046,6 @@ class Actions
|
|||
|
||||
// IDN
|
||||
$aResult['Email'] = \MailSo\Base\Utils::IdnToUtf8($aResult['Email']);
|
||||
$aResult['ParentEmail'] = \MailSo\Base\Utils::IdnToUtf8($aResult['ParentEmail']);
|
||||
$aResult['MailToEmail'] = \MailSo\Base\Utils::IdnToUtf8($aResult['MailToEmail']);
|
||||
$aResult['DevEmail'] = \MailSo\Base\Utils::IdnToUtf8($aResult['DevEmail']);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ trait Accounts
|
|||
StorageType::CONFIG,
|
||||
'additionalaccounts'
|
||||
);
|
||||
$aAccounts = $sAccounts ? \json_decode($sAccounts, true) : $this->ConvertInsecureAccounts($oAccount);
|
||||
$aAccounts = $sAccounts ? \json_decode($sAccounts, true) : \SnappyMail\Upgrade::ConvertInsecureAccounts($this, $oAccount);
|
||||
if ($aAccounts && \is_array($aAccounts)) {
|
||||
return $aAccounts;
|
||||
}
|
||||
|
|
@ -36,81 +36,6 @@ trait Accounts
|
|||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert the old less secure data into better secured data
|
||||
*/
|
||||
protected function ConvertInsecureAccounts(MainAccount $oMainAccount) : array
|
||||
{
|
||||
$sAccounts = $this->StorageProvider()->Get($oMainAccount, StorageType::CONFIG, 'accounts');
|
||||
if (!$sAccounts || '{' !== $sAccounts[0]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$aAccounts = \json_decode($sAccounts, true);
|
||||
if (!$aAccounts || !\is_array($aAccounts)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$aNewAccounts = [];
|
||||
if (1 < \count($aAccounts)) {
|
||||
$sOrder = $this->StorageProvider()->Get($oMainAccount, StorageType::CONFIG, 'accounts_identities_order');
|
||||
$aOrder = $sOrder ? \json_decode($sOrder, true) : [];
|
||||
if (!empty($aOrder['Accounts']) && \is_array($aOrder['Accounts']) && 1 < \count($aOrder['Accounts'])) {
|
||||
$aAccounts = \array_filter(\array_merge(
|
||||
\array_fill_keys($aOrder['Accounts'], null),
|
||||
$aAccounts
|
||||
));
|
||||
}
|
||||
$sHash = $oMainAccount->CryptKey();
|
||||
foreach ($aAccounts as $sEmail => $sToken) {
|
||||
try {
|
||||
$aNewAccounts[$sEmail] = [
|
||||
'account',
|
||||
$sEmail,
|
||||
$sEmail,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
$oMainAccount->Email(),
|
||||
\hash_hmac('sha1', '', $sHash)
|
||||
];
|
||||
if (!$sToken) {
|
||||
\error_log("ConvertInsecureAccount {$sEmail} no token");
|
||||
continue;
|
||||
}
|
||||
$aAccountHash = \RainLoop\Utils::DecodeKeyValues($sToken);
|
||||
if (empty($aAccountHash[0]) || 'token' !== $aAccountHash[0] // simple token validation
|
||||
|| 8 > \count($aAccountHash) // length checking
|
||||
) {
|
||||
\error_log("ConvertInsecureAccount {$sEmail} invalid aAccountHash: " . print_r($aAccountHash,1));
|
||||
continue;
|
||||
}
|
||||
$aAccountHash[3] = \SnappyMail\Crypt::EncryptUrlSafe($aAccountHash[3], $sHash);
|
||||
$aNewAccounts[$sEmail] = [
|
||||
'account',
|
||||
$aAccountHash[1],
|
||||
$aAccountHash[2],
|
||||
$aAccountHash[3],
|
||||
$aAccountHash[11],
|
||||
$aAccountHash[8],
|
||||
$aAccountHash[9],
|
||||
$oMainAccount->Email(),
|
||||
\hash_hmac('sha1', $aAccountHash[3], $sHash)
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
\error_log("ConvertInsecureAccount {$sEmail} failed");
|
||||
}
|
||||
}
|
||||
|
||||
$this->SetAccounts($oMainAccount, $aNewAccounts);
|
||||
}
|
||||
|
||||
$this->StorageProvider()->Clear($oMainAccount, StorageType::CONFIG, 'accounts');
|
||||
|
||||
return $aNewAccounts;
|
||||
}
|
||||
|
||||
protected function SetAccounts(MainAccount $oAccount, array $aAccounts = array()): void
|
||||
{
|
||||
$sParentEmail = $oAccount->Email();
|
||||
|
|
@ -206,7 +131,7 @@ trait Accounts
|
|||
$aResult['IncLogin'] = $oAccount->IncLogin();
|
||||
$aResult['OutLogin'] = $oAccount->OutLogin();
|
||||
$aResult['AccountHash'] = $oAccount->Hash();
|
||||
$aResult['ParentEmail'] = ($oAccount instanceof \RainLoop\Model\AdditionalAccount)
|
||||
$aResult['MainEmail'] = ($oAccount instanceof \RainLoop\Model\AdditionalAccount)
|
||||
? $oAccount->ParentEmail() : '';
|
||||
$aResult['ContactsIsAllowed'] = $this->AddressBookProvider($oAccount)->IsActive();
|
||||
$oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount);
|
||||
|
|
@ -301,19 +226,11 @@ trait Accounts
|
|||
{
|
||||
$oAccount = $this->getMainAccountFromToken();
|
||||
|
||||
$aAccounts = false;
|
||||
if ($this->GetCapa(false, Capa::ADDITIONAL_ACCOUNTS, $oAccount)) {
|
||||
$aAccounts = \array_map(
|
||||
return $this->DefaultResponse(__FUNCTION__, array(
|
||||
'Accounts' => \array_map(
|
||||
'MailSo\\Base\\Utils::IdnToUtf8',
|
||||
\array_keys($this->GetAccounts($oAccount))
|
||||
);
|
||||
if ($aAccounts) {
|
||||
\array_unshift($aAccounts, \MailSo\Base\Utils::IdnToUtf8($oAccount->Email()));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__, array(
|
||||
'Accounts' => $aAccounts,
|
||||
),
|
||||
'Identities' => $this->GetIdentities($oAccount)
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,17 +278,7 @@ trait Contacts
|
|||
return $aData;
|
||||
}
|
||||
|
||||
// Try the old
|
||||
$aData = \RainLoop\Utils::DecodeKeyValues($sData);
|
||||
if ($aData) {
|
||||
$this->setContactsSyncData($oAccount, $aData);
|
||||
return array(
|
||||
'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false,
|
||||
'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '',
|
||||
'User' => isset($aData['User']) ? \trim($aData['User']) : '',
|
||||
'Password' => isset($aData['Password']) ? $aData['Password'] : ''
|
||||
);
|
||||
}
|
||||
return \SnappyMail\Upgrade::ConvertInsecureContactsSync($this, $oAccount);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -811,22 +811,6 @@ class ServiceActions
|
|||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to AdditionalAccount
|
||||
*/
|
||||
public function ServiceChange() : string
|
||||
{
|
||||
try {
|
||||
$this->oActions->switchAccount(
|
||||
empty($this->aPaths[2]) ? '' : \urldecode(\trim($this->aPaths[2]))
|
||||
);
|
||||
$this->oActions->Location('./');
|
||||
} catch (\Exception $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
|
|
|
|||
114
snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php
Normal file
114
snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
namespace SnappyMail;
|
||||
|
||||
use RainLoop\Providers\Storage\Enumerations\StorageType;
|
||||
|
||||
abstract class Upgrade
|
||||
{
|
||||
|
||||
/**
|
||||
* Attempt to convert the old less secure data into better secured data
|
||||
*/
|
||||
public function ConvertInsecureAccounts(\RainLoop\Actions $oActions, \RainLoop\Model\MainAccount $oMainAccount) : array
|
||||
{
|
||||
$oStorage = $oActions->StorageProvider();
|
||||
$sAccounts = $oStorage->Get($oMainAccount, StorageType::CONFIG, 'accounts');
|
||||
if (!$sAccounts || '{' !== $sAccounts[0]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$aAccounts = \json_decode($sAccounts, true);
|
||||
if (!$aAccounts || !\is_array($aAccounts)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$aNewAccounts = [];
|
||||
if (1 < \count($aAccounts)) {
|
||||
$sOrder = $oStorage->Get($oMainAccount, StorageType::CONFIG, 'accounts_identities_order');
|
||||
$aOrder = $sOrder ? \json_decode($sOrder, true) : [];
|
||||
if (!empty($aOrder['Accounts']) && \is_array($aOrder['Accounts']) && 1 < \count($aOrder['Accounts'])) {
|
||||
$aAccounts = \array_filter(\array_merge(
|
||||
\array_fill_keys($aOrder['Accounts'], null),
|
||||
$aAccounts
|
||||
));
|
||||
}
|
||||
$sHash = $oMainAccount->CryptKey();
|
||||
foreach ($aAccounts as $sEmail => $sToken) {
|
||||
try {
|
||||
$aNewAccounts[$sEmail] = [
|
||||
'account',
|
||||
$sEmail,
|
||||
$sEmail,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
$oMainAccount->Email(),
|
||||
\hash_hmac('sha1', '', $sHash)
|
||||
];
|
||||
if (!$sToken) {
|
||||
\error_log("ConvertInsecureAccount {$sEmail} no token");
|
||||
continue;
|
||||
}
|
||||
$aAccountHash = \RainLoop\Utils::DecodeKeyValues($sToken);
|
||||
if (empty($aAccountHash[0]) || 'token' !== $aAccountHash[0] // simple token validation
|
||||
|| 8 > \count($aAccountHash) // length checking
|
||||
) {
|
||||
\error_log("ConvertInsecureAccount {$sEmail} invalid aAccountHash: " . \print_r($aAccountHash,1));
|
||||
continue;
|
||||
}
|
||||
$aAccountHash[3] = Crypt::EncryptUrlSafe($aAccountHash[3], $sHash);
|
||||
$aNewAccounts[$sEmail] = [
|
||||
'account',
|
||||
$aAccountHash[1],
|
||||
$aAccountHash[2],
|
||||
$aAccountHash[3],
|
||||
$aAccountHash[11],
|
||||
$aAccountHash[8],
|
||||
$aAccountHash[9],
|
||||
$oMainAccount->Email(),
|
||||
\hash_hmac('sha1', $aAccountHash[3], $sHash)
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
\error_log("ConvertInsecureAccount {$sEmail} failed");
|
||||
}
|
||||
}
|
||||
|
||||
$oActions->SetAccounts($oMainAccount, $aNewAccounts);
|
||||
}
|
||||
|
||||
$oStorage->Clear($oMainAccount, StorageType::CONFIG, 'accounts');
|
||||
|
||||
return $aNewAccounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert the old less secure data into better secured data
|
||||
*/
|
||||
public function ConvertInsecureContactsSync(\RainLoop\Actions $oActions, \RainLoop\Model\Account $oAccount) : ?array
|
||||
{
|
||||
$sData = $oActions->StorageProvider()->Get($oAccount,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
|
||||
'contacts_sync'
|
||||
);
|
||||
|
||||
if (!empty($sData)) {
|
||||
$aData = \json_decode($sData);
|
||||
if (!$aData) {
|
||||
$aData = \RainLoop\Utils::DecodeKeyValues($sData);
|
||||
if ($aData) {
|
||||
$oActions->setContactsSyncData($oAccount, $aData);
|
||||
return array(
|
||||
'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false,
|
||||
'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '',
|
||||
'User' => isset($aData['User']) ? \trim($aData['User']) : '',
|
||||
'Password' => isset($aData['Password']) ? $aData['Password'] : ''
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,20 +9,28 @@
|
|||
<span data-i18n="SETTINGS_ACCOUNTS/BUTTON_ADD_ACCOUNT"></span>
|
||||
</a>
|
||||
<table class="table table-hover list-table accounts-list" data-bind="i18nUpdate: accounts">
|
||||
<tbody>
|
||||
<td></td>
|
||||
<td colspan="3" data-bind="text: mainEmail"></td>
|
||||
</tbody>
|
||||
<tbody data-bind="foreach: accounts">
|
||||
<!-- ko if:isAdditional -->
|
||||
<tr draggable="true" data-bind="sortableItem: { list: $root.accounts, afterMove: $root.accountsAndIdentitiesAfterMove }">
|
||||
<td class="e-action" data-bind="css: {'e-action': canBeEdit}">
|
||||
<td>
|
||||
<i class="fontastic drag-handle">⬍</i>
|
||||
</td>
|
||||
<td class="e-action">
|
||||
<span class="account-name" data-bind="text: email"></span>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-small btn-small-small btn-danger button-confirm-delete" data-bind="visible: canBeDeleted, css: {'delete-access': deleteAccess}, click: function(oAccount) { $root.deleteAccount(oAccount); }"
|
||||
<a class="btn btn-small btn-small-small btn-danger button-confirm-delete" data-bind="css: {'delete-access': deleteAccess}, click: function(oAccount) { $root.deleteAccount(oAccount); }"
|
||||
data-i18n="GLOBAL/ARE_YOU_SURE"></a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete fontastic" data-bind="visible: !deleteAccess() && canBeDeleted(), click: function (oAccount) { $root.accountForDeletion(oAccount); }">🗑</span>
|
||||
<span class="delete fontastic" data-bind="visible: !deleteAccess(), click: function (oAccount) { $root.accountForDeletion(oAccount); }">🗑</span>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- /ko -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@
|
|||
<!-- ko if: accounts().length -->
|
||||
<!-- ko foreach: accounts -->
|
||||
<li role="presentation">
|
||||
<a class="account-item" href="#" data-bind="click: $root.accountClick,
|
||||
attr: {'href': changeAccountLink()}, css: {'current': $root.accountEmail() === email}">
|
||||
<a class="account-item" href="#" data-bind="click: $root.accountClick, css: {'current': $root.accountEmail() === email}">
|
||||
<!-- <b class="pull-right counter" data-bind="visible: 0 < count()">
|
||||
<span data-bind="text: count, visible: 100 > count()"></span>
|
||||
<span data-bind="visible: 99 < count()">99+</span>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue