mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 15:45:55 +08:00
Improved Kolab AddressBook/Contacts
This commit is contained in:
parent
5c9b7d7000
commit
824311937a
|
@ -4,6 +4,7 @@ import { koComputable } from 'External/ko';
|
|||
import { SettingsGet } from 'Common/Globals';
|
||||
import { i18n, trigger as translatorTrigger } from 'Common/Translator';
|
||||
import { ContactUserStore } from 'Stores/User/Contact';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
export class UserSettingsContacts /*extends AbstractViewSettings*/ {
|
||||
|
@ -48,5 +49,33 @@ export class UserSettingsContacts /*extends AbstractViewSettings*/ {
|
|||
Password: ContactUserStore.syncPass()
|
||||
})
|
||||
);
|
||||
|
||||
this.kolabContactFolder = ko.observable(SettingsGet('KolabContactFolder'));
|
||||
this.kolabContactFolder.subscribe(value =>
|
||||
Remote.saveSettings(null, { KolabContactFolder: value })
|
||||
);
|
||||
this.showKolab = FolderUserStore.allowKolab();
|
||||
this.folderSelectList = koComputable(() => {
|
||||
const
|
||||
aResult = [{
|
||||
id: '',
|
||||
name: '',
|
||||
}],
|
||||
foldersWalk = folders => {
|
||||
folders.forEach(oItem => {
|
||||
if ('contact' === oItem.kolabType()) {
|
||||
aResult.push({
|
||||
id: oItem.fullName,
|
||||
name: oItem.fullName
|
||||
});
|
||||
}
|
||||
if (oItem.subFolders.length) {
|
||||
foldersWalk(oItem.subFolders());
|
||||
}
|
||||
});
|
||||
};
|
||||
foldersWalk(FolderUserStore.folderList());
|
||||
return aResult;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import { koComputable } from 'External/ko';
|
|||
|
||||
import { Notification } from 'Common/Enums';
|
||||
import { FolderMetadataKeys } from 'Common/EnumsUser';
|
||||
import { SettingsCapa } from 'Common/Globals';
|
||||
import { getNotification } from 'Common/Translator';
|
||||
|
||||
import { setFolder, getFolderFromCacheList, removeFolderFromCacheList } from 'Common/Cache';
|
||||
|
@ -26,7 +25,7 @@ const folderForDeletion = ko.observable(null).askDeleteHelper();
|
|||
|
||||
export class UserSettingsFolders /*extends AbstractViewSettings*/ {
|
||||
constructor() {
|
||||
this.showKolab = koComputable(() => FolderUserStore.hasCapability('METADATA') && SettingsCapa('Kolab'));
|
||||
this.showKolab = FolderUserStore.allowKolab();
|
||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||
this.kolabTypeOptions = ko.observableArray();
|
||||
let i18nFilter = key => i18n('SETTINGS_FOLDERS/TYPE_' + key);
|
||||
|
|
|
@ -6,7 +6,7 @@ import { UNUSED_OPTION_VALUE } from 'Common/Consts';
|
|||
import { forEachObjectEntry } from 'Common/Utils';
|
||||
import { addObservablesTo, addSubscribablesTo, addComputablesTo } from 'External/ko';
|
||||
import { getFolderInboxName, getFolderFromCacheList } from 'Common/Cache';
|
||||
import { Settings } from 'Common/Globals';
|
||||
import { Settings, SettingsCapa } from 'Common/Globals';
|
||||
//import Remote from 'Remote/User/Fetch'; // Circular dependency
|
||||
|
||||
export const FolderUserStore = new class {
|
||||
|
@ -117,6 +117,10 @@ export const FolderUserStore = new class {
|
|||
return this.capabilities().includes(name);
|
||||
}
|
||||
|
||||
allowKolab() {
|
||||
return FolderUserStore.hasCapability('METADATA') && SettingsCapa('Kolab');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array}
|
||||
*/
|
||||
|
|
|
@ -248,26 +248,8 @@ class Actions
|
|||
break;
|
||||
case 'address-book':
|
||||
// Providers\AddressBook\AddressBookInterface
|
||||
$sDsn = \trim($this->oConfig->Get('contacts', 'pdo_dsn', ''));
|
||||
$sUser = \trim($this->oConfig->Get('contacts', 'pdo_user', ''));
|
||||
$sPassword = (string)$this->oConfig->Get('contacts', 'pdo_password', '');
|
||||
$sDsnType = Providers\AddressBook\PdoAddressBook::validPdoType($this->oConfig->Get('contacts', 'type', 'sqlite'));
|
||||
if ('sqlite' === $sDsnType) {
|
||||
$sUser = $sPassword = '';
|
||||
$sDsn = 'sqlite:' . APP_PRIVATE_DATA . 'AddressBook.sqlite';
|
||||
/*
|
||||
// TODO: use local db?
|
||||
$homedir = $this->StorageProvider()->GenerateFilePath(
|
||||
$oAccount,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::ROOT
|
||||
);
|
||||
$sDsn = 'sqlite:' . $homedir . '/AddressBook.sqlite';
|
||||
*/
|
||||
} else {
|
||||
$sDsn = $sDsnType . ':' . \preg_replace('/^[a-z]+:/', '', $sDsn);
|
||||
}
|
||||
$mResult = new Providers\AddressBook\PdoAddressBook($sDsn, $sUser, $sPassword, $sDsnType);
|
||||
// $mResult = new Providers\AddressBook\KolabAddressBook($this->MailClient()->ImapClient());
|
||||
$mResult = new Providers\AddressBook\PdoAddressBook();
|
||||
// $mResult = new Providers\AddressBook\KolabAddressBook();
|
||||
break;
|
||||
case 'identities':
|
||||
case 'suggestions':
|
||||
|
@ -750,7 +732,7 @@ class Actions
|
|||
'ContactsAutosave' => (bool) $oConfig->Get('defaults', 'contacts_autosave', true),
|
||||
'HideUnsubscribed' => false,
|
||||
'MainEmail' => '',
|
||||
'InterfaceAnimation' => true,
|
||||
'KolabContactFolder' => '',
|
||||
'UserBackgroundName' => '',
|
||||
'UserBackgroundHash' => ''
|
||||
);
|
||||
|
@ -838,6 +820,7 @@ class Actions
|
|||
$aResult['HideUnsubscribed'] = (bool)$oSettingsLocal->GetConf('HideUnsubscribed', $aResult['HideUnsubscribed']);
|
||||
$aResult['UseThreads'] = (bool)$oSettingsLocal->GetConf('UseThreads', $aResult['UseThreads']);
|
||||
$aResult['ReplySameFolder'] = (bool)$oSettingsLocal->GetConf('ReplySameFolder', $aResult['ReplySameFolder']);
|
||||
$aResult['KolabContactFolder'] = (string)$oSettingsLocal->GetConf('KolabContactFolder', $aResult['KolabContactFolder']);
|
||||
}
|
||||
|
||||
if ($oConfig->Get('login', 'determine_user_language', true)) {
|
||||
|
@ -1166,7 +1149,7 @@ class Actions
|
|||
'DangerousActions' => (bool) $oConfig->Get('capa', 'dangerous_actions', true),
|
||||
'GnuPG' => (bool) $oConfig->Get('security', 'openpgp', false) && \SnappyMail\PGP\GnuPG::isSupported(),
|
||||
'Identities' => (bool) $oConfig->Get('webmail', 'allow_additional_identities', false),
|
||||
'Kolab' => (bool) $oConfig->Get('labs', 'kolab_enabled', false),
|
||||
'Kolab' => (bool) $oConfig->Get('labs', 'kolab_enabled', false) /* && ImapClient->IsSupported('METADATA')*/,
|
||||
'MessageActions' => (bool) $oConfig->Get('capa', 'message_actions', true),
|
||||
'OpenPGP' => (bool) $oConfig->Get('security', 'openpgp', false),
|
||||
'Prefetch' => (bool) $oConfig->Get('labs', 'allow_prefetch', false),
|
||||
|
|
|
@ -345,6 +345,7 @@ trait User
|
|||
$this->setSettingsFromParams($oSettingsLocal, 'UseThreads', 'bool');
|
||||
$this->setSettingsFromParams($oSettingsLocal, 'ReplySameFolder', 'bool');
|
||||
$this->setSettingsFromParams($oSettingsLocal, 'HideUnsubscribed', 'bool');
|
||||
$this->setSettingsFromParams($oSettingsLocal, 'KolabContactFolder', 'string');
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__,
|
||||
$this->SettingsProvider()->Save($oAccount, $oSettings) &&
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace RainLoop\Providers\AddressBook;
|
|||
|
||||
use RainLoop\Providers\AddressBook\Enumerations\PropertyType;
|
||||
|
||||
class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInterface
|
||||
class KolabAddressBook implements AddressBookInterface
|
||||
{
|
||||
use CardDAV;
|
||||
|
||||
|
@ -12,27 +12,54 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt
|
|||
$oImapClient,
|
||||
$sFolderName;
|
||||
|
||||
function __construct(\MailSo\Imap\ImapClient $oImapClient)
|
||||
protected function ImapClient() : \MailSo\Imap\ImapClient
|
||||
{
|
||||
$this->oImapClient = $oImapClient;
|
||||
if (!$this->oImapClient /*&&\RainLoop\Api::Config()->Get('labs', 'kolab_enabled', false)*/) {
|
||||
$oActions = \RainLoop\Api::Actions();
|
||||
$oMailClient = $oActions->MailClient();
|
||||
if (!$oMailClient->IsLoggined()) {
|
||||
$oActions->getAccountFromToken()->IncConnectAndLoginHelper($oActions->Plugins(), $oMailClient, $oActions->Config());
|
||||
}
|
||||
$this->oImapClient = $oMailClient->ImapClient();
|
||||
}
|
||||
return $this->oImapClient;
|
||||
}
|
||||
|
||||
public function SetFolder(string $sFolderName) : bool
|
||||
public function FolderName() : string
|
||||
{
|
||||
$metadata = $this->oImapClient->FolderGetMetadata($sFolderName, [\MailSo\Imap\Enumerations\MetadataKeys::KOLAB_CTYPE]);
|
||||
if ($metadata && 'contact' !== \array_shift($metadata)) {
|
||||
// Throw error
|
||||
// $this->oImapClient->FolderList() : array
|
||||
return false;
|
||||
if (!\is_string($this->sFolderName)) {
|
||||
$oActions = \RainLoop\Api::Actions();
|
||||
$oAccount = $oActions->getAccountFromToken();
|
||||
$this->sFolderName = (string) $oActions->SettingsProvider(true)->Load($oAccount)->GetConf('KolabContactFolder', '');
|
||||
}
|
||||
$this->oImapClient->FolderSelect($sFolderName);
|
||||
$this->sFolderName = $sFolderName;
|
||||
return true;
|
||||
return $this->sFolderName;
|
||||
}
|
||||
|
||||
public function SelectFolder() : bool
|
||||
{
|
||||
try {
|
||||
$sFolderName = $this->FolderName();
|
||||
if (!$sFolderName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$metadata = $this->ImapClient()->FolderGetMetadata($sFolderName, [\MailSo\Imap\Enumerations\MetadataKeys::KOLAB_CTYPE]);
|
||||
if (!$metadata || 'contact' !== \array_shift($metadata)) {
|
||||
throw new \Exception("Invalid kolab contact folder: {$sFolderName}");
|
||||
}
|
||||
|
||||
$this->ImapClient()->FolderSelect($sFolderName);
|
||||
$this->sFolderName = $sFolderName;
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
\trigger_error("KolabAddressBook {$sFolderName} error: {$e->getMessage()}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function IsSupported() : bool
|
||||
{
|
||||
// Check $this->oImapClient->IsSupported('METADATA')
|
||||
// Check $this->ImapClient()->IsSupported('METADATA')
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -58,6 +85,10 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt
|
|||
// TODO
|
||||
// $emails = $oContact->GetEmails();
|
||||
|
||||
if (!$this->SelectFolder()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oContact->PopulateDisplayAndFullNameValue();
|
||||
|
||||
$sUID = $oContact->GetUID();
|
||||
|
@ -88,27 +119,27 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt
|
|||
$oMessage->SubParts->append($oPart);
|
||||
|
||||
// Search in IMAP folder:
|
||||
$aUids = $this->oImapClient->MessageSimpleSearch("SUBJECT {$sUID}");
|
||||
$aUids = $this->ImapClient()->MessageSimpleSearch("SUBJECT {$sUID}");
|
||||
/*
|
||||
$email = \MailSo\Imap\SearchCriterias::escapeSearchString($this->oImapClient, $sEmail);
|
||||
$aUids = $this->oImapClient->MessageSimpleSearch("OR SUBJECT {$sUID} FROM {$email}");
|
||||
$aUids = $this->oImapClient->MessageSimpleSearch("OR SUBJECT {$sUID} FROM {$email} BODY {$email}");
|
||||
$email = \MailSo\Imap\SearchCriterias::escapeSearchString($this->ImapClient(), $sEmail);
|
||||
$aUids = $this->ImapClient()->MessageSimpleSearch("OR SUBJECT {$sUID} FROM {$email}");
|
||||
$aUids = $this->ImapClient()->MessageSimpleSearch("OR SUBJECT {$sUID} FROM {$email} BODY {$email}");
|
||||
|
||||
$aUids = $this->oImapClient->MessageSimpleSearch('HEADER Subject '.$sUID);
|
||||
$aUids = $this->ImapClient()->MessageSimpleSearch('HEADER Subject '.$sUID);
|
||||
return 1 === \count($aUids) && \is_numeric($aUids[0]) ? (int) $aUids[0] : null;
|
||||
*/
|
||||
|
||||
if ($aUids) {
|
||||
// Replace Message
|
||||
if (false && $this->oImapClient->IsSupported('REPLACE')) {
|
||||
if (false && $this->ImapClient()->IsSupported('REPLACE')) {
|
||||
// UID REPLACE
|
||||
} else {
|
||||
$oRange = new \MailSo\Imap\SequenceSet($aUids[0]);
|
||||
$this->oImapClient->MessageStoreFlag($oRange,
|
||||
$this->ImapClient()->MessageStoreFlag($oRange,
|
||||
array(\MailSo\Imap\Enumerations\MessageFlag::DELETED),
|
||||
\MailSo\Imap\Enumerations\StoreAction::ADD_FLAGS_SILENT
|
||||
);
|
||||
$this->oImapClient->FolderExpunge($oRange);
|
||||
$this->ImapClient()->FolderExpunge($oRange);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +149,7 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt
|
|||
$oMessage->ToStream(false), array($rMessageStream), 8192, true, true);
|
||||
if (false !== $iMessageStreamSize) {
|
||||
\rewind($rMessageStream);
|
||||
$this->oImapClient->MessageAppendStream($this->sFolderName, $rMessageStream, $iMessageStreamSize);
|
||||
$this->ImapClient()->MessageAppendStream($this->sFolderName, $rMessageStream, $iMessageStreamSize);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -138,8 +169,76 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt
|
|||
|
||||
public function GetContacts(string $sEmail, int $iOffset = 0, int $iLimit = 20, string $sSearch = '', int &$iResultCount = 0) : array
|
||||
{
|
||||
// TODO
|
||||
return [];
|
||||
$oParams = new \MailSo\Mail\MessageListParams;
|
||||
$oParams->sFolderName = $this->FolderName();
|
||||
$oParams->iOffset = $iOffset;
|
||||
$oParams->iLimit = $iLimit;
|
||||
if ($sSearch) {
|
||||
$oParams->sSearch = 'from='.$sSearch;
|
||||
}
|
||||
$oParams->sSort = 'FROM';
|
||||
// $oParams->iPrevUidNext = $this->GetActionParam('UidNext', 0);
|
||||
// $oParams->bUseThreads = false;
|
||||
|
||||
if (!\strlen($oParams->sFolderName)) {
|
||||
// return [];
|
||||
throw new ClientException(Notifications::CantGetMessageList);
|
||||
}
|
||||
|
||||
$this->ImapClient();
|
||||
|
||||
$aResult = [];
|
||||
|
||||
try
|
||||
{
|
||||
$oMessageList = \RainLoop\Api::Actions()->MailClient()->MessageList($oParams);
|
||||
foreach ($oMessageList as $oMessage) {
|
||||
$oContact = new Classes\Contact;
|
||||
$oContact->IdContact = $oMessage->Uid();
|
||||
$oContact->IdContactStr = $oMessage->Subject();
|
||||
// $oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : '';
|
||||
$oContact->Changed = $oMessage->HeaderTimeStampInUTC();
|
||||
|
||||
$oFrom = $oMessage->From();
|
||||
if ($oFrom) {
|
||||
$oMail = $oFrom[0];
|
||||
$oProperty = new Classes\Property(PropertyType::EMAIl, $oMail->GetEmail());
|
||||
$oContact->Properties[] = $oProperty;
|
||||
$oProperty = new Classes\Property(PropertyType::FULLNAME, $oMail->GetDisplayName());
|
||||
// $oProperty = new Classes\Property(PropertyType::FULLNAME, $oMail->ToString());
|
||||
$oContact->Properties[] = $oProperty;
|
||||
// $oProperty = new Classes\Property(PropertyType::NICK_NAME, $oMail->GetDisplayName());
|
||||
// $oContact->Properties[] = $oProperty;
|
||||
|
||||
$oContact->UpdateDependentValues();
|
||||
$aResult[] = $oContact;
|
||||
/*
|
||||
// TODO extract xCard attachment
|
||||
$oMessage->ContentType() = multipart/mixed
|
||||
$oMessage->Attachments() : ?AttachmentCollection
|
||||
[0] => MailSo\Mail\Attachment(
|
||||
[oBodyStructure:MailSo\Mail\Attachment:private] => MailSo\Imap\BodyStructure(
|
||||
[sContentType:MailSo\Imap\BodyStructure:private] => application/vcard+xml
|
||||
[sCharset:MailSo\Imap\BodyStructure:private] =>
|
||||
[aBodyParams:MailSo\Imap\BodyStructure:private] => Array(
|
||||
[name] => kolab.xml
|
||||
)
|
||||
[sMailEncodingName:MailSo\Imap\BodyStructure:private] => quoted-printable
|
||||
[sDisposition:MailSo\Imap\BodyStructure:private] => attachment
|
||||
[sFileName:MailSo\Imap\BodyStructure:private] => kolab.xml
|
||||
[iSize:MailSo\Imap\BodyStructure:private] => 1043
|
||||
[sPartID:MailSo\Imap\BodyStructure:private] => 2
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (\Throwable $oException)
|
||||
{
|
||||
throw $oException;
|
||||
throw new ClientException(Notifications::CantGetMessageList, $oException);
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
public function GetContactByID(string $sEmail, $mID, bool $bIsStrID = false) : ?Classes\Contact
|
||||
|
@ -151,18 +250,18 @@ class KolabAddressBook implements \RainLoop\Providers\AddressBook\AddressBookInt
|
|||
public function GetSuggestions(string $sEmail, string $sSearch, int $iLimit = 20) : array
|
||||
{
|
||||
$sSearch = \trim($sSearch);
|
||||
if (2 > \strlen($sSearch) || !$this->SetFolder(/*TODO 'Contacts'*/)) {
|
||||
if (2 > \strlen($sSearch) || !$this->SelectFolder()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sSearch = \MailSo\Imap\SearchCriterias::escapeSearchString($this->oImapClient, $sSearch);
|
||||
$sSearch = \MailSo\Imap\SearchCriterias::escapeSearchString($this->ImapClient(), $sSearch);
|
||||
$aUids = \array_slice(
|
||||
$this->oImapClient->MessageSimpleSearch("FROM {$sSearch}"),
|
||||
$this->ImapClient()->MessageSimpleSearch("FROM {$sSearch}"),
|
||||
0, $iLimit
|
||||
);
|
||||
|
||||
$aResult = [];
|
||||
foreach ($this->oImapClient->Fetch(['BODY.PEEK[HEADER.FIELDS (FROM)]'], \implode(',', $aUids), true) as $oFetchResponse) {
|
||||
foreach ($this->ImapClient()->Fetch(['BODY.PEEK[HEADER.FIELDS (FROM)]'], \implode(',', $aUids), true) as $oFetchResponse) {
|
||||
$oHeaders = new \MailSo\Mime\HeaderCollection($oFetchResponse->GetHeaderFieldsValue());
|
||||
$oFrom = $oHeaders->GetAsEmailCollection(\MailSo\Mime\Enumerations\Header::FROM_, true);
|
||||
foreach ($oFrom as $oMail) {
|
||||
|
|
|
@ -30,8 +30,28 @@ class PdoAddressBook
|
|||
*/
|
||||
private $sPassword;
|
||||
|
||||
public function __construct(string $sDsn, string $sUser = '', string $sPassword = '', string $sDsnType = 'mysql')
|
||||
public function __construct()
|
||||
{
|
||||
$oConfig = \RainLoop\Api::Config();
|
||||
$sDsnType = static::validPdoType($oConfig->Get('contacts', 'type', 'sqlite'));
|
||||
if ('sqlite' === $sDsnType) {
|
||||
$sUser = $sPassword = '';
|
||||
$sDsn = 'sqlite:' . APP_PRIVATE_DATA . 'AddressBook.sqlite';
|
||||
/*
|
||||
// TODO: use local db?
|
||||
$homedir = \RainLoop\Api::Actions()->StorageProvider()->GenerateFilePath(
|
||||
$oAccount,
|
||||
\RainLoop\Providers\Storage\Enumerations\StorageType::ROOT
|
||||
);
|
||||
$sDsn = 'sqlite:' . $homedir . '/AddressBook.sqlite';
|
||||
*/
|
||||
} else {
|
||||
$sDsn = \trim($oConfig->Get('contacts', 'pdo_dsn', ''));
|
||||
$sUser = \trim($oConfig->Get('contacts', 'pdo_user', ''));
|
||||
$sPassword = (string)$oConfig->Get('contacts', 'pdo_password', '');
|
||||
$sDsn = $sDsnType . ':' . \preg_replace('/^[a-z]+:/', '', $sDsn);
|
||||
}
|
||||
|
||||
$this->sDsn = $sDsn;
|
||||
$this->sUser = $sUser;
|
||||
$this->sPassword = $sPassword;
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "إرسال الرسالة",
|
||||
"LABEL_CLOSE_COMPOSE": "إغلاق الرسالة"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "خاطىء token",
|
||||
"AUTH_ERROR": "خطأ في كلمة السر \\اسم المستخدم",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Изпрати съобщението",
|
||||
"LABEL_CLOSE_COMPOSE": "Затвори създаването"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Невалиден токен",
|
||||
"AUTH_ERROR": "Неуспешно удостоверяване",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Odeslat zprávu",
|
||||
"LABEL_CLOSE_COMPOSE": "Zavřít okno se zprávou"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Neplatný token",
|
||||
"AUTH_ERROR": "Ověření selhalo",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Send meddelelse",
|
||||
"LABEL_CLOSE_COMPOSE": "Luk forfatter mode"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Ugyldigt tegn",
|
||||
"AUTH_ERROR": "Godkendelse fejlede",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Nachricht senden",
|
||||
"LABEL_CLOSE_COMPOSE": "Popup schließen"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Ungültiger Token",
|
||||
"AUTH_ERROR": "Authentifizierung fehlgeschlagen",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Send message",
|
||||
"LABEL_CLOSE_COMPOSE": "Close compose"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Invalid token",
|
||||
"AUTH_ERROR": "Authentication failed",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Send message",
|
||||
"LABEL_CLOSE_COMPOSE": "Close compose"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Invalid token",
|
||||
"AUTH_ERROR": "Authentication failed",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Enviar mensaje",
|
||||
"LABEL_CLOSE_COMPOSE": "Cerrar componer"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Elemento no válido",
|
||||
"AUTH_ERROR": "Autentificación fallida",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Saada kiri",
|
||||
"LABEL_CLOSE_COMPOSE": "Sulge kirja koostamise aken"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Puudulik tõend",
|
||||
"AUTH_ERROR": "Autentimine ebaõnnestus",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "فرستادن پیام",
|
||||
"LABEL_CLOSE_COMPOSE": "بستن صفحه ایجاد"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "توکن نامعتبر",
|
||||
"AUTH_ERROR": "احراز هویت با موفقیت همراه نبود",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Lähetä viesti",
|
||||
"LABEL_CLOSE_COMPOSE": "Sule luo viesti"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Väärä suojaustunnus",
|
||||
"AUTH_ERROR": "Tunnistusvirhe",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Envoyer le message",
|
||||
"LABEL_CLOSE_COMPOSE": "Fermer la fenêtre d'édition"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Jeton invalide",
|
||||
"AUTH_ERROR": "L'authentification a échoué",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Üzenet küldés",
|
||||
"LABEL_CLOSE_COMPOSE": "Levélírás bezárása"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Érvénytelen kulcs",
|
||||
"AUTH_ERROR": "Érvénytelen hitelesítés",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Kirim pesan",
|
||||
"LABEL_CLOSE_COMPOSE": "Tutup buat pesan"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Token tidak sah",
|
||||
"AUTH_ERROR": "Otentikasi gagal",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Senda skilaboð",
|
||||
"LABEL_CLOSE_COMPOSE": "Loka ritli"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Rangt tákn",
|
||||
"AUTH_ERROR": "Auðkenning mistókst",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Invia messaggio",
|
||||
"LABEL_CLOSE_COMPOSE": "Chiudi finestra di scrittura"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Token invalido",
|
||||
"AUTH_ERROR": "Autenticazione fallita",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "メッセージを送信",
|
||||
"LABEL_CLOSE_COMPOSE": "作成画面を閉じる"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "無効なトークン",
|
||||
"AUTH_ERROR": "認証に失敗しました",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "메시지 보내기",
|
||||
"LABEL_CLOSE_COMPOSE": "쓰기 창 닫기"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "잘못된 토큰입니다.",
|
||||
"AUTH_ERROR": "인증에 실패했습니다",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Siųsti laišką",
|
||||
"LABEL_CLOSE_COMPOSE": "Uždaryti laišką"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Neteisingas raktas",
|
||||
"AUTH_ERROR": "Autorizacija nepavyko",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Send message",
|
||||
"LABEL_CLOSE_COMPOSE": "Close compose"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Nepareizs tokens",
|
||||
"AUTH_ERROR": "Autorizācija neizdevās",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Send melding",
|
||||
"LABEL_CLOSE_COMPOSE": "Lukk ny melding"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Ugyldig info",
|
||||
"AUTH_ERROR": "Autentisering mislyktes",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Bericht versturen",
|
||||
"LABEL_CLOSE_COMPOSE": "Nieuw bericht venster sluiten"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Ongeldige token",
|
||||
"AUTH_ERROR": "Authenticatie mislukt",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Wyślij wiadomość",
|
||||
"LABEL_CLOSE_COMPOSE": "Zamknij okno tworzenia wiadomości"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Nieprawidłowy token",
|
||||
"AUTH_ERROR": "Błąd autoryzacji",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Enviar mensagem",
|
||||
"LABEL_CLOSE_COMPOSE": "Fechar composição"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Senha inválida",
|
||||
"AUTH_ERROR": "Falha na autenticação",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Enviar mensagem",
|
||||
"LABEL_CLOSE_COMPOSE": "Fechar janela de composição"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Token inválido",
|
||||
"AUTH_ERROR": "Falha na autenticação",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Send message",
|
||||
"LABEL_CLOSE_COMPOSE": "Close compose"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Semn invalid",
|
||||
"AUTH_ERROR": "Nu se poate realiza conectarea",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Отправить сообщение",
|
||||
"LABEL_CLOSE_COMPOSE": "Закрыть сообщение"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Неверный токен запроса",
|
||||
"AUTH_ERROR": "Не удалось авторизоваться",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Send message",
|
||||
"LABEL_CLOSE_COMPOSE": "Close compose"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Neplatný token",
|
||||
"AUTH_ERROR": "Overenie zlyhalo",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Pošlji sporočilo",
|
||||
"LABEL_CLOSE_COMPOSE": "Zapri sestavljanje"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Neveljaven žeton",
|
||||
"AUTH_ERROR": "Spodletelo overjanje",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Skicka meddelande",
|
||||
"LABEL_CLOSE_COMPOSE": "Stäng fönster"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Ogiltigt tecken",
|
||||
"AUTH_ERROR": "Autentisering misslyckades",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Mesajı Gönder",
|
||||
"LABEL_CLOSE_COMPOSE": "Oluşturulanı Kapat"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Geçersiz token",
|
||||
"AUTH_ERROR": "Kimlik doğrulama başarısız oldu",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Надіслати повідомлення",
|
||||
"LABEL_CLOSE_COMPOSE": "Закрити повідомлення"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "Невірний токен запиту",
|
||||
"AUTH_ERROR": "Не вдалося авторизуватися",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "发送邮件",
|
||||
"LABEL_CLOSE_COMPOSE": "退出新邮件"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "无效标记",
|
||||
"AUTH_ERROR": "认证失败",
|
||||
|
|
|
@ -500,6 +500,9 @@
|
|||
"LABEL_SEND_MESSAGE": "Send message",
|
||||
"LABEL_CLOSE_COMPOSE": "Close compose"
|
||||
},
|
||||
"KOLAB": {
|
||||
"CONTACTS_FOLDER": "Contacts Folder"
|
||||
},
|
||||
"NOTIFICATIONS": {
|
||||
"INVALID_TOKEN": "無效標記",
|
||||
"AUTH_ERROR": "認證失敗",
|
||||
|
|
|
@ -40,3 +40,11 @@
|
|||
spellcheck="false" data-bind="value: syncPass">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-horizontal" data-bind="visible: showKolab">
|
||||
<div class="legend">Kolab</div>
|
||||
<div class="control-group">
|
||||
<label data-i18n="KOLAB/CONTACTS_FOLDER"></label>
|
||||
<select data-bind="options: folderSelectList, value: kolabContactFolder,
|
||||
optionsText: 'name', optionsValue: 'id'"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue