diff --git a/dev/App/User.js b/dev/App/User.js
index 829aee2b5..b4a2792f2 100644
--- a/dev/App/User.js
+++ b/dev/App/User.js
@@ -26,6 +26,7 @@
SettingsStore = require('Stores/User/Settings'),
AccountStore = require('Stores/User/Account'),
IdentityStore = require('Stores/User/Identity'),
+ TemplateStore = require('Stores/User/Template'),
FolderStore = require('Stores/User/Folder'),
PgpStore = require('Stores/User/Pgp'),
@@ -587,6 +588,8 @@
oIdentity.name(Utils.pString(oIdentityData['Name']));
oIdentity.replyTo(Utils.pString(oIdentityData['ReplyTo']));
oIdentity.bcc(Utils.pString(oIdentityData['Bcc']));
+ oIdentity.signature(Utils.pString(oIdentityData['Signature']));
+ oIdentity.signatureInsertBefore(!!oIdentityData['SignatureInsertBefore']);
return oIdentity;
}));
@@ -595,6 +598,29 @@
});
};
+ AppUser.prototype.templates = function ()
+ {
+ TemplateStore.templates.loading(true);
+
+ Remote.templates(function (sResult, oData) {
+
+ TemplateStore.templates.loading(false);
+
+ if (Enums.StorageResultType.Success === sResult && oData.Result &&
+ Utils.isArray(oData.Result['Templates']))
+ {
+ Utils.delegateRunOnDestroy(TemplateStore.templates());
+
+ IdentityStore.templates(_.map(oData.Result['Templates'], function (oIdentityData) {
+ return {
+ 'id': Utils.pString(oIdentityData['Id']),
+ 'name': Utils.pString(oIdentityData['Name'])
+ };
+ }));
+ }
+ });
+ };
+
AppUser.prototype.quota = function ()
{
Remote.quota(function (sResult, oData) {
diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js
index 32794cb2b..752d02797 100644
--- a/dev/Common/Globals.js
+++ b/dev/Common/Globals.js
@@ -135,7 +135,7 @@
'removeButtons': 'Format,Undo,Redo,Cut,Copy,Paste,Anchor,Strike,Subscript,Superscript,Image,SelectAll,Source',
'removeDialogTabs': 'link:advanced;link:target;image:advanced;images:advanced',
- 'extraPlugins': 'plain', // signature
+ 'extraPlugins': 'plain,signature',
'allowedContent': true,
'font_defaultLabel': 'Arial',
diff --git a/dev/Common/HtmlEditor.js b/dev/Common/HtmlEditor.js
index e0b4c8593..ead03e929 100644
--- a/dev/Common/HtmlEditor.js
+++ b/dev/Common/HtmlEditor.js
@@ -64,12 +64,16 @@
/**
* @param {string} sSignature
+ * @param {bool} bHtml
+ * @param {bool} bInsertBefore
*/
- HtmlEditor.prototype.setSignature = function (sSignature)
+ HtmlEditor.prototype.setSignature = function (sSignature, bHtml, bInsertBefore)
{
if (this.editor)
{
this.editor.execCommand('insertSignature', {
+ 'isHtml': bHtml,
+ 'insertBefore': bInsertBefore,
'signature': sSignature
});
}
@@ -92,24 +96,46 @@
};
/**
- * @param {boolean=} bWrapIsHtml = false
+ * @param {string} sText
* @return {string}
*/
- HtmlEditor.prototype.getData = function (bWrapIsHtml)
+ HtmlEditor.prototype.clearSignatureSigns = function (sText)
{
+ return sText
+ .replace("\u0002", '').replace("\u0002", '')
+ .replace("\u0002", '').replace("\u0002", '')
+ .replace("\u0003", '').replace("\u0003", '')
+ .replace("\u0003", '').replace("\u0003", '')
+ ;
+ }
+ /**
+ * @param {boolean=} bWrapIsHtml = false
+ * @param {boolean=} bClearSignatureSigns = false
+ * @return {string}
+ */
+ HtmlEditor.prototype.getData = function (bWrapIsHtml, bClearSignatureSigns)
+ {
+ var sResult = '';
if (this.editor)
{
if ('plain' === this.editor.mode && this.editor.plugins.plain && this.editor.__plain)
{
- return this.editor.__plain.getRawData();
+ sResult = this.editor.__plain.getRawData();
+ }
+ else
+ {
+ sResult = bWrapIsHtml ?
+ '
' +
+ this.editor.getData() + '
' : this.editor.getData();
}
- return bWrapIsHtml ?
- '' +
- this.editor.getData() + '
' : this.editor.getData();
+ if (bClearSignatureSigns)
+ {
+ sResult = this.clearSignatureSigns(sResult);
+ }
}
- return '';
+ return sResult;
};
HtmlEditor.prototype.modeToggle = function (bPlain)
diff --git a/dev/External/ko.js b/dev/External/ko.js
index ae142fbf2..84a9783f8 100644
--- a/dev/External/ko.js
+++ b/dev/External/ko.js
@@ -954,6 +954,25 @@
return this;
};
+ ko.observable.fn.deleteAccessHelper = function ()
+ {
+ this.extend({'falseTimeout': 3000}).extend({'toggleSubscribe': [null,
+ function (oPrev) {
+ if (oPrev && oPrev.deleteAccess)
+ {
+ oPrev.deleteAccess(false);
+ }
+ }, function (oNext) {
+ if (oNext && oNext.deleteAccess)
+ {
+ oNext.deleteAccess(true);
+ }
+ }
+ ]});
+
+ return this;
+ };
+
ko.observable.fn.validateFunc = function (fFunc)
{
var Utils = require('Common/Utils');
diff --git a/dev/Model/Contact.js b/dev/Model/Contact.js
index 1467c72b6..5c653361d 100644
--- a/dev/Model/Contact.js
+++ b/dev/Model/Contact.js
@@ -112,7 +112,7 @@
/**
* @return string
*/
- ContactModel.prototype.lineAsCcc = function ()
+ ContactModel.prototype.lineAsCss = function ()
{
var aResult = [];
if (this.deleted())
diff --git a/dev/Model/Identity.js b/dev/Model/Identity.js
index 7a6cef544..d28c201a3 100644
--- a/dev/Model/Identity.js
+++ b/dev/Model/Identity.js
@@ -7,8 +7,6 @@
_ = require('_'),
ko = require('ko'),
- Utils = require('Common/Utils'),
-
AbstractModel = require('Knoin/AbstractModel')
;
@@ -28,6 +26,9 @@
this.replyTo = ko.observable('');
this.bcc = ko.observable('');
+ this.signature = ko.observable('');
+ this.signatureInsertBefore = ko.observable(false);
+
this.deleteAccess = ko.observable(false);
this.canBeDeleted = ko.computed(function () {
return '' !== this.id();
diff --git a/dev/Model/Message.js b/dev/Model/Message.js
index c0fc83136..30a01c860 100644
--- a/dev/Model/Message.js
+++ b/dev/Model/Message.js
@@ -63,6 +63,7 @@
this.newForAnimation = ko.observable(false);
this.deleted = ko.observable(false);
+ this.deletedMark = ko.observable(false);
this.unseen = ko.observable(false);
this.flagged = ko.observable(false);
this.answered = ko.observable(false);
@@ -322,6 +323,7 @@
this.newForAnimation(false);
this.deleted(false);
+ this.deletedMark(false);
this.unseen(false);
this.flagged(false);
this.answered(false);
@@ -546,6 +548,7 @@
this.answered(!!oJsonMessage.IsAnswered);
this.forwarded(!!oJsonMessage.IsForwarded);
this.isReadReceipt(!!oJsonMessage.IsReadReceipt);
+ this.deletedMark(!!oJsonMessage.IsDeleted);
bResult = true;
}
@@ -621,13 +624,17 @@
/**
* @return string
*/
- MessageModel.prototype.lineAsCcc = function ()
+ MessageModel.prototype.lineAsCss = function ()
{
var aResult = [];
if (this.deleted())
{
aResult.push('deleted');
}
+ if (this.deletedMark())
+ {
+ aResult.push('deleted-mark');
+ }
if (this.selected())
{
aResult.push('selected');
@@ -972,6 +979,7 @@
this.answered(oMessage.answered());
this.forwarded(oMessage.forwarded());
this.isReadReceipt(oMessage.isReadReceipt());
+ this.deletedMark(oMessage.deletedMark());
this.priority(oMessage.priority());
@@ -1339,8 +1347,8 @@
*/
MessageModel.prototype.flagHash = function ()
{
- return [this.deleted(), this.unseen(), this.flagged(), this.answered(), this.forwarded(),
- this.isReadReceipt()].join('');
+ return [this.deleted(), this.deletedMark(), this.unseen(), this.flagged(), this.answered(), this.forwarded(),
+ this.isReadReceipt()].join(',');
};
module.exports = MessageModel;
diff --git a/dev/Model/Template.js b/dev/Model/Template.js
new file mode 100644
index 000000000..e8b53ae1d
--- /dev/null
+++ b/dev/Model/Template.js
@@ -0,0 +1,50 @@
+
+(function () {
+
+ 'use strict';
+
+ var
+ _ = require('_'),
+ ko = require('ko'),
+
+ AbstractModel = require('Knoin/AbstractModel')
+ ;
+
+ /**
+ * @constructor
+ *
+ * @param {string} sID
+ * @param {string} sName
+ * @param {string} sBody
+ */
+ function TemplateModel(sID, sName, sBody)
+ {
+ AbstractModel.call(this, 'TemplateModel');
+
+ this.id = sID;
+ this.name = sName;
+ this.body = sBody;
+
+ this.deleteAccess = ko.observable(false);
+ }
+
+ _.extend(TemplateModel.prototype, AbstractModel.prototype);
+
+ /**
+ * @type {string}
+ */
+ TemplateModel.prototype.id = '';
+
+ /**
+ * @type {string}
+ */
+ TemplateModel.prototype.name = '';
+
+ /**
+ * @type {string}
+ */
+ TemplateModel.prototype.body = '';
+
+ module.exports = TemplateModel;
+
+}());
\ No newline at end of file
diff --git a/dev/Screen/User/Settings.js b/dev/Screen/User/Settings.js
index e477fa84a..201d394c6 100644
--- a/dev/Screen/User/Settings.js
+++ b/dev/Screen/User/Settings.js
@@ -85,6 +85,9 @@
'SettingsChangePassword', 'SETTINGS_LABELS/LABEL_CHANGE_PASSWORD_NAME', 'change-password');
}
+// kn.addSettingsViewModel(require('Settings/User/Templates'),
+// 'SettingsTemplates', 'SETTINGS_LABELS/LABEL_TEMPLATES_NAME', 'templates');
+
kn.addSettingsViewModel(require('Settings/User/Folders'),
'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders');
diff --git a/dev/Settings/User/Accounts.js b/dev/Settings/User/Accounts.js
index d3a85c51a..43ffb65f6 100644
--- a/dev/Settings/User/Accounts.js
+++ b/dev/Settings/User/Accounts.js
@@ -9,6 +9,7 @@
ko = require('ko'),
Enums = require('Common/Enums'),
+ Utils = require('Common/Utils'),
Translator = require('Common/Translator'),
Links = require('Common/Links'),
@@ -34,33 +35,8 @@
return '' === this.processText() ? 'hidden' : 'visible';
}, this);
- this.accountForDeletion = ko.observable(null).extend({'falseTimeout': 3000}).extend({'toggleSubscribe': [this,
- function (oPrev) {
- if (oPrev)
- {
- oPrev.deleteAccess(false);
- }
- }, function (oNext) {
- if (oNext)
- {
- oNext.deleteAccess(true);
- }
- }
- ]});
-
- this.identityForDeletion = ko.observable(null).extend({'falseTimeout': 3000}).extend({'toggleSubscribe': [this,
- function (oPrev) {
- if (oPrev)
- {
- oPrev.deleteAccess(false);
- }
- }, function (oNext) {
- if (oNext)
- {
- oNext.deleteAccess(true);
- }
- }
- ]});
+ this.accountForDeletion = ko.observable(null).deleteAccessHelper();
+ this.identityForDeletion = ko.observable(null).deleteAccessHelper();
}
AccountsUserSettings.prototype.scrollableOptions = function ()
@@ -158,6 +134,12 @@
}
};
+ AccountsUserSettings.prototype.accountsAndIdentitiesAfterMove = function ()
+ {
+ Remote.accountsAndIdentitiesSortOrder(null,
+ AccountStore.accountsEmails.peek(), IdentityStore.identitiesIDS.peek());
+ };
+
AccountsUserSettings.prototype.onBuild = function (oDom)
{
var self = this;
diff --git a/dev/Settings/User/Templates.js b/dev/Settings/User/Templates.js
new file mode 100644
index 000000000..c0b1e8e9a
--- /dev/null
+++ b/dev/Settings/User/Templates.js
@@ -0,0 +1,105 @@
+
+(function () {
+
+ 'use strict';
+
+ var
+ ko = require('ko'),
+
+ Translator = require('Common/Translator'),
+
+ TemplateStore = require('Stores/User/Template'),
+
+ Remote = require('Storage/User/Remote')
+ ;
+
+ /**
+ * @constructor
+ */
+ function TemplatesUserSettings()
+ {
+ this.templates = TemplateStore.templates;
+
+ this.processText = ko.computed(function () {
+ return TemplateStore.templates.loading() ? Translator.i18n('SETTINGS_TEMPLETS/LOADING_PROCESS') : '';
+ }, this);
+
+ this.visibility = ko.computed(function () {
+ return '' === this.processText() ? 'hidden' : 'visible';
+ }, this);
+
+ this.templateForDeletion = ko.observable(null).deleteAccessHelper();
+ }
+
+ TemplatesUserSettings.prototype.scrollableOptions = function ()
+ {
+ return {
+ handle: '.drag-handle'
+ };
+ };
+
+ TemplatesUserSettings.prototype.addNewTemplate = function ()
+ {
+ require('Knoin/Knoin').showScreenPopup(require('View/Popup/Template'));
+ };
+
+ TemplatesUserSettings.prototype.editTemplate = function (oTemplateItem)
+ {
+ if (oTemplateItem)
+ {
+ require('Knoin/Knoin').showScreenPopup(require('View/Popup/Template'), [oTemplateItem]);
+ }
+ };
+
+ /**
+ * @param {AccountModel} oTemplateToRemove
+ */
+ TemplatesUserSettings.prototype.deleteTemplate = function (oTemplateToRemove)
+ {
+ if (oTemplateToRemove && oTemplateToRemove.deleteAccess())
+ {
+ this.templateForDeletion(null);
+
+ var
+ self = this,
+ fRemoveAccount = function (oAccount) {
+ return oTemplateToRemove === oAccount;
+ }
+ ;
+
+ if (oTemplateToRemove)
+ {
+ this.templates.remove(fRemoveAccount);
+
+ Remote.templateDelete(function () {
+ self.reloadTemplates();
+ }, oTemplateToRemove.id);
+ }
+ }
+ };
+
+ TemplatesUserSettings.prototype.reloadTemplates = function ()
+ {
+ require('App/User').templates();
+ };
+
+ TemplatesUserSettings.prototype.onBuild = function (oDom)
+ {
+ var self = this;
+
+ oDom
+ .on('click', '.templates-list .template-item .e-action', function () {
+ var oTemplateItem = ko.dataFor(this);
+ if (oTemplateItem)
+ {
+ self.editTemplate(oTemplateItem);
+ }
+ })
+ ;
+
+ this.reloadTemplates();
+ };
+
+ module.exports = TemplatesUserSettings;
+
+}());
\ No newline at end of file
diff --git a/dev/Storage/User/Cache.js b/dev/Storage/User/Cache.js
index 6d483fb98..e83773b65 100644
--- a/dev/Storage/User/Cache.js
+++ b/dev/Storage/User/Cache.js
@@ -313,6 +313,7 @@
oMessage.answered(!!aFlags[2]);
oMessage.forwarded(!!aFlags[3]);
oMessage.isReadReceipt(!!aFlags[4]);
+ oMessage.deletedMark(!!aFlags[5]);
}
if (0 < oMessage.threads().length)
@@ -343,7 +344,8 @@
this.setMessageFlagsToCache(
oMessage.folderFullNameRaw,
oMessage.uid,
- [oMessage.unseen(), oMessage.flagged(), oMessage.answered(), oMessage.forwarded(), oMessage.isReadReceipt()]
+ [oMessage.unseen(), oMessage.flagged(), oMessage.answered(), oMessage.forwarded(),
+ oMessage.isReadReceipt(), oMessage.deletedMark()]
);
}
};
diff --git a/dev/Storage/User/Remote.js b/dev/Storage/User/Remote.js
index 7a467e8e6..a9b6178e5 100644
--- a/dev/Storage/User/Remote.js
+++ b/dev/Storage/User/Remote.js
@@ -188,11 +188,13 @@
/**
* @param {?Function} fCallback
* @param {Array} aAccounts
+ * @param {Array} aIdentities
*/
- RemoteUserStorage.prototype.accountSortOrder = function (fCallback, aAccounts)
+ RemoteUserStorage.prototype.accountsAndIdentitiesSortOrder = function (fCallback, aAccounts, aIdentities)
{
- this.defaultRequest(fCallback, 'AccountSortOrder', {
- 'Accounts': aAccounts
+ this.defaultRequest(fCallback, 'AccountsAndIdentitiesSortOrder', {
+ 'Accounts': aAccounts,
+ 'Identities': aIdentities
});
};
@@ -203,15 +205,20 @@
* @param {string} sName
* @param {string} sReplyTo
* @param {string} sBcc
+ * @param {string} sSignature
+ * @param {boolean} bSignatureInsertBefore
*/
- RemoteUserStorage.prototype.identityUpdate = function (fCallback, sId, sEmail, sName, sReplyTo, sBcc)
+ RemoteUserStorage.prototype.identityUpdate = function (fCallback, sId, sEmail, sName, sReplyTo, sBcc,
+ sSignature, bSignatureInsertBefore)
{
this.defaultRequest(fCallback, 'IdentityUpdate', {
'Id': sId,
'Email': sEmail,
'Name': sName,
'ReplyTo': sReplyTo,
- 'Bcc': sBcc
+ 'Bcc': sBcc,
+ 'Signature': sSignature,
+ 'SignatureInsertBefore': bSignatureInsertBefore ? '1' : '0'
});
};
@@ -265,6 +272,36 @@
this.defaultRequest(fCallback, 'Filters', {});
};
+ /**
+ * @param {?Function} fCallback
+ */
+ RemoteUserStorage.prototype.templates = function (fCallback)
+ {
+ this.defaultRequest(fCallback, 'Templates', {});
+ };
+
+ /**
+ * @param {?Function} fCallback
+ */
+ RemoteUserStorage.prototype.templateGetById = function (fCallback, sID)
+ {
+ this.defaultRequest(fCallback, 'TemplateGetByID', {
+ 'ID': sID
+ });
+ };
+
+ /**
+ * @param {?Function} fCallback
+ */
+ RemoteUserStorage.prototype.templateGetById = function (fCallback, sID, sName, sBody)
+ {
+ this.defaultRequest(fCallback, 'TemplateSetup', {
+ 'ID': sID,
+ 'Name': sName,
+ 'Body': sBody
+ });
+ };
+
/**
* @param {?Function} fCallback
* @param {string} sFolderFullNameRaw
diff --git a/dev/Stores/User/Account.js b/dev/Stores/User/Account.js
index f0d0ec0de..d562ccde6 100644
--- a/dev/Stores/User/Account.js
+++ b/dev/Stores/User/Account.js
@@ -7,9 +7,7 @@
_ = require('_'),
ko = require('ko'),
- Settings = require('Storage/Settings'),
-
- Remote = require('Storage/User/Remote')
+ Settings = require('Storage/Settings')
;
/**
@@ -26,24 +24,10 @@
this.accounts = ko.observableArray([]);
this.accounts.loading = ko.observable(false).extend({'throttle': 100});
- this.accountsEmailNames = ko.observableArray([]).extend({'throttle': 1000});
- this.accountsEmailNames.skipFirst = true;
-
- this.accounts.subscribe(function (aList) {
- this.accountsEmailNames(_.compact(_.map(aList, function (oItem) {
+ this.accountsEmails = ko.computed(function () {
+ return _.compact(_.map(this.accounts(), function (oItem) {
return oItem ? oItem.email : null;
- })));
- }, this);
-
- this.accountsEmailNames.subscribe(function (aList) {
- if (this.accountsEmailNames.skipFirst)
- {
- this.accountsEmailNames.skipFirst = false;
- }
- else if (aList && 1 < aList.length)
- {
- Remote.accountSortOrder(null, aList);
- }
+ }));
}, this);
this.accountsUnreadCount = ko.computed(function () {
@@ -65,10 +49,6 @@
AccountUserStore.prototype.populate = function ()
{
this.email(Settings.settingsGet('Email'));
-// this.incLogin(Settings.settingsGet('IncLogin'));
-// this.outLogin(Settings.settingsGet('OutLogin'));
-
-// this.signature(Settings.settingsGet('Signature'));
};
module.exports = new AccountUserStore();
diff --git a/dev/Stores/User/Identity.js b/dev/Stores/User/Identity.js
index 4fb95ba71..1f0a2acc8 100644
--- a/dev/Stores/User/Identity.js
+++ b/dev/Stores/User/Identity.js
@@ -4,6 +4,7 @@
'use strict';
var
+ _ = require('_'),
ko = require('ko')
;
@@ -14,6 +15,12 @@
{
this.identities = ko.observableArray([]);
this.identities.loading = ko.observable(false).extend({'throttle': 100});
+
+ this.identitiesIDS = ko.computed(function () {
+ return _.compact(_.map(this.identities(), function (oItem) {
+ return oItem ? oItem.id : null;
+ }));
+ }, this);
}
module.exports = new IdentityUserStore();
diff --git a/dev/Stores/User/Template.js b/dev/Stores/User/Template.js
new file mode 100644
index 000000000..8a3f41030
--- /dev/null
+++ b/dev/Stores/User/Template.js
@@ -0,0 +1,44 @@
+
+(function () {
+
+ 'use strict';
+
+ var
+ _ = require('_'),
+ ko = require('ko'),
+
+ Remote = require('Storage/User/Remote')
+ ;
+
+ /**
+ * @constructor
+ */
+ function TemplateUserStore()
+ {
+ this.templates = ko.observableArray([]);
+ this.templates.loading = ko.observable(false).extend({'throttle': 100});
+
+ this.templatesNames = ko.observableArray([]).extend({'throttle': 1000});
+ this.templatesNames.skipFirst = true;
+
+ this.templates.subscribe(function (aList) {
+ this.templatesNames(_.compact(_.map(aList, function (oItem) {
+ return oItem ? oItem.name : null;
+ })));
+ }, this);
+
+ this.templatesNames.subscribe(function (aList) {
+ if (this.templatesNames.skipFirst)
+ {
+ this.templatesNames.skipFirst = false;
+ }
+ else if (aList && 1 < aList.length)
+ {
+ Remote.templatesSortOrder(null, aList);
+ }
+ }, this);
+ }
+
+ module.exports = new TemplateUserStore();
+
+}());
diff --git a/dev/Styles/@Main.less b/dev/Styles/@Main.less
index 9ad3e3c95..e6fe214f2 100644
--- a/dev/Styles/@Main.less
+++ b/dev/Styles/@Main.less
@@ -63,6 +63,7 @@
@import "Filter.less";
@import "Languages.less";
@import "Account.less";
+@import "Template.less";
@import "OpenPgpKey.less";
@import "TwoFactor.less";
@import "Identity.less";
@@ -85,6 +86,7 @@
@import "Settings.less";
@import "SettingsGeneral.less";
@import "SettingsAccounts.less";
+@import "SettingsTemplates.less";
@import "SettingsOpenPGP.less";
@import "SettingsFolders.less";
@import "SettingsThemes.less";
diff --git a/dev/Styles/Compose.less b/dev/Styles/Compose.less
index 6ec64376c..94106d0ae 100644
--- a/dev/Styles/Compose.less
+++ b/dev/Styles/Compose.less
@@ -52,9 +52,10 @@
background-color: #333;
background-color: rgba(0,0,0,0.8) !important;
- .close {
- color: #fff;
+ .close, .close-custom, .minimize-custom {
opacity: 1;
+ color: #fff;
+ border-color: #eee;
}
.btn.disabled {
@@ -157,7 +158,7 @@
.error {
color: red;
}
- .close {
+ .close, .close-custom {
float: left;
padding-right: 13px;
}
diff --git a/dev/Styles/Contacts.less b/dev/Styles/Contacts.less
index c5d1176b1..02b27013f 100644
--- a/dev/Styles/Contacts.less
+++ b/dev/Styles/Contacts.less
@@ -41,7 +41,7 @@
background-color: #333;
background-color: rgba(0,0,0,0.8) !important;
- .close {
+ .close, .close-custom {
color: #fff;
opacity: 1;
}
diff --git a/dev/Styles/Identity.less b/dev/Styles/Identity.less
index 5de93b5d9..422f59fac 100644
--- a/dev/Styles/Identity.less
+++ b/dev/Styles/Identity.less
@@ -1,9 +1,30 @@
.popups {
.b-identity-content {
+
+ &.modal {
+ width: 750px;
+ }
+
.modal-header {
background-color: #fff;
}
+ .modal-body {
+ overflow: hidden;
+ }
+
+ .control-label {
+ width: 100px;
+ }
+
+ .controls {
+ margin-left: 120px;
+ }
+
+ .e-signature-place {
+ height: 300px;
+ }
+
.textEmail {
margin-top: 5px;
font-weight: bold;
diff --git a/dev/Styles/MessageList.less b/dev/Styles/MessageList.less
index 7131f3f4c..8ea5707a2 100644
--- a/dev/Styles/MessageList.less
+++ b/dev/Styles/MessageList.less
@@ -249,6 +249,13 @@ html.rl-no-preview-pane {
margin-right:5px
}
+ &.deleted-mark {
+ opacity: .7;
+ .sender, .subject, .subject-prefix, .subject-suffix {
+ text-decoration: line-through;
+ }
+ }
+
&.important .importantMark {
display: inline;
}
diff --git a/dev/Styles/SettingsTemplates.less b/dev/Styles/SettingsTemplates.less
new file mode 100644
index 000000000..85718eba1
--- /dev/null
+++ b/dev/Styles/SettingsTemplates.less
@@ -0,0 +1,70 @@
+
+.b-settings-identities {
+
+ .process-place {
+ text-align: center;
+ width: 600px;
+ padding: 14px 0;
+ }
+
+ .list-table {
+
+ width: 600px;
+
+ td {
+ padding: 4px 8px;
+ line-height: 30px;
+ }
+
+ .drag-handle {
+ color: #eee;
+ }
+
+ tr:hover .drag-handle {
+ color: #aaa;
+ }
+
+ .template-img {
+ font-size: 12px;
+ margin-right: 5px;
+ }
+
+ .template-name {
+ display: inline-block;
+ word-break: break-all;
+ box-sizing: border-box;
+ line-height: 22px;
+ }
+ }
+
+ .template-item {
+
+ .e-action {
+ cursor: pointer;
+ }
+
+ .drag-handle {
+ cursor: pointer;
+ }
+
+ .button-delete {
+ margin-right: 15px;
+ margin-top: 5px;
+ visibility: hidden;
+ opacity: 0;
+ }
+
+ .delete-access {
+ &.button-delete {
+ visibility: visible;
+ margin-right: 0;
+ opacity: 1;
+ }
+ }
+
+ .delete-template {
+ cursor: pointer;
+ opacity: 0.5;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dev/Styles/Template.less b/dev/Styles/Template.less
new file mode 100644
index 000000000..6ab167d76
--- /dev/null
+++ b/dev/Styles/Template.less
@@ -0,0 +1,12 @@
+.popups {
+ .b-template-add-content {
+
+ &.modal {
+ width: 700px;
+ }
+
+ .modal-header {
+ background-color: #fff;
+ }
+ }
+}
diff --git a/dev/Styles/_BootstrapFix.less b/dev/Styles/_BootstrapFix.less
index 492118778..2c005356a 100644
--- a/dev/Styles/_BootstrapFix.less
+++ b/dev/Styles/_BootstrapFix.less
@@ -11,6 +11,32 @@ label.inline {
display: inline-block;
}
+.close-custom {
+ .close;
+}
+
+button.close-custom {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+
+.minimize-custom {
+ border: 0px solid #333;
+ border-bottom-width: 3px;
+ display: inline-block;
+ float: right;
+ height: 20px;
+ width: 16px;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 20px;
+ margin-right: 15px;
+ cursor: pointer;
+}
+
.legend {
display: block;
width: 100%;
diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js
index dea6dd3ad..2cfce8863 100644
--- a/dev/View/Popup/Compose.js
+++ b/dev/View/Popup/Compose.js
@@ -33,7 +33,6 @@
Data = require('Storage/User/Data'),
Cache = require('Storage/User/Cache'),
Remote = require('Storage/User/Remote'),
- Local = require('Storage/Client'),
ComposeAttachmentModel = require('Model/ComposeAttachment'),
@@ -74,6 +73,7 @@
}
;
+ this.oLastMessage = null;
this.oEditor = null;
this.aDraftInfo = null;
this.sInReplyTo = '';
@@ -198,49 +198,19 @@
this.composeEditorArea = ko.observable(null);
- this.identities = AccountStore.identities;
+ this.identities = IdentityStore.identities;
this.identitiesOptions = ko.computed(function () {
return _.map(IdentityStore.identities(), function (oItem) {
return {
+ 'item': oItem,
'optValue': oItem.id(),
'optText': oItem.formattedName()
};
});
}, this);
- this.currentIdentityID = ko.observable(
- Local.get(Enums.ClientSideKeyName.ComposeLastIdentityID, ''));
-
- this.currentIdentity = ko.computed(function () {
-
- var
- oItem = null,
- sID = this.currentIdentityID(),
- aList = IdentityStore.identities()
- ;
-
- if (Utils.isArray(aList))
- {
- oItem = _.find(aList, function (oItem) {
- return oItem && sID === oItem.id();
- });
-
- if (!oItem)
- {
- oItem = _.find(aList, function (oItem) {
- return oItem && '' === oItem.id();
- });
- }
-
- if (!oItem)
- {
- oItem = aList[0] ? aList[0] : null;
- }
- }
-
- return oItem ? oItem : null;
-
- }, this);
+ this.currentIdentity = ko.observable(
+ this.identities()[0] ? this.identities()[0] : null);
this.currentIdentity.extend({'toggleSubscribe': [this,
function (oIdentity) {
@@ -361,7 +331,7 @@
Remote.sendMessage(
this.sendMessageResponse,
- this.currentIdentityID(),
+ this.currentIdentity() ? this.currentIdentity().id() : '',
this.draftFolder(),
this.draftUid(),
sSentFolder,
@@ -371,7 +341,7 @@
this.replyTo(),
this.subject(),
this.oEditor ? this.oEditor.isHtml() : false,
- this.oEditor ? this.oEditor.getData(true) : '',
+ this.oEditor ? this.oEditor.getData(true, true) : '',
this.prepearAttachmentsForSendOrSave(),
this.aDraftInfo,
this.sInReplyTo,
@@ -400,7 +370,7 @@
Remote.saveMessage(
this.saveMessageResponse,
- this.currentIdentityID(),
+ this.currentIdentity() ? this.currentIdentity().id() : '',
this.draftFolder(),
this.draftUid(),
FolderStore.draftFolder(),
@@ -597,7 +567,6 @@
ComposePopupView.prototype.findIdentityByMessage = function (sComposeType, oMessage)
{
var
- sDefaultIdentityID = Local.get(Enums.ClientSideKeyName.ComposeLastIdentityID, ''),
aIdentities = IdentityStore.identities(),
oResultIdentity = null,
oIdentitiesCache = {},
@@ -640,34 +609,15 @@
}
}
- if (!oResultIdentity)
- {
- oResultIdentity = _.find(aIdentities, function (oItem) {
- return oItem.id() === sDefaultIdentityID;
- });
- }
-
- if (!oResultIdentity && '' !== sDefaultIdentityID)
- {
- oResultIdentity = _.find(aIdentities, function (oItem) {
- return oItem.id() === '';
- });
- }
-
- if (!oResultIdentity)
- {
- oResultIdentity = aIdentities[0] || null;
- }
-
- return oResultIdentity;
+ return oResultIdentity || aIdentities[0] || null;
};
ComposePopupView.prototype.selectIdentity = function (oIdentity)
{
- if (oIdentity)
+ if (oIdentity && oIdentity.item)
{
- this.currentIdentityID(oIdentity.optValue);
- Local.set(Enums.ClientSideKeyName.ComposeLastIdentityID, oIdentity.optValue);
+ this.currentIdentity(oIdentity.item);
+ this.setSignatureFromIdentity(oIdentity.item);
}
};
@@ -775,97 +725,6 @@
kn.routeOn();
};
- /**
- * @param {string} sInputText
- * @param {string} sSignature
- * @param {string=} sFrom
- * @param {string=} sComposeType
- * @return {string}
- */
- ComposePopupView.prototype.convertSignature = function (sInputText, sSignature, sFrom, sComposeType)
- {
- var bHtml = false, bData = false;
- if ('' !== sSignature)
- {
- if (':HTML:' === sSignature.substr(0, 6))
- {
- bHtml = true;
- sSignature = sSignature.substr(6);
- }
-
- sSignature = sSignature.replace(/[\r]/g, '');
-
- sFrom = Utils.pString(sFrom);
- if ('' !== sFrom)
- {
- sSignature = sSignature.replace(/{{FROM-FULL}}/g, sFrom);
-
- if (-1 === sFrom.indexOf(' ') && 0 < sFrom.indexOf('@'))
- {
- sFrom = sFrom.replace(/@(.+)$/, '');
- }
-
- sSignature = sSignature.replace(/{{FROM}}/g, sFrom);
- }
-
- sSignature = sSignature.replace(/[\s]{1,2}{{FROM}}/g, '{{FROM}}');
- sSignature = sSignature.replace(/[\s]{1,2}{{FROM-FULL}}/g, '{{FROM-FULL}}');
-
- sSignature = sSignature.replace(/{{FROM}}/g, '');
- sSignature = sSignature.replace(/{{FROM-FULL}}/g, '');
-
- if (-1 < sSignature.indexOf('{{DATE}}'))
- {
- sSignature = sSignature.replace(/{{DATE}}/g, moment().format('llll'));
- }
-
- // Enums.ComposeType.Empty === sComposeType
- if (-1 < sSignature.indexOf('{{BODY}}'))
- {
- if (sInputText)
- {
- bData = true;
-
- sSignature = bHtml ? sSignature : Utils.plainToHtml(sSignature, true);
- sSignature = sSignature.replace('{{BODY}}', sInputText);
- }
- else
- {
- sSignature = sSignature.replace(/[\n]?{{BODY}}[\n]?/g, '{{BODY}}');
- sSignature = sSignature.replace(/{{BODY}}/g, '');
-
- sSignature = bHtml ? sSignature : Utils.plainToHtml(sSignature, true);
- }
- }
- else
- {
- sSignature = bHtml ? sSignature : Utils.plainToHtml(sSignature, true);
- }
- }
-
- if (!bData)
- {
- if (sSignature)
- {
- switch (sComposeType)
- {
- case Enums.ComposeType.Empty:
- sInputText = sInputText + '
' + sSignature;
- break;
- default:
- sInputText = sSignature + '
' + sInputText;
- break;
- }
- }
- }
- else
- {
- sInputText = sSignature;
- }
-
- return sInputText;
- };
-
ComposePopupView.prototype.editor = function (fOnInit)
{
if (fOnInit)
@@ -873,13 +732,11 @@
var self = this;
if (!this.oEditor && this.composeEditorArea())
{
- _.delay(function () {
- self.oEditor = new HtmlEditor(self.composeEditorArea(), null, function () {
- fOnInit(self.oEditor);
- }, function (bHtml) {
- self.isHtml(!!bHtml);
- });
- }, 300);
+ self.oEditor = new HtmlEditor(self.composeEditorArea(), null, function () {
+ fOnInit(self.oEditor);
+ }, function (bHtml) {
+ self.isHtml(!!bHtml);
+ });
}
else if (this.oEditor)
{
@@ -888,6 +745,101 @@
}
};
+ ComposePopupView.prototype.converSignature = function (sSignature)
+ {
+ var
+ iLimit = 10,
+ oMatch = null,
+ aMoments = [],
+ oMomentRegx = /{{MOMENT:([^}]+)}}/g,
+ sFrom = ''
+ ;
+
+ sSignature = sSignature.replace(/[\r]/g, '');
+
+ sFrom = this.oLastMessage ? this.emailArrayToStringLineHelper(this.oLastMessage.from, true) : '';
+ if ('' !== sFrom)
+ {
+ sSignature = sSignature.replace(/{{FROM-FULL}}/g, sFrom);
+
+ if (-1 === sFrom.indexOf(' ') && 0 < sFrom.indexOf('@'))
+ {
+ sFrom = sFrom.replace(/@[\S]+/, '');
+ }
+
+ sSignature = sSignature.replace(/{{FROM}}/g, sFrom);
+ }
+
+ sSignature = sSignature.replace(/[\s]{1,2}{{FROM}}/g, '{{FROM}}');
+ sSignature = sSignature.replace(/[\s]{1,2}{{FROM-FULL}}/g, '{{FROM-FULL}}');
+
+ sSignature = sSignature.replace(/{{FROM}}/g, '');
+ sSignature = sSignature.replace(/{{FROM-FULL}}/g, '');
+
+ if (-1 < sSignature.indexOf('{{DATE}}'))
+ {
+ sSignature = sSignature.replace(/{{DATE}}/g, moment().format('llll'));
+ }
+
+ if (-1 < sSignature.indexOf('{{TIME}}'))
+ {
+ sSignature = sSignature.replace(/{{TIME}}/g, moment().format('LT'));
+ }
+ if (-1 < sSignature.indexOf('{{MOMENT:'))
+ {
+ try
+ {
+ while ((oMatch = oMomentRegx.exec(sSignature)) !== null)
+ {
+ if (oMatch && oMatch[0] && oMatch[1])
+ {
+ aMoments.push([oMatch[0], oMatch[1]]);
+ }
+
+ iLimit--;
+ if (0 === iLimit)
+ {
+ break;
+ }
+ }
+
+ if (aMoments && 0 < aMoments.length)
+ {
+ _.each(aMoments, function (aData) {
+ sSignature = sSignature.replace(aData[0], moment().format(aData[1]));
+ });
+ }
+
+ sSignature = sSignature.replace(/{{MOMENT:[^}]+}}/g, '');
+ }
+ catch(e) {}
+ }
+
+ return sSignature;
+ };
+
+ ComposePopupView.prototype.setSignatureFromIdentity = function (oIdentity)
+ {
+ if (oIdentity)
+ {
+ var self = this;
+ this.editor(function (oEditor) {
+ var bHtml = false, sSignature = oIdentity.signature();
+ if ('' !== sSignature)
+ {
+ if (':HTML:' === sSignature.substr(0, 6))
+ {
+ bHtml = true;
+ sSignature = sSignature.substr(6);
+ }
+ }
+
+ oEditor.setSignature(self.converSignature(sSignature),
+ bHtml, !!oIdentity.signatureInsertBefore());
+ });
+ }
+ };
+
/**
* @param {string=} sType = Enums.ComposeType.Empty
* @param {?MessageModel|Array=} oMessageOrArray = null
@@ -956,6 +908,28 @@
}
};
+ /**
+ *
+ * @param {Array} aList
+ * @param {boolean} bFriendly
+ * @returns {string}
+ */
+ ComposePopupView.prototype.emailArrayToStringLineHelper = function (aList, bFriendly)
+ {
+ var
+ iIndex = 0,
+ iLen = aList.length,
+ aResult = []
+ ;
+
+ for (; iIndex < iLen; iIndex++)
+ {
+ aResult.push(aList[iIndex].toLine(!!bFriendly));
+ }
+
+ return aResult.join(', ');
+ };
+
/**
* @param {string=} sType = Enums.ComposeType.Empty
* @param {?MessageModel|Array=} oMessageOrArray = null
@@ -984,26 +958,10 @@
oExcludeEmail = {},
oIdentity = null,
mEmail = AccountStore.email(),
- sSignature = AccountStore.signature(),
aDownloads = [],
aDraftInfo = null,
oMessage = null,
- sComposeType = sType || Enums.ComposeType.Empty,
- fEmailArrayToStringLineHelper = function (aList, bFriendly) {
-
- var
- iIndex = 0,
- iLen = aList.length,
- aResult = []
- ;
-
- for (; iIndex < iLen; iIndex++)
- {
- aResult.push(aList[iIndex].toLine(!!bFriendly));
- }
-
- return aResult.join(', ');
- }
+ sComposeType = sType || Enums.ComposeType.Empty
;
oMessageOrArray = oMessageOrArray || null;
@@ -1013,39 +971,34 @@
(!Utils.isArray(oMessageOrArray) ? oMessageOrArray : null);
}
+ this.oLastMessage = oMessage;
+
if (null !== mEmail)
{
oExcludeEmail[mEmail] = true;
}
+ this.reset();
+
oIdentity = this.findIdentityByMessage(sComposeType, oMessage);
if (oIdentity)
{
oExcludeEmail[oIdentity.email()] = true;
- this.currentIdentityID(oIdentity.id());
}
- else
- {
- this.currentIdentityID('');
- }
-
- this.reset();
-
- this.currentIdentityID.valueHasMutated(); // Populate BBC from Identity
if (Utils.isNonEmptyArray(aToEmails))
{
- this.to(fEmailArrayToStringLineHelper(aToEmails));
+ this.to(this.emailArrayToStringLineHelper(aToEmails));
}
if (Utils.isNonEmptyArray(aCcEmails))
{
- this.cc(fEmailArrayToStringLineHelper(aCcEmails));
+ this.cc(this.emailArrayToStringLineHelper(aCcEmails));
}
if (Utils.isNonEmptyArray(aBccEmails))
{
- this.bcc(fEmailArrayToStringLineHelper(aBccEmails));
+ this.bcc(this.emailArrayToStringLineHelper(aBccEmails));
}
if ('' !== sComposeType && oMessage)
@@ -1074,7 +1027,7 @@
break;
case Enums.ComposeType.Reply:
- this.to(fEmailArrayToStringLineHelper(oMessage.replyEmails(oExcludeEmail)));
+ this.to(this.emailArrayToStringLineHelper(oMessage.replyEmails(oExcludeEmail)));
this.subject(Utils.replySubjectAdd('Re', sSubject));
this.prepearMessageAttachments(oMessage, sComposeType);
this.aDraftInfo = ['reply', oMessage.uid, oMessage.folderFullNameRaw];
@@ -1084,8 +1037,8 @@
case Enums.ComposeType.ReplyAll:
aResplyAllParts = oMessage.replyAllEmails(oExcludeEmail);
- this.to(fEmailArrayToStringLineHelper(aResplyAllParts[0]));
- this.cc(fEmailArrayToStringLineHelper(aResplyAllParts[1]));
+ this.to(this.emailArrayToStringLineHelper(aResplyAllParts[0]));
+ this.cc(this.emailArrayToStringLineHelper(aResplyAllParts[1]));
this.subject(Utils.replySubjectAdd('Re', sSubject));
this.prepearMessageAttachments(oMessage, sComposeType);
this.aDraftInfo = ['reply', oMessage.uid, oMessage.folderFullNameRaw];
@@ -1110,10 +1063,10 @@
break;
case Enums.ComposeType.Draft:
- this.to(fEmailArrayToStringLineHelper(oMessage.to));
- this.cc(fEmailArrayToStringLineHelper(oMessage.cc));
- this.bcc(fEmailArrayToStringLineHelper(oMessage.bcc));
- this.replyTo(fEmailArrayToStringLineHelper(oMessage.replyTo));
+ this.to(this.emailArrayToStringLineHelper(oMessage.to));
+ this.cc(this.emailArrayToStringLineHelper(oMessage.cc));
+ this.bcc(this.emailArrayToStringLineHelper(oMessage.bcc));
+ this.replyTo(this.emailArrayToStringLineHelper(oMessage.replyTo));
this.bFromDraft = true;
@@ -1129,10 +1082,10 @@
break;
case Enums.ComposeType.EditAsNew:
- this.to(fEmailArrayToStringLineHelper(oMessage.to));
- this.cc(fEmailArrayToStringLineHelper(oMessage.cc));
- this.bcc(fEmailArrayToStringLineHelper(oMessage.bcc));
- this.replyTo(fEmailArrayToStringLineHelper(oMessage.replyTo));
+ this.to(this.emailArrayToStringLineHelper(oMessage.to));
+ this.cc(this.emailArrayToStringLineHelper(oMessage.cc));
+ this.bcc(this.emailArrayToStringLineHelper(oMessage.bcc));
+ this.replyTo(this.emailArrayToStringLineHelper(oMessage.replyTo));
this.subject(sSubject);
this.prepearMessageAttachments(oMessage, sComposeType);
@@ -1153,7 +1106,7 @@
'EMAIL': sFrom
});
- sText = '
' + sReplyTitle + ':' +
+ sText = '
' + sReplyTitle + ':' +
'' + sText + '
';
break;
@@ -1162,7 +1115,7 @@
sFrom = oMessage.fromToLine(false, true);
sTo = oMessage.toToLine(false, true);
sCc = oMessage.ccToLine(false, true);
- sText = '
' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_TITLE') +
+ sText = '
' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_TITLE') +
'
' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_FROM') + ': ' + sFrom +
'
' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_TO') + ': ' + sTo +
(0 < sCc.length ? '
' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_CC') + ': ' + sCc : '') +
@@ -1175,19 +1128,20 @@
break;
}
- if ('' !== sSignature &&
- Enums.ComposeType.EditAsNew !== sComposeType && Enums.ComposeType.Draft !== sComposeType)
- {
- sText = this.convertSignature(sText, sSignature, fEmailArrayToStringLineHelper(oMessage.from, true), sComposeType);
- }
-
this.editor(function (oEditor) {
+
oEditor.setHtml(sText, false);
+
if (Enums.EditorDefaultType.PlainForced === self.editorDefaultType() ||
(!oMessage.isHtml() && Enums.EditorDefaultType.HtmlForced !== self.editorDefaultType()))
{
oEditor.modeToggle(false);
}
+
+ if (oIdentity)
+ {
+ self.setSignatureFromIdentity(oIdentity);
+ }
});
}
else if (Enums.ComposeType.Empty === sComposeType)
@@ -1195,18 +1149,21 @@
this.subject(Utils.isNormal(sCustomSubject) ? '' + sCustomSubject : '');
sText = Utils.isNormal(sCustomPlainText) ? '' + sCustomPlainText : '';
- if ('' !== sSignature)
- {
- sText = this.convertSignature(Utils.plainToHtml(sText, true), sSignature, '', sComposeType);
- }
this.editor(function (oEditor) {
+
oEditor.setHtml(sText, false);
+
if (Enums.EditorDefaultType.Html !== self.editorDefaultType() &&
Enums.EditorDefaultType.HtmlForced !== self.editorDefaultType())
{
oEditor.modeToggle(false);
}
+
+ if (oIdentity)
+ {
+ self.setSignatureFromIdentity(oIdentity);
+ }
});
}
else if (Utils.isNonEmptyArray(oMessageOrArray))
@@ -1214,6 +1171,22 @@
_.each(oMessageOrArray, function (oMessage) {
self.addMessageAsAttachment(oMessage);
});
+
+ this.editor(function (oEditor) {
+
+ oEditor.setHtml('', false);
+
+ if (Enums.EditorDefaultType.Html !== self.editorDefaultType() &&
+ Enums.EditorDefaultType.HtmlForced !== self.editorDefaultType())
+ {
+ oEditor.modeToggle(false);
+ }
+
+ if (oIdentity)
+ {
+ self.setSignatureFromIdentity(oIdentity);
+ }
+ });
}
aDownloads = this.getAttachmentsDownloadsForUpload();
@@ -1252,6 +1225,11 @@
}, aDownloads);
}
+ if (oIdentity)
+ {
+ this.currentIdentity(oIdentity);
+ }
+
this.triggerForResize();
};
diff --git a/dev/View/Popup/Identity.js b/dev/View/Popup/Identity.js
index a7c543929..a513dcb34 100644
--- a/dev/View/Popup/Identity.js
+++ b/dev/View/Popup/Identity.js
@@ -26,6 +26,8 @@
{
AbstractView.call(this, 'Popups', 'PopupsIdentity');
+ var self = this;
+
this.id = '';
this.edit = ko.observable(false);
this.owner = ko.observable(false);
@@ -43,16 +45,32 @@
this.bcc.focused = ko.observable(false);
this.signature = ko.observable('');
+ this.signatureInsertBefore = ko.observable(false);
- // this.email.subscribe(function () {
- // this.email.hasError(false);
- // }, this);
+ this.showBcc = ko.observable(false);
+ this.showReplyTo = ko.observable(false);
this.submitRequest = ko.observable(false);
this.submitError = ko.observable('');
+ this.bcc.subscribe(function (aValue) {
+ if (false === self.showBcc() && 0 < aValue.length)
+ {
+ self.showBcc(true);
+ }
+ }, this);
+
+ this.replyTo.subscribe(function (aValue) {
+ if (false === self.showReplyTo() && 0 < aValue.length)
+ {
+ self.showReplyTo(true);
+ }
+ }, this);
+
this.addOrEditIdentityCommand = Utils.createCommand(this, function () {
+ this.populateSignatureFromEditor();
+
if (!this.email.hasError())
{
this.email.hasError('' === Utils.trim(this.email()));
@@ -102,7 +120,8 @@
this.submitError(Translator.getNotification(Enums.Notification.UnknownError));
}
- }, this), this.id, this.email(), this.name(), this.replyTo(), this.bcc());
+ }, this), this.id, this.email(), this.name(), this.replyTo(), this.bcc(),
+ this.signature(), this.signatureInsertBefore());
return true;
@@ -126,17 +145,22 @@
this.email('');
this.replyTo('');
this.bcc('');
+ this.signature('');
+ this.signatureInsertBefore(false);
this.email.hasError(false);
this.replyTo.hasError(false);
this.bcc.hasError(false);
+ this.showBcc(false);
+ this.showReplyTo(false);
+
this.submitRequest(false);
this.submitError('');
if (this.editor)
{
- this.editor.clear(false);
+ this.editor.setPlain('', false);
}
};
@@ -156,6 +180,8 @@
this.email(oIdentity.email());
this.replyTo(oIdentity.replyTo());
this.bcc(oIdentity.bcc());
+ this.signature(oIdentity.signature());
+ this.signatureInsertBefore(oIdentity.signatureInsertBefore());
this.owner(this.id === '');
}
@@ -185,15 +211,23 @@
}
};
+ IdentityPopupView.prototype.populateSignatureFromEditor = function ()
+ {
+ if (this.editor)
+ {
+ this.signature(
+ (this.editor.isHtml() ? ':HTML:' : '') + this.editor.getData()
+ );
+ }
+ };
+
IdentityPopupView.prototype.onFocus = function ()
{
if (!this.editor && this.signatureDom())
{
var self = this;
this.editor = new HtmlEditor(self.signatureDom(), function () {
- self.signature(
- (self.editor.isHtml() ? ':HTML:' : '') + self.editor.getData()
- );
+ self.populateSignatureFromEditor();
}, function () {
self.setSignature(self.signature());
});
diff --git a/dev/View/Popup/Template.js b/dev/View/Popup/Template.js
new file mode 100644
index 000000000..da43d16a3
--- /dev/null
+++ b/dev/View/Popup/Template.js
@@ -0,0 +1,179 @@
+
+(function () {
+
+ 'use strict';
+
+ var
+ _ = require('_'),
+ ko = require('ko'),
+
+ Enums = require('Common/Enums'),
+ Utils = require('Common/Utils'),
+ Translator = require('Common/Translator'),
+ HtmlEditor = require('Common/HtmlEditor'),
+
+ Remote = require('Storage/User/Remote'),
+
+ kn = require('Knoin/Knoin'),
+ AbstractView = require('Knoin/AbstractView')
+ ;
+
+ /**
+ * @constructor
+ * @extends AbstractView
+ */
+ function TemplatePopupView()
+ {
+ AbstractView.call(this, 'Popups', 'PopupsTemplate');
+
+ this.editor = null;
+ this.signatureDom = ko.observable(null);
+
+ this.id = ko.observable('');
+
+ this.name = ko.observable('');
+ this.name.error = ko.observable(false);
+ this.name.focus = ko.observable(false);
+
+ this.body = ko.observable('');
+ this.body.error = ko.observable(false);
+
+ this.name.subscribe(function () {
+ this.name.error(false);
+ }, this);
+
+ this.body.subscribe(function () {
+ this.body.error(false);
+ }, this);
+
+ this.submitRequest = ko.observable(false);
+ this.submitError = ko.observable('');
+
+ this.addTemplateCommand = Utils.createCommand(this, function () {
+
+ this.name.error('' === Utils.trim(this.name()));
+ this.body.error('' === Utils.trim(this.body()) ||
+ ':HTML:' === Utils.trim(this.body()));
+
+ if (this.name.error() || this.body.error())
+ {
+ return false;
+ }
+
+ this.submitRequest(true);
+
+ Remote.templateSetup(_.bind(function (sResult, oData) {
+
+ this.submitRequest(false);
+ if (Enums.StorageResultType.Success === sResult && oData)
+ {
+ if (oData.Result)
+ {
+ require('App/User').templates();
+ this.cancelCommand();
+ }
+ else if (oData.ErrorCode)
+ {
+ this.submitError(Translator.getNotification(oData.ErrorCode));
+ }
+ }
+ else
+ {
+ this.submitError(Translator.getNotification(Enums.Notification.UnknownError));
+ }
+
+ }, this), this.id(), this.name(), this.body());
+
+ return true;
+
+ }, function () {
+ return !this.submitRequest();
+ });
+
+ kn.constructorEnd(this);
+ }
+
+ kn.extendAsViewModel(['View/Popup/Template'], TemplatePopupView);
+ _.extend(TemplatePopupView.prototype, AbstractView.prototype);
+
+ TemplatePopupView.prototype.clearPopup = function ()
+ {
+ this.id('');
+
+ this.name('');
+ this.name.error(false);
+
+ this.body('');
+ this.body.loading(false);
+ this.body.error(false);
+
+ this.submitRequest(false);
+ this.submitError('');
+ };
+
+ TemplatePopupView.prototype.editorSetBody = function (sBody)
+ {
+ var
+ self = this,
+ fEditorSetData = function (sBody) {
+ if (self.editor)
+ {
+ if (':HTML:' === sBody.substr(0, 6))
+ {
+ self.editor.setHtml(sBody.substr(6), false);
+ }
+ else
+ {
+ self.editor.setPlain(sBody, false);
+ }
+ }
+ }
+ ;
+
+ if (!this.editor && this.signatureDom())
+ {
+ this.editor = new HtmlEditor(self.signatureDom(), function () {
+ self.body(
+ (self.editor.isHtml() ? ':HTML:' : '') + self.editor.getData()
+ );
+ }, function () {
+ fEditorSetData(sBody);
+ });
+ }
+ else if (this.editor)
+ {
+ fEditorSetData(sBody);
+ }
+ };
+
+ TemplatePopupView.prototype.onShow = function (oTemplate)
+ {
+ var self = this;
+
+ this.clearPopup();
+
+ if (oTemplate && oTemplate.id)
+ {
+ this.id(oTemplate.id);
+ this.name(oTemplate.name);
+
+ this.body.loading(true);
+
+ Remote.templateGetById(function () {
+
+ self.body.loading(false);
+
+ self.editorSetBody('');
+
+ }, this.id());
+ }
+ };
+
+ TemplatePopupView.prototype.onFocus = function ()
+ {
+ this.name.focus(true);
+ };
+
+ module.exports = TemplatePopupView;
+
+}());
\ No newline at end of file
diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js
index 847d14dba..8f74fbcb2 100644
--- a/dev/View/User/MailBox/MessageView.js
+++ b/dev/View/User/MailBox/MessageView.js
@@ -190,7 +190,7 @@
this.viewDate = ko.observable('');
this.viewSize = ko.observable('');
this.viewMoment = ko.observable('');
- this.viewLineAsCcc = ko.observable('');
+ this.viewLineAsCss = ko.observable('');
this.viewViewLink = ko.observable('');
this.viewDownloadLink = ko.observable('');
this.viewUserPic = ko.observable(Consts.DataImages.UserDotPic);
@@ -272,7 +272,7 @@
this.viewDate(oMessage.fullFormatDateValue());
this.viewSize(oMessage.friendlySize());
this.viewMoment(oMessage.momentDate());
- this.viewLineAsCcc(oMessage.lineAsCcc());
+ this.viewLineAsCss(oMessage.lineAsCss());
this.viewViewLink(oMessage.viewLink());
this.viewDownloadLink(oMessage.downloadLink());
this.viewIsImportant(oMessage.isImportant());
diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php
index 5196bb7b9..7186607b4 100644
--- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php
+++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php
@@ -2098,13 +2098,14 @@ class Actions
{
$sOrder = $this->StorageProvider()->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
- 'accounts_order'
+ 'accounts_identities_order'
);
$aOrder = empty($sOrder) ? array() : @\json_decode($sOrder, true);
- if (\is_array($aAccounts) && \is_array($aOrder) && 0 < \count($aOrder))
+ if (isset($aOrder['Accounts']) && \is_array($aOrder['Accounts']) &&
+ 1 < \count($aOrder['Accounts']))
{
- $aAccounts = \array_merge(\array_flip($aOrder), $aAccounts);
+ $aAccounts = \array_merge(\array_flip($aOrder['Accounts']), $aAccounts);
}
}
@@ -2174,6 +2175,24 @@ class Actions
\array_unshift($aIdentities,
\RainLoop\Model\Identity::NewInstanceFromAccount($oAccount));
}
+
+ if (1 < \count($aIdentities))
+ {
+ $sOrder = $this->StorageProvider()->Get($oAccount,
+ \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
+ 'accounts_identities_order'
+ );
+
+ $aOrder = empty($sOrder) ? array() : @\json_decode($sOrder, true);
+ if (isset($aOrder['Identities']) && \is_array($aOrder['Identities']) &&
+ 1 < \count($aOrder['Identities']))
+ {
+ $aList = $aOrder['Identities'];
+ \usort($aIdentities, function ($a, $b) use ($aList) {
+ return \array_search($a->Id(), $aList) < \array_search($b->Id(), $aList) ? -1 : 1;
+ });
+ }
+ }
}
return $aIdentities;
@@ -2483,19 +2502,24 @@ class Actions
*
* @throws \MailSo\Base\Exceptions\Exception
*/
- public function DoAccountSortOrder()
+ public function DoAccountsAndIdentitiesSortOrder()
{
$oAccount = $this->getAccountFromToken();
- $aList = $this->GetActionParam('Accounts', null);
- if (!\is_array($aList) || 2 > \count($aList))
+ $aAccounts = $this->GetActionParam('Accounts', null);
+ $aIdentities = $this->GetActionParam('Identities', null);
+
+ if (!\is_array($aAccounts) || !\is_array($aIdentities))
{
return $this->FalseResponse(__FUNCTION__);
}
return $this->DefaultResponse(__FUNCTION__, $this->StorageProvider()->Put($oAccount,
- \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'accounts_order',
- \json_encode($aList)
+ \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'accounts_identities_order',
+ \json_encode(array(
+ 'Accounts' => $aAccounts,
+ 'Identities' => $aIdentities
+ ))
));
}
@@ -4952,6 +4976,7 @@ class Actions
'IsSeen' => in_array('\\seen', $aLowerFlags),
'IsFlagged' => in_array('\\flagged', $aLowerFlags),
'IsAnswered' => in_array('\\answered', $aLowerFlags),
+ 'IsDeleted' => in_array('\\deleted', $aLowerFlags),
'IsForwarded' => 0 < strlen($sForwardedFlag) && in_array(strtolower($sForwardedFlag), $aLowerFlags),
'IsReadReceipt' => 0 < strlen($sReadReceiptFlag) && in_array(strtolower($sReadReceiptFlag), $aLowerFlags)
);
@@ -8503,6 +8528,7 @@ class Actions
$mResult['IsSeen'] = \in_array('\\seen', $aFlags);
$mResult['IsFlagged'] = \in_array('\\flagged', $aFlags);
$mResult['IsAnswered'] = \in_array('\\answered', $aFlags);
+ $mResult['IsDeleted'] = \in_array('\\deleted', $aFlags);
$sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', '');
$sReadReceiptFlag = $this->Config()->Get('labs', 'imap_read_receipt_flag', '');
diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Identity.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Identity.php
index 0d10cd6a2..19dd0c661 100644
--- a/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Identity.php
+++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Identity.php
@@ -29,6 +29,16 @@ class Identity
*/
private $sBcc;
+ /**
+ * @var string
+ */
+ private $sSignature;
+
+ /**
+ * @var bool
+ */
+ private $bSignatureInsertBefore;
+
/**
* @param string $sId = ''
* @param string $sEmail = ''
@@ -42,6 +52,8 @@ class Identity
$this->sName = '';
$this->sReplyTo = '';
$this->sBcc = '';
+ $this->sSignature = '';
+ $this->bSignatureInsertBefore = false;
}
/**
@@ -114,6 +126,22 @@ class Identity
return $this->sBcc;
}
+ /**
+ * @return string
+ */
+ public function Signature()
+ {
+ return $this->sSignature;
+ }
+
+ /**
+ * @return bool
+ */
+ public function SignatureInsertBefore()
+ {
+ return $this->bSignatureInsertBefore;
+ }
+
/**
* @param array $aData
* @param bool $bAjax = false
@@ -129,6 +157,9 @@ class Identity
$this->sName = isset($aData['Name']) ? $aData['Name'] : '';
$this->sReplyTo = !empty($aData['ReplyTo']) ? $aData['ReplyTo'] : '';
$this->sBcc = !empty($aData['Bcc']) ? $aData['Bcc'] : '';
+ $this->sSignature = !empty($aData['Signature']) ? $aData['Signature'] : '';
+ $this->bSignatureInsertBefore = isset($aData['SignatureInsertBefore']) ?
+ ($bAjax ? '1' === $aData['SignatureInsertBefore'] : !!$aData['SignatureInsertBefore']) : true;
return true;
}
@@ -148,7 +179,9 @@ class Identity
'Email' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->Email()) : $this->Email(),
'Name' => $this->Name(),
'ReplyTo' => $this->ReplyTo(),
- 'Bcc' => $this->Bcc()
+ 'Bcc' => $this->Bcc(),
+ 'Signature' => $this->Signature(),
+ 'SignatureInsertBefore' => $this->SignatureInsertBefore()
);
}
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html
index e046ed550..24d8a6c2e 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html
@@ -1,4 +1,4 @@
-
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItemNoPreviewPane.html b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItemNoPreviewPane.html
index d816fdb7b..9bb8dc536 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItemNoPreviewPane.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItemNoPreviewPane.html
@@ -1,4 +1,4 @@
-
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html
index a3f62142c..fad824d4b 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html
@@ -258,7 +258,7 @@
-
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html
index 373e76eb7..ac33f802e 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html
@@ -11,12 +11,10 @@
-
-
-
-
-
-
+
+
×
+
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsContacts.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsContacts.html
index 497a53675..5268b4045 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsContacts.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsContacts.html
@@ -89,7 +89,7 @@