mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-09 22:48:28 +03:00
commit
5fc83df67e
140 changed files with 3349 additions and 1341 deletions
|
|
@ -1,6 +1,8 @@
|
|||
RainLoop Webmail
|
||||
==================
|
||||
|
||||
[](https://gitter.im/RainLoop/rainloop-webmail?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Simple, modern & fast web-based email client.
|
||||
|
||||
Modest system requirements, decent performance, simple installation and upgrade, no database required - all these make RainLoop Webmail a perfect choice for your email solution.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ function saveLangStructure($sLangFile, $aLang)
|
|||
}
|
||||
}
|
||||
|
||||
\file_put_contents(LANGS_PATH.'/'.$sLangFile, implode("\n", $aResultLines));
|
||||
\file_put_contents(LANGS_PATH.'/'.$sLangFile, implode("\n", $aResultLines)."\n");
|
||||
}
|
||||
|
||||
$sNL = "\n";
|
||||
|
|
|
|||
19
build/test.php
Normal file
19
build/test.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
// simple connection test
|
||||
|
||||
$host = 'imap.gmail.com';
|
||||
$port = 993;
|
||||
|
||||
echo $host.':'.$port;
|
||||
|
||||
$errorStr = '';
|
||||
$errorNo = 0;
|
||||
|
||||
$connection = stream_socket_client($host.':'.$port, $errorNo, $errorStr, 5, STREAM_CLIENT_CONNECT);
|
||||
if (is_resource($connection)) {
|
||||
echo ' = OK';
|
||||
fclose($connection);
|
||||
} else {
|
||||
echo ' = ERROR ([#'.$errorNo.'] '.$errorStr.')';
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
|
||||
}, 50));
|
||||
|
||||
// TODO
|
||||
// DEBUG
|
||||
// Events.sub({
|
||||
// 'window.resize': function () {
|
||||
// window.console.log('window.resize');
|
||||
|
|
@ -284,14 +284,17 @@
|
|||
|
||||
ko.components.register('x-script', require('Component/Script'));
|
||||
|
||||
if (Settings.settingsGet('MaterialDesign') && Globals.bAnimationSupported)
|
||||
if (/**false && /**/Settings.settingsGet('MaterialDesign') && Globals.bAnimationSupported)
|
||||
{
|
||||
ko.components.register('Checkbox', require('Component/MaterialDesign/Checkbox'));
|
||||
ko.components.register('CheckboxSimple', require('Component/Checkbox'));
|
||||
}
|
||||
else
|
||||
{
|
||||
// ko.components.register('Checkbox', require('Component/Classic/Checkbox'));
|
||||
// ko.components.register('CheckboxSimple', require('Component/Classic/Checkbox'));
|
||||
ko.components.register('Checkbox', require('Component/Checkbox'));
|
||||
ko.components.register('CheckboxSimple', require('Component/Checkbox'));
|
||||
}
|
||||
|
||||
Translator.initOnStartOrLangChange(Translator.initNotificationLanguage, Translator);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,12 @@
|
|||
Events.pub('interval.20m');
|
||||
}, 60000 * 15);
|
||||
|
||||
window.setTimeout(function () {
|
||||
window.setInterval(function () {
|
||||
Events.pub('interval.2m-after5m');
|
||||
}, 60000 * 2);
|
||||
}, 60000 * 5);
|
||||
|
||||
window.setTimeout(function () {
|
||||
window.setInterval(function () {
|
||||
Events.pub('interval.5m-after5m');
|
||||
|
|
@ -174,7 +180,15 @@
|
|||
if (Utils.isUnd(bDropPagePosition) ? false : !!bDropPagePosition)
|
||||
{
|
||||
MessageStore.messageListPage(1);
|
||||
MessageStore.messageListPageBeforeThread(1);
|
||||
iOffset = 0;
|
||||
|
||||
kn.setHash(Links.mailBox(
|
||||
FolderStore.currentFolderFullNameHash(),
|
||||
MessageStore.messageListPage(),
|
||||
MessageStore.messageListSearch(),
|
||||
MessageStore.messageListThreadUid()
|
||||
), true, true);
|
||||
}
|
||||
|
||||
MessageStore.messageListLoading(true);
|
||||
|
|
@ -201,17 +215,18 @@
|
|||
);
|
||||
}
|
||||
|
||||
}, FolderStore.currentFolderFullNameRaw(), iOffset, SettingsStore.messagesPerPage(), MessageStore.messageListSearch());
|
||||
}, FolderStore.currentFolderFullNameRaw(), iOffset, SettingsStore.messagesPerPage(),
|
||||
MessageStore.messageListSearch(), MessageStore.messageListThreadUid());
|
||||
};
|
||||
|
||||
AppUser.prototype.recacheInboxMessageList = function ()
|
||||
{
|
||||
Remote.messageList(Utils.emptyFunction, Cache.getFolderInboxName(), 0, SettingsStore.messagesPerPage(), '', true);
|
||||
Remote.messageList(Utils.emptyFunction, Cache.getFolderInboxName(), 0, SettingsStore.messagesPerPage(), '', '', true);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Function} fResultFunc
|
||||
* @returns {boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
AppUser.prototype.contactsSync = function (fResultFunc)
|
||||
{
|
||||
|
|
@ -754,7 +769,7 @@
|
|||
var
|
||||
self = this,
|
||||
iUtc = Momentor.momentNowUnix(),
|
||||
aFolders = FolderStore.getNextFolderNames(bBoot)
|
||||
aFolders = FolderStore.getNextFolderNames()
|
||||
;
|
||||
|
||||
if (Utils.isNonEmptyArray(aFolders))
|
||||
|
|
@ -824,7 +839,8 @@
|
|||
});
|
||||
|
||||
if (bBoot)
|
||||
{ _.delay(function () {
|
||||
{
|
||||
_.delay(function () {
|
||||
self.folderInformationMultiply(true);
|
||||
}, 2000);
|
||||
}
|
||||
|
|
@ -843,10 +859,8 @@
|
|||
AppUser.prototype.messageListAction = function (sFolderFullNameRaw, mUid, iSetAction, aMessages)
|
||||
{
|
||||
var
|
||||
bRoot = false,
|
||||
aAllUids = [],
|
||||
aRootUids = [],
|
||||
oFolder = null,
|
||||
aRootUids = [],
|
||||
iAlreadyUnread = 0
|
||||
;
|
||||
|
||||
|
|
@ -855,31 +869,16 @@
|
|||
aMessages = MessageStore.messageListChecked();
|
||||
}
|
||||
|
||||
if (true === mUid)
|
||||
{
|
||||
bRoot = true;
|
||||
}
|
||||
else if (aMessages && aMessages[0] && mUid &&
|
||||
sFolderFullNameRaw === aMessages[0].uid && mUid === aMessages[0].uid)
|
||||
{
|
||||
bRoot = true;
|
||||
}
|
||||
aRootUids = _.uniq(_.compact(_.map(aMessages, function (oMessage) {
|
||||
return (oMessage && oMessage.uid) ? oMessage.uid : null;
|
||||
})));
|
||||
|
||||
_.each(aMessages, function (oMessage) {
|
||||
aRootUids.push(oMessage.uid);
|
||||
aAllUids = _.union(aAllUids, oMessage.threads(), [oMessage.uid]);
|
||||
});
|
||||
|
||||
aAllUids = _.uniq(aAllUids);
|
||||
aRootUids = _.uniq(aRootUids);
|
||||
aRootUids = aAllUids; // always all
|
||||
|
||||
if ('' !== sFolderFullNameRaw && 0 < aAllUids.length)
|
||||
if ('' !== sFolderFullNameRaw && 0 < aRootUids.length)
|
||||
{
|
||||
switch (iSetAction) {
|
||||
case Enums.MessageSetAction.SetSeen:
|
||||
|
||||
_.each(bRoot ? aAllUids : aRootUids, function (sSubUid) {
|
||||
_.each(aRootUids, function (sSubUid) {
|
||||
iAlreadyUnread += Cache.storeMessageFlagsToCacheBySetAction(
|
||||
sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
});
|
||||
|
|
@ -890,7 +889,7 @@
|
|||
oFolder.messageCountUnread(oFolder.messageCountUnread() - iAlreadyUnread);
|
||||
}
|
||||
|
||||
Remote.messageSetSeen(Utils.emptyFunction, sFolderFullNameRaw, bRoot ? aAllUids : aRootUids, true);
|
||||
Remote.messageSetSeen(Utils.emptyFunction, sFolderFullNameRaw, aRootUids, true);
|
||||
break;
|
||||
|
||||
case Enums.MessageSetAction.UnsetSeen:
|
||||
|
|
@ -921,12 +920,12 @@
|
|||
|
||||
case Enums.MessageSetAction.UnsetFlag:
|
||||
|
||||
_.each(bRoot ? aAllUids : aRootUids, function (sSubUid) {
|
||||
_.each(aRootUids, function (sSubUid) {
|
||||
Cache.storeMessageFlagsToCacheBySetAction(
|
||||
sFolderFullNameRaw, sSubUid, iSetAction);
|
||||
});
|
||||
|
||||
Remote.messageSetFlagged(Utils.emptyFunction, sFolderFullNameRaw, bRoot ? aAllUids : aRootUids, false);
|
||||
Remote.messageSetFlagged(Utils.emptyFunction, sFolderFullNameRaw, aRootUids, false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1369,7 +1368,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
Events.sub('interval.5m-after5m', function () {
|
||||
Events.sub('interval.2m-after5m', function () {
|
||||
self.folderInformationMultiply();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -20,58 +20,90 @@
|
|||
{
|
||||
var self = this;
|
||||
|
||||
this.obj = this.createNewObject();
|
||||
this.objForNotification = this.createNewObject();
|
||||
// this.userMedia = window.navigator.getUserMedia || window.navigator.webkitGetUserMedia ||
|
||||
// window.navigator.mozGetUserMedia || window.navigator.msGetUserMedia;
|
||||
//
|
||||
// this.audioContext = window.AudioContext || window.webkitAudioContext;
|
||||
// if (!this.audioContext || !window.Float32Array)
|
||||
// {
|
||||
// this.audioContext = null;
|
||||
// this.userMedia = null;
|
||||
// }
|
||||
|
||||
this.supported = !Globals.bMobileDevice && !Globals.bSafari &&
|
||||
this.obj && '' !== this.obj.canPlayType('audio/mpeg');
|
||||
this.player = this.createNewObject();
|
||||
|
||||
if (this.obj && this.supported)
|
||||
this.supported = !Globals.bMobileDevice && !Globals.bSafari && !!this.player && !!this.player.play;
|
||||
if (this.supported && this.player.canPlayType)
|
||||
{
|
||||
this.objForNotification.src = Links.sound('new-mail.mp3');
|
||||
this.supportedMp3 = '' !== this.player.canPlayType('audio/mpeg;').replace(/no/, '');
|
||||
this.supportedWav = '' !== this.player.canPlayType('audio/wav; codecs="1"').replace(/no/, '');
|
||||
this.supportedOgg = '' !== this.player.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, '');
|
||||
this.supportedNotification = this.supported && this.supportedMp3;
|
||||
}
|
||||
|
||||
$(this.obj).on('ended error', function () {
|
||||
if (!this.player || (!this.supportedMp3 && !this.supportedOgg && !this.supportedWav))
|
||||
{
|
||||
this.supported = false;
|
||||
this.supportedMp3 = false;
|
||||
this.supportedOgg = false;
|
||||
this.supportedWav = false;
|
||||
this.supportedNotification = false;
|
||||
}
|
||||
|
||||
if (this.supported)
|
||||
{
|
||||
$(this.player).on('ended error', function () {
|
||||
self.stop();
|
||||
});
|
||||
|
||||
Events.sub('audio.api.stop', function () {
|
||||
self.stop();
|
||||
});
|
||||
|
||||
Events.sub('audio.api.play', function (sUrl, sName) {
|
||||
self.playMp3(sUrl, sName);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Audio.prototype.obj = null;
|
||||
Audio.prototype.objForNotification = null;
|
||||
Audio.prototype.player = null;
|
||||
Audio.prototype.notificator = null;
|
||||
|
||||
Audio.prototype.supported = false;
|
||||
Audio.prototype.supportedMp3 = false;
|
||||
Audio.prototype.supportedOgg = false;
|
||||
Audio.prototype.supportedWav = false;
|
||||
Audio.prototype.supportedNotification = false;
|
||||
|
||||
// Audio.prototype.record = function ()
|
||||
// {
|
||||
// this.getUserMedia({audio:true}, function () {
|
||||
// window.console.log(arguments);
|
||||
// }, function(oError) {
|
||||
// window.console.log(arguments);
|
||||
// });
|
||||
// };
|
||||
|
||||
Audio.prototype.createNewObject = function ()
|
||||
{
|
||||
var obj = window.Audio ? new window.Audio() : null;
|
||||
if (obj && obj.canPlayType)
|
||||
var player = window.Audio ? new window.Audio() : null;
|
||||
if (player && player.canPlayType && player.pause && player.play)
|
||||
{
|
||||
obj.preload = 'none';
|
||||
obj.loop = false;
|
||||
obj.autoplay = false;
|
||||
obj.muted = false;
|
||||
player.preload = 'none';
|
||||
player.loop = false;
|
||||
player.autoplay = false;
|
||||
player.muted = false;
|
||||
}
|
||||
|
||||
return obj;
|
||||
return player;
|
||||
};
|
||||
|
||||
Audio.prototype.paused = function ()
|
||||
{
|
||||
return this.supported ? !!this.obj.paused : true;
|
||||
return this.supported ? !!this.player.paused : true;
|
||||
};
|
||||
|
||||
Audio.prototype.stop = function ()
|
||||
{
|
||||
if (this.supported && this.obj.pause)
|
||||
if (this.supported && this.player.pause)
|
||||
{
|
||||
this.obj.pause();
|
||||
this.player.pause();
|
||||
}
|
||||
|
||||
Events.pub('audio.stop');
|
||||
|
|
@ -79,33 +111,73 @@
|
|||
|
||||
Audio.prototype.pause = Audio.prototype.stop;
|
||||
|
||||
Audio.prototype.clearName = function (sName, sExt)
|
||||
{
|
||||
sExt = sExt || '';
|
||||
sName = Utils.isUnd(sName) ? '' : Utils.trim(sName);
|
||||
if (sExt && '.' + sExt === sName.toLowerCase().substr((sExt.length + 1) * -1))
|
||||
{
|
||||
sName = Utils.trim(sName.substr(0, sName.length - 4));
|
||||
}
|
||||
|
||||
if ('' === sName)
|
||||
{
|
||||
sName = 'audio';
|
||||
}
|
||||
|
||||
return sName;
|
||||
};
|
||||
|
||||
Audio.prototype.playMp3 = function (sUrl, sName)
|
||||
{
|
||||
if (this.supported && this.obj.play)
|
||||
if (this.supported && this.supportedMp3)
|
||||
{
|
||||
this.obj.src = sUrl;
|
||||
this.obj.play();
|
||||
this.player.src = sUrl;
|
||||
this.player.play();
|
||||
|
||||
sName = Utils.isUnd(sName) ? '' : Utils.trim(sName);
|
||||
if ('.mp3' === sName.toLowerCase().substr(-4))
|
||||
{
|
||||
sName = Utils.trim(sName.substr(0, sName.length - 4));
|
||||
}
|
||||
Events.pub('audio.start', [this.clearName(sName, 'mp3'), 'mp3']);
|
||||
}
|
||||
};
|
||||
|
||||
if ('' === sName)
|
||||
{
|
||||
sName = 'audio';
|
||||
}
|
||||
Audio.prototype.playOgg = function (sUrl, sName)
|
||||
{
|
||||
if (this.supported && this.supportedOgg)
|
||||
{
|
||||
this.player.src = sUrl;
|
||||
this.player.play();
|
||||
|
||||
Events.pub('audio.start', [sName]);
|
||||
sName = this.clearName(sName, 'oga');
|
||||
sName = this.clearName(sName, 'ogg');
|
||||
|
||||
Events.pub('audio.start', [sName, 'ogg']);
|
||||
}
|
||||
};
|
||||
|
||||
Audio.prototype.playWav = function (sUrl, sName)
|
||||
{
|
||||
if (this.supported && this.supportedWav)
|
||||
{
|
||||
this.player.src = sUrl;
|
||||
this.player.play();
|
||||
|
||||
Events.pub('audio.start', [this.clearName(sName, 'wav'), 'wav']);
|
||||
}
|
||||
};
|
||||
|
||||
Audio.prototype.playNotification = function ()
|
||||
{
|
||||
if (this.supported && this.objForNotification.play)
|
||||
if (this.supported && this.supportedMp3)
|
||||
{
|
||||
this.objForNotification.play();
|
||||
if (!this.notificator)
|
||||
{
|
||||
this.notificator = this.createNewObject();
|
||||
this.notificator.src = Links.sound('new-mail.mp3');
|
||||
}
|
||||
|
||||
if (this.notificator && this.notificator.play)
|
||||
{
|
||||
this.notificator.play();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -302,12 +302,16 @@
|
|||
|
||||
if (aFlags && 0 < aFlags.length)
|
||||
{
|
||||
oMessage.unseen(!!aFlags[0]);
|
||||
oMessage.flagged(!!aFlags[1]);
|
||||
oMessage.answered(!!aFlags[2]);
|
||||
oMessage.forwarded(!!aFlags[3]);
|
||||
oMessage.isReadReceipt(!!aFlags[4]);
|
||||
oMessage.deletedMark(!!aFlags[5]);
|
||||
|
||||
if (!oMessage.__simple_message__)
|
||||
{
|
||||
oMessage.unseen(!!aFlags[0]);
|
||||
oMessage.answered(!!aFlags[2]);
|
||||
oMessage.forwarded(!!aFlags[3]);
|
||||
oMessage.isReadReceipt(!!aFlags[4]);
|
||||
oMessage.deletedMark(!!aFlags[5]);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < oMessage.threads().length)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,27 @@
|
|||
|
||||
var Enums = {};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.FileType = {
|
||||
'Unknown': 'unknown',
|
||||
'Text': 'text',
|
||||
'Html': 'html',
|
||||
'Code': 'code',
|
||||
'Eml': 'eml',
|
||||
'WordText': 'word-text',
|
||||
'Pdf': 'pdf',
|
||||
'Image': 'image',
|
||||
'Audio': 'audio',
|
||||
'Video': 'video',
|
||||
'Sheet': 'sheet',
|
||||
'Presentation': 'presentation',
|
||||
'Certificate': 'certificate',
|
||||
'CertificateBin': 'certificate-bin',
|
||||
'Archive': 'archive'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
Globals.$win = $(window);
|
||||
Globals.$doc = $(window.document);
|
||||
Globals.$html = $('html');
|
||||
Globals.$body = $('body');
|
||||
Globals.$div = $('<div></div>');
|
||||
|
||||
Globals.$win.__sizes = [0, 0];
|
||||
|
|
@ -268,7 +269,7 @@
|
|||
});
|
||||
|
||||
Globals.keyScopeReal.subscribe(function (sValue) {
|
||||
// window.console.log('keyScope=' + sValue); // TODO
|
||||
// window.console.log('keyScope=' + sValue); // DEBUG
|
||||
key.setScope(sValue);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
this.resize = _.throttle(_.bind(this.resize, this), 100);
|
||||
|
||||
this.__inited = false;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +192,7 @@
|
|||
|
||||
HtmlEditor.prototype.setHtml = function (sHtml, bFocus)
|
||||
{
|
||||
if (this.editor)
|
||||
if (this.editor && this.__inited)
|
||||
{
|
||||
this.modeToggle(true);
|
||||
|
||||
|
|
@ -207,7 +209,7 @@
|
|||
|
||||
HtmlEditor.prototype.setPlain = function (sPlain, bFocus)
|
||||
{
|
||||
if (this.editor)
|
||||
if (this.editor && this.__inited)
|
||||
{
|
||||
this.modeToggle(false);
|
||||
if ('plain' === this.editor.mode && this.editor.plugins.plain && this.editor.__plain)
|
||||
|
|
@ -259,7 +261,7 @@
|
|||
}
|
||||
|
||||
oConfig.enterMode = window.CKEDITOR.ENTER_BR;
|
||||
oConfig.shiftEnterMode = window.CKEDITOR.ENTER_BR;
|
||||
oConfig.shiftEnterMode = window.CKEDITOR.ENTER_P;
|
||||
|
||||
oConfig.language = Globals.oHtmlEditorLangsMap[sLanguage] || 'en';
|
||||
if (window.CKEDITOR.env)
|
||||
|
|
@ -267,17 +269,6 @@
|
|||
window.CKEDITOR.env.isCompatible = true;
|
||||
}
|
||||
|
||||
// oConfig.allowedContent = {
|
||||
// $1: {
|
||||
// elements: window.CKEDITOR.dtd,
|
||||
// attributes: true,
|
||||
// styles: true,
|
||||
// classes: true
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// oConfig.disallowedContent = 'script; style; iframe; frame; *[on*]';
|
||||
|
||||
window.CKEDITOR.dtd.$removeEmpty['p'] = 1;
|
||||
|
||||
self.editor = window.CKEDITOR.appendTo(self.$element[0], oConfig);
|
||||
|
|
@ -307,24 +298,29 @@
|
|||
self.focusTrigger();
|
||||
});
|
||||
|
||||
if (self.fOnReady)
|
||||
{
|
||||
self.editor.on('instanceReady', function () {
|
||||
self.editor.on('instanceReady', function () {
|
||||
|
||||
if (self.editor.removeMenuItem)
|
||||
{
|
||||
self.editor.removeMenuItem('cut');
|
||||
self.editor.removeMenuItem('copy');
|
||||
self.editor.removeMenuItem('paste');
|
||||
}
|
||||
if (self.editor.removeMenuItem)
|
||||
{
|
||||
self.editor.removeMenuItem('cut');
|
||||
self.editor.removeMenuItem('copy');
|
||||
self.editor.removeMenuItem('paste');
|
||||
}
|
||||
|
||||
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65 /* A */, 'selectAll');
|
||||
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65 /* A */, 'selectAll');
|
||||
|
||||
|
||||
self.__resizable = true;
|
||||
self.__inited = true;
|
||||
|
||||
self.resize();
|
||||
|
||||
if (self.fOnReady)
|
||||
{
|
||||
self.fOnReady();
|
||||
self.__resizable = true;
|
||||
self.resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -59,47 +59,62 @@
|
|||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @param {string=} sCustomSpecSuffix
|
||||
* @return {string}
|
||||
*/
|
||||
Links.prototype.attachmentDownload = function (sDownload)
|
||||
Links.prototype.attachmentDownload = function (sDownload, sCustomSpecSuffix)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + this.sSpecSuffix + '/Download/' + this.subQueryPrefix() + '/' + sDownload;
|
||||
sCustomSpecSuffix = Utils.isUnd(sCustomSpecSuffix) ? this.sSpecSuffix : sCustomSpecSuffix;
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + sCustomSpecSuffix + '/Download/' +
|
||||
this.subQueryPrefix() + '/' + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @param {string=} sCustomSpecSuffix
|
||||
* @return {string}
|
||||
*/
|
||||
Links.prototype.attachmentPreview = function (sDownload)
|
||||
Links.prototype.attachmentPreview = function (sDownload, sCustomSpecSuffix)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + this.sSpecSuffix + '/View/' + this.subQueryPrefix() + '/' + sDownload;
|
||||
sCustomSpecSuffix = Utils.isUnd(sCustomSpecSuffix) ? this.sSpecSuffix : sCustomSpecSuffix;
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + sCustomSpecSuffix + '/View/' +
|
||||
this.subQueryPrefix() + '/' + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @param {string=} sCustomSpecSuffix
|
||||
* @return {string}
|
||||
*/
|
||||
Links.prototype.attachmentThumbnailPreview = function (sDownload)
|
||||
Links.prototype.attachmentThumbnailPreview = function (sDownload, sCustomSpecSuffix)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + this.sSpecSuffix + '/ViewThumbnail/' + this.subQueryPrefix() + '/' + sDownload;
|
||||
sCustomSpecSuffix = Utils.isUnd(sCustomSpecSuffix) ? this.sSpecSuffix : sCustomSpecSuffix;
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + sCustomSpecSuffix + '/ViewThumbnail/' +
|
||||
this.subQueryPrefix() + '/' + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @param {string=} sCustomSpecSuffix
|
||||
* @return {string}
|
||||
*/
|
||||
Links.prototype.attachmentPreviewAsPlain = function (sDownload)
|
||||
Links.prototype.attachmentPreviewAsPlain = function (sDownload, sCustomSpecSuffix)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + this.sSpecSuffix + '/ViewAsPlain/' + this.subQueryPrefix() + '/' + sDownload;
|
||||
sCustomSpecSuffix = Utils.isUnd(sCustomSpecSuffix) ? this.sSpecSuffix : sCustomSpecSuffix;
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + sCustomSpecSuffix + '/ViewAsPlain/' +
|
||||
this.subQueryPrefix() + '/' + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @param {string=} sCustomSpecSuffix
|
||||
* @return {string}
|
||||
*/
|
||||
Links.prototype.attachmentFramed = function (sDownload)
|
||||
Links.prototype.attachmentFramed = function (sDownload, sCustomSpecSuffix)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + this.sSpecSuffix + '/FramedView/' + this.subQueryPrefix() + '/' + sDownload;
|
||||
sCustomSpecSuffix = Utils.isUnd(sCustomSpecSuffix) ? this.sSpecSuffix : sCustomSpecSuffix;
|
||||
return this.sServer + '/Raw/' + this.subQueryPrefix() + '/' + sCustomSpecSuffix + '/FramedView/' +
|
||||
this.subQueryPrefix() + '/' + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -257,23 +272,30 @@
|
|||
* @param {string} sFolder
|
||||
* @param {number=} iPage = 1
|
||||
* @param {string=} sSearch = ''
|
||||
* @param {string=} sThreadUid = ''
|
||||
* @return {string}
|
||||
*/
|
||||
Links.prototype.mailBox = function (sFolder, iPage, sSearch)
|
||||
Links.prototype.mailBox = function (sFolder, iPage, sSearch, sThreadUid)
|
||||
{
|
||||
iPage = Utils.isNormal(iPage) ? Utils.pInt(iPage) : 1;
|
||||
sSearch = Utils.pString(sSearch);
|
||||
|
||||
var sResult = this.sBase + 'mailbox/';
|
||||
var
|
||||
sResult = this.sBase + 'mailbox/',
|
||||
iThreadUid = Utils.pInt(sThreadUid)
|
||||
;
|
||||
|
||||
if ('' !== sFolder)
|
||||
{
|
||||
sResult += encodeURI(sFolder);
|
||||
sResult += encodeURI(sFolder) + (0 < iThreadUid ? '~' + iThreadUid : '');
|
||||
}
|
||||
|
||||
if (1 < iPage)
|
||||
{
|
||||
sResult = sResult.replace(/[\/]+$/, '');
|
||||
sResult += '/p' + iPage;
|
||||
}
|
||||
|
||||
if ('' !== sSearch)
|
||||
{
|
||||
sResult = sResult.replace(/[\/]+$/, '');
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
Selector.prototype.autoSelect = function ()
|
||||
{
|
||||
|
|
@ -452,7 +452,7 @@
|
|||
|
||||
/**
|
||||
* @param {Object} oItem
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
Selector.prototype.getItemUid = function (oItem)
|
||||
{
|
||||
|
|
@ -633,13 +633,20 @@
|
|||
|
||||
var
|
||||
iOffset = 20,
|
||||
aList = this.list(),
|
||||
oFocused = $(this.sItemFocusedSelector, this.oContentScrollable),
|
||||
oPos = oFocused.position(),
|
||||
iVisibleHeight = this.oContentVisible.height(),
|
||||
iFocusedHeight = oFocused.outerHeight()
|
||||
;
|
||||
|
||||
if (oPos && (oPos.top < 0 || oPos.top + iFocusedHeight > iVisibleHeight))
|
||||
if (aList && aList[0] && aList[0].focused())
|
||||
{
|
||||
this.oContentScrollable.scrollTop(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (oPos && (oPos.top < 0 || oPos.top + iFocusedHeight > iVisibleHeight))
|
||||
{
|
||||
if (oPos.top < 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@
|
|||
/**
|
||||
* @param {string} sMailToUrl
|
||||
* @param {Function} PopupComposeVoreModel
|
||||
* @returns {boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
Utils.mailToHelper = function (sMailToUrl, PopupComposeVoreModel)
|
||||
{
|
||||
|
|
@ -598,7 +598,7 @@
|
|||
* @param {string} sTheme
|
||||
* @return {string}
|
||||
*/
|
||||
Utils.convertThemeName = function (sTheme)
|
||||
Utils.convertThemeName = _.memoize(function (sTheme)
|
||||
{
|
||||
if ('@custom' === sTheme.substr(-7))
|
||||
{
|
||||
|
|
@ -606,7 +606,7 @@
|
|||
}
|
||||
|
||||
return Utils.trim(sTheme.replace(/[^a-zA-Z0-9]+/g, ' ').replace(/([A-Z])/g, ' $1').replace(/[\s]+/g, ' '));
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} sName
|
||||
|
|
@ -822,40 +822,6 @@
|
|||
|
||||
sText = '',
|
||||
|
||||
splitPlainText = function (sText)
|
||||
{
|
||||
var
|
||||
iLen = 100,
|
||||
sPrefix = '',
|
||||
sSubText = '',
|
||||
sResult = sText,
|
||||
iSpacePos = 0,
|
||||
iNewLinePos = 0
|
||||
;
|
||||
|
||||
while (sResult.length > iLen)
|
||||
{
|
||||
sSubText = sResult.substring(0, iLen);
|
||||
iSpacePos = sSubText.lastIndexOf(' ');
|
||||
iNewLinePos = sSubText.lastIndexOf('\n');
|
||||
|
||||
if (-1 !== iNewLinePos)
|
||||
{
|
||||
iSpacePos = iNewLinePos;
|
||||
}
|
||||
|
||||
if (-1 === iSpacePos)
|
||||
{
|
||||
iSpacePos = iLen;
|
||||
}
|
||||
|
||||
sPrefix += sSubText.substring(0, iSpacePos) + '\n';
|
||||
sResult = sResult.substring(iSpacePos + 1);
|
||||
}
|
||||
|
||||
return sPrefix + sResult;
|
||||
},
|
||||
|
||||
convertBlockquote = function (sText) {
|
||||
sText = Utils.trim(sText);
|
||||
sText = '> ' + sText.replace(/\n/gm, '\n> ');
|
||||
|
|
@ -929,7 +895,7 @@
|
|||
.replace(/&/gi, '&')
|
||||
;
|
||||
|
||||
sText = splitPlainText(Utils.trim(sText));
|
||||
sText = Utils.splitPlainText(Utils.trim(sText));
|
||||
|
||||
iPos = 0;
|
||||
iLimit = 800;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
this.inverted = Utils.isUnd(oParams.inverted) ? false : !!oParams.inverted;
|
||||
|
||||
this.labeled = !Utils.isUnd(oParams.label);
|
||||
this.labelAnimated = !!oParams.labelAnimated;
|
||||
}
|
||||
|
||||
_.extend(AbstracCheckbox.prototype, AbstractComponent.prototype);
|
||||
|
|
@ -59,6 +60,13 @@
|
|||
}
|
||||
};
|
||||
|
||||
AbstracCheckbox.prototype.keypress = function() {
|
||||
if (!this.readOnly && this.enable() && !this.disable())
|
||||
{
|
||||
this.value(!this.value());
|
||||
}
|
||||
};
|
||||
|
||||
AbstracCheckbox.componentExportHelper = AbstractComponent.componentExportHelper;
|
||||
|
||||
module.exports = AbstracCheckbox;
|
||||
|
|
|
|||
14
dev/External/Opentip.js
vendored
14
dev/External/Opentip.js
vendored
|
|
@ -9,10 +9,21 @@
|
|||
;
|
||||
|
||||
Opentip.styles.rainloop = {
|
||||
|
||||
'extends': 'standard',
|
||||
|
||||
'fixed': true,
|
||||
'target': true,
|
||||
|
||||
'delay': 0.2,
|
||||
'hideDelay': 0,
|
||||
|
||||
'hideEffect': 'fade',
|
||||
'hideEffectDuration': 0.2,
|
||||
|
||||
'showEffect': 'fade',
|
||||
'showEffectDuration': 0.2,
|
||||
|
||||
'showOn': 'mouseover click',
|
||||
'removeElementsOnHide': true,
|
||||
|
||||
|
|
@ -26,8 +37,7 @@
|
|||
|
||||
Opentip.styles.rainloopTip = {
|
||||
'extends': 'rainloop',
|
||||
'stemLength': 3,
|
||||
'stemBase': 5,
|
||||
'delay': 0.4,
|
||||
'group': 'rainloopTips'
|
||||
};
|
||||
|
||||
|
|
|
|||
79
dev/External/ko.js
vendored
79
dev/External/ko.js
vendored
|
|
@ -24,31 +24,41 @@
|
|||
'init': function (oElement, fValueAccessor) {
|
||||
|
||||
var
|
||||
fValue = fValueAccessor(),
|
||||
oEditor = null,
|
||||
fValue = fValueAccessor(),
|
||||
|
||||
fUpdateEditorValue = function () {
|
||||
if (oEditor)
|
||||
if (fValue && fValue.__editor)
|
||||
{
|
||||
oEditor.setHtmlOrPlain(fValue());
|
||||
fValue.__editor.setHtmlOrPlain(fValue());
|
||||
}
|
||||
},
|
||||
|
||||
fUpdateKoValue = function () {
|
||||
if (oEditor)
|
||||
if (fValue && fValue.__editor)
|
||||
{
|
||||
fValue(oEditor.getDataWithHtmlMark());
|
||||
fValue(fValue.__editor.getDataWithHtmlMark());
|
||||
}
|
||||
},
|
||||
|
||||
fOnReady = function () {
|
||||
fValue.__editor = oEditor;
|
||||
fUpdateEditorValue();
|
||||
},
|
||||
|
||||
HtmlEditor = require('Common/HtmlEditor')
|
||||
;
|
||||
|
||||
if (fValue)
|
||||
if (ko.isObservable(fValue) && HtmlEditor)
|
||||
{
|
||||
oEditor = new HtmlEditor(oElement, fUpdateKoValue, fUpdateEditorValue, fUpdateKoValue);
|
||||
fValue.__editor = oEditor;
|
||||
oEditor = new HtmlEditor(oElement, fUpdateKoValue, fOnReady, fUpdateKoValue);
|
||||
|
||||
fValue.__fetchEditorValue = fUpdateKoValue;
|
||||
fValue.__updateEditorValue = fUpdateEditorValue;
|
||||
|
||||
fValue.subscribe(fUpdateEditorValue);
|
||||
|
||||
// ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () {
|
||||
// });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -61,6 +71,7 @@
|
|||
sValue = '',
|
||||
Translator = null,
|
||||
$oEl = $(oElement),
|
||||
fValue = fValueAccessor(),
|
||||
bMobile = 'on' === ($oEl.data('tooltip-mobile') || 'off'),
|
||||
Globals = require('Common/Globals')
|
||||
;
|
||||
|
|
@ -68,7 +79,7 @@
|
|||
if (!Globals.bMobileDevice || bMobile)
|
||||
{
|
||||
bi18n = 'on' === ($oEl.data('tooltip-i18n') || 'on');
|
||||
sValue = ko.unwrap(fValueAccessor());
|
||||
sValue = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue);
|
||||
|
||||
oElement.__opentip = new Opentip(oElement, {
|
||||
'style': 'rainloopTip',
|
||||
|
|
@ -78,12 +89,21 @@
|
|||
|
||||
Globals.dropdownVisibility.subscribe(function (bV) {
|
||||
if (bV) {
|
||||
oElement.__opentip.deactivate();
|
||||
} else {
|
||||
oElement.__opentip.activate();
|
||||
oElement.__opentip.hide();
|
||||
}
|
||||
});
|
||||
|
||||
if ('' === sValue)
|
||||
{
|
||||
oElement.__opentip.hide();
|
||||
oElement.__opentip.deactivate();
|
||||
oElement.__opentip.setContent('');
|
||||
}
|
||||
else
|
||||
{
|
||||
oElement.__opentip.activate();
|
||||
}
|
||||
|
||||
if (bi18n)
|
||||
{
|
||||
Translator = require('Common/Translator');
|
||||
|
|
@ -113,6 +133,7 @@
|
|||
bi18n = true,
|
||||
sValue = '',
|
||||
$oEl = $(oElement),
|
||||
fValue = fValueAccessor(),
|
||||
bMobile = 'on' === ($oEl.data('tooltip-mobile') || 'off'),
|
||||
Globals = require('Common/Globals')
|
||||
;
|
||||
|
|
@ -120,7 +141,7 @@
|
|||
if ((!Globals.bMobileDevice || bMobile) && oElement.__opentip)
|
||||
{
|
||||
bi18n = 'on' === ($oEl.data('tooltip-i18n') || 'on');
|
||||
sValue = ko.unwrap(fValueAccessor());
|
||||
sValue = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue);
|
||||
|
||||
if (sValue)
|
||||
{
|
||||
|
|
@ -165,7 +186,8 @@
|
|||
|
||||
var
|
||||
$oEl = $(oElement),
|
||||
sValue = ko.unwrap(fValueAccessor()),
|
||||
fValue = fValueAccessor(),
|
||||
sValue = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue),
|
||||
oOpenTips = oElement.__opentip
|
||||
;
|
||||
|
||||
|
|
@ -295,6 +317,18 @@
|
|||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.keypress = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
|
||||
$(oElement).on('keypress.koKeyPress', function (oEvent) {
|
||||
fValueAccessor().call(oViewModel, oEvent);
|
||||
});
|
||||
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () {
|
||||
$(oElement).off('keypress.koKeyPress');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.onEnter = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
|
||||
$(oElement).on('keypress.koOnEnter', function (oEvent) {
|
||||
|
|
@ -311,6 +345,21 @@
|
|||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.onTab = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
|
||||
$(oElement).on('keydown.koOnTab', function (oEvent) {
|
||||
if (oEvent && 9 === window.parseInt(oEvent.keyCode, 10))
|
||||
{
|
||||
return fValueAccessor().call(oViewModel, !!oEvent.shiftKey);
|
||||
}
|
||||
});
|
||||
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () {
|
||||
$(oElement).off('keydown.koOnTab');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.onEsc = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
|
||||
$(oElement).on('keypress.koOnEsc', function (oEvent) {
|
||||
|
|
|
|||
|
|
@ -170,8 +170,6 @@
|
|||
Globals.popupVisibilityNames.push(this.viewModelName);
|
||||
oViewModel.viewModelDom.css('z-index', 3000 + Globals.popupVisibilityNames().length + 10);
|
||||
|
||||
// Utils.delegateRun(this, 'onShow'); // moved to showScreenPopup function (for parameters)
|
||||
|
||||
if (this.onShowTrigger)
|
||||
{
|
||||
this.onShowTrigger(!this.onShowTrigger());
|
||||
|
|
@ -257,6 +255,8 @@
|
|||
|
||||
if (ViewModelClassToShow.__vm && ViewModelClassToShow.__dom)
|
||||
{
|
||||
Utils.delegateRun(ViewModelClassToShow.__vm, 'onBeforeShow', aParameters || []);
|
||||
|
||||
ViewModelClassToShow.__vm.modalVisibility(true);
|
||||
|
||||
Utils.delegateRun(ViewModelClassToShow.__vm, 'onShow', aParameters || []);
|
||||
|
|
@ -383,6 +383,8 @@
|
|||
if (ViewModelClass.__vm && ViewModelClass.__dom &&
|
||||
'Popups' !== ViewModelClass.__vm.viewModelPosition())
|
||||
{
|
||||
Utils.delegateRun(ViewModelClass.__vm, 'onBeforeShow');
|
||||
|
||||
ViewModelClass.__dom.show();
|
||||
ViewModelClass.__vm.viewModelVisibility(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
_ = require('_'),
|
||||
ko = require('ko'),
|
||||
|
||||
Enums = require('Common/Enums'),
|
||||
Globals = require('Common/Globals'),
|
||||
Utils = require('Common/Utils'),
|
||||
Links = require('Common/Links'),
|
||||
|
|
@ -27,6 +28,8 @@
|
|||
|
||||
this.mimeType = '';
|
||||
this.fileName = '';
|
||||
this.fileNameExt = '';
|
||||
this.fileType = Enums.FileType.Unknown;
|
||||
this.estimatedSize = 0;
|
||||
this.friendlySize = '';
|
||||
this.isInline = false;
|
||||
|
|
@ -57,6 +60,8 @@
|
|||
|
||||
AttachmentModel.prototype.mimeType = '';
|
||||
AttachmentModel.prototype.fileName = '';
|
||||
AttachmentModel.prototype.fileType = '';
|
||||
AttachmentModel.prototype.fileNameExt = '';
|
||||
AttachmentModel.prototype.estimatedSize = 0;
|
||||
AttachmentModel.prototype.friendlySize = '';
|
||||
AttachmentModel.prototype.isInline = false;
|
||||
|
|
@ -97,6 +102,9 @@
|
|||
this.friendlySize = Utils.friendlySize(this.estimatedSize);
|
||||
this.cidWithOutTags = this.cid.replace(/^<+/, '').replace(/>+$/, '');
|
||||
|
||||
this.fileNameExt = Utils.getFileExtension(this.fileName);
|
||||
this.fileType = AttachmentModel.staticFileType(this.fileNameExt, this.mimeType);
|
||||
|
||||
bResult = true;
|
||||
}
|
||||
|
||||
|
|
@ -108,9 +116,7 @@
|
|||
*/
|
||||
AttachmentModel.prototype.isImage = function ()
|
||||
{
|
||||
return -1 < Utils.inArray(this.mimeType.toLowerCase(),
|
||||
['image/png', 'image/jpg', 'image/jpeg', 'image/gif']
|
||||
);
|
||||
return Enums.FileType.Image === this.fileType;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -118,7 +124,26 @@
|
|||
*/
|
||||
AttachmentModel.prototype.isMp3 = function ()
|
||||
{
|
||||
return Audio.supported && '.mp3' === this.fileName.toLowerCase().substr(-4);
|
||||
return Enums.FileType.Audio === this.fileType &&
|
||||
'mp3' === this.fileNameExt;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
AttachmentModel.prototype.isOgg = function ()
|
||||
{
|
||||
return Enums.FileType.Audio === this.fileType &&
|
||||
('oga' === this.fileNameExt || 'ogg' === this.fileNameExt);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
AttachmentModel.prototype.isWav = function ()
|
||||
{
|
||||
return Enums.FileType.Audio === this.fileType &&
|
||||
'wav' === this.fileNameExt;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -134,8 +159,12 @@
|
|||
*/
|
||||
AttachmentModel.prototype.isText = function ()
|
||||
{
|
||||
return -1 < Utils.inArray(this.mimeType, ['application/pgp-signature', 'message/delivery-status', 'message/rfc822']) ||
|
||||
('text/' === this.mimeType.substr(0, 5) && -1 === Utils.inArray(this.mimeType, ['text/html']));
|
||||
return Enums.FileType.Text === this.fileType ||
|
||||
Enums.FileType.Eml === this.fileType ||
|
||||
Enums.FileType.Certificate === this.fileType ||
|
||||
Enums.FileType.Html === this.fileType ||
|
||||
Enums.FileType.Code === this.fileType
|
||||
;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -143,7 +172,7 @@
|
|||
*/
|
||||
AttachmentModel.prototype.isPdf = function ()
|
||||
{
|
||||
return Globals.bAllowPdfPreview && 'application/pdf' === this.mimeType;
|
||||
return Enums.FileType.Pdf === this.fileType;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -152,7 +181,7 @@
|
|||
AttachmentModel.prototype.isFramed = function ()
|
||||
{
|
||||
return this.framed && (Globals.__APP__ && Globals.__APP__.googlePreviewSupported()) &&
|
||||
!this.isPdf() && !this.isText() && !this.isImage();
|
||||
!(this.isPdf() && Globals.bAllowPdfPreview) && !this.isText() && !this.isImage();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -160,7 +189,8 @@
|
|||
*/
|
||||
AttachmentModel.prototype.hasPreview = function ()
|
||||
{
|
||||
return this.isImage() || this.isPdf() || this.isText() || this.isFramed();
|
||||
return this.isImage() || (this.isPdf() && Globals.bAllowPdfPreview) ||
|
||||
this.isText() || this.isFramed();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -168,7 +198,10 @@
|
|||
*/
|
||||
AttachmentModel.prototype.hasPreplay = function ()
|
||||
{
|
||||
return this.isMp3();
|
||||
return (Audio.supportedMp3 && this.isMp3()) ||
|
||||
(Audio.supportedOgg && this.isOgg()) ||
|
||||
(Audio.supportedWav && this.isWav())
|
||||
;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -229,7 +262,7 @@
|
|||
switch (true)
|
||||
{
|
||||
case this.isImage():
|
||||
case this.isPdf():
|
||||
case this.isPdf() && Globals.bAllowPdfPreview:
|
||||
sResult = this.linkPreview();
|
||||
break;
|
||||
case this.isText():
|
||||
|
|
@ -274,76 +307,90 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @param {string} sExt
|
||||
* @param {string} sMimeType
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
AttachmentModel.staticIconClassHelper = function (sMimeType)
|
||||
AttachmentModel.staticFileType = _.memoize(function (sExt, sMimeType)
|
||||
{
|
||||
sExt = Utils.trim(sExt).toLowerCase();
|
||||
sMimeType = Utils.trim(sMimeType).toLowerCase();
|
||||
|
||||
var
|
||||
sText = '',
|
||||
sClass = 'icon-file',
|
||||
aParts = sMimeType.split('/')
|
||||
sResult = Enums.FileType.Unknown,
|
||||
aMimeTypeParts = sMimeType.split('/')
|
||||
;
|
||||
|
||||
if (aParts && aParts[1])
|
||||
switch (true)
|
||||
{
|
||||
if ('image' === aParts[0])
|
||||
{
|
||||
sClass = 'icon-file-image';
|
||||
}
|
||||
else if ('text' === aParts[0])
|
||||
{
|
||||
sClass = 'icon-file-text';
|
||||
}
|
||||
else if ('audio' === aParts[0])
|
||||
{
|
||||
sClass = 'icon-file-music';
|
||||
}
|
||||
else if ('video' === aParts[0])
|
||||
{
|
||||
sClass = 'icon-file-movie';
|
||||
}
|
||||
else if (-1 < Utils.inArray(aParts[1],
|
||||
['zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2', 'x-zip', 'x-7z', 'x-rar', 'x-tar', 'x-gzip', 'x-bzip', 'x-bzip2', 'x-zip-compressed', 'x-7z-compressed', 'x-rar-compressed']))
|
||||
{
|
||||
sClass = 'icon-file-zip';
|
||||
}
|
||||
else if (-1 < Utils.inArray(aParts[1],
|
||||
['pdf', 'x-pdf']))
|
||||
{
|
||||
sText = 'pdf';
|
||||
sClass = 'icon-none';
|
||||
}
|
||||
// else if (-1 < Utils.inArray(aParts[1], [
|
||||
// 'exe', 'x-exe', 'x-winexe', 'bat'
|
||||
// ]))
|
||||
// {
|
||||
// sClass = 'icon-console';
|
||||
// }
|
||||
else if (-1 < Utils.inArray(sMimeType, [
|
||||
'application/pgp-signature', 'application/pkcs7-signature'
|
||||
]))
|
||||
{
|
||||
sClass = 'icon-file-certificate';
|
||||
}
|
||||
else if (-1 < Utils.inArray(sMimeType, [
|
||||
case 'image' === aMimeTypeParts[0] || -1 < Utils.inArray(sExt, [
|
||||
'png', 'jpg', 'jpeg', 'gif', 'bmp'
|
||||
]):
|
||||
sResult = Enums.FileType.Image;
|
||||
break;
|
||||
case 'audio' === aMimeTypeParts[0] || -1 < Utils.inArray(sExt, [
|
||||
'mp3', 'ogg', 'oga', 'wav'
|
||||
]):
|
||||
sResult = Enums.FileType.Audio;
|
||||
break;
|
||||
case 'video' === aMimeTypeParts[0] || -1 < Utils.inArray(sExt, [
|
||||
'mkv', 'avi'
|
||||
]):
|
||||
sResult = Enums.FileType.Video;
|
||||
break;
|
||||
case -1 < Utils.inArray(sExt, [
|
||||
'php', 'js', 'css'
|
||||
]):
|
||||
sResult = Enums.FileType.Code;
|
||||
break;
|
||||
case 'eml' === sExt || -1 < Utils.inArray(sMimeType, [
|
||||
'message/delivery-status', 'message/rfc822'
|
||||
]))
|
||||
{
|
||||
sClass = 'icon-file-text';
|
||||
}
|
||||
else if (-1 < Utils.inArray(aParts[1], [
|
||||
]):
|
||||
sResult = Enums.FileType.Eml;
|
||||
break;
|
||||
case ('text' === aMimeTypeParts[0] && 'html' !== aMimeTypeParts[1]) || -1 < Utils.inArray(sExt, [
|
||||
'txt', 'log'
|
||||
]):
|
||||
sResult = Enums.FileType.Text;
|
||||
break;
|
||||
case ('text/html' === sMimeType) || -1 < Utils.inArray(sExt, [
|
||||
'html'
|
||||
]):
|
||||
sResult = Enums.FileType.Html;
|
||||
break;
|
||||
case -1 < Utils.inArray(aMimeTypeParts[1], [
|
||||
'zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2', 'x-zip', 'x-7z', 'x-rar', 'x-tar', 'x-gzip', 'x-bzip', 'x-bzip2', 'x-zip-compressed', 'x-7z-compressed', 'x-rar-compressed'
|
||||
]) || -1 < Utils.inArray(sExt, [
|
||||
'zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2'
|
||||
]):
|
||||
sResult = Enums.FileType.Archive;
|
||||
break;
|
||||
case -1 < Utils.inArray(aMimeTypeParts[1], ['pdf', 'x-pdf']) || -1 < Utils.inArray(sExt, [
|
||||
'pdf'
|
||||
]):
|
||||
sResult = Enums.FileType.Pdf;
|
||||
break;
|
||||
case -1 < Utils.inArray(sMimeType, [
|
||||
'application/pgp-signature', 'application/pgp-keys'
|
||||
]) || -1 < Utils.inArray(sExt, [
|
||||
'asc', 'pem', 'ppk'
|
||||
]):
|
||||
sResult = Enums.FileType.Certificate;
|
||||
break;
|
||||
case -1 < Utils.inArray(sMimeType, ['application/pkcs7-signature']) ||
|
||||
-1 < Utils.inArray(sExt, ['p7s']):
|
||||
|
||||
sResult = Enums.FileType.CertificateBin;
|
||||
break;
|
||||
case -1 < Utils.inArray(aMimeTypeParts[1], [
|
||||
'rtf', 'msword', 'vnd.msword', 'vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'vnd.ms-word.document.macroEnabled.12',
|
||||
'vnd.ms-word.template.macroEnabled.12'
|
||||
]))
|
||||
{
|
||||
sClass = 'icon-file-text';
|
||||
}
|
||||
else if (-1 < Utils.inArray(aParts[1], [
|
||||
]):
|
||||
sResult = Enums.FileType.WordText;
|
||||
break;
|
||||
case -1 < Utils.inArray(aMimeTypeParts[1], [
|
||||
'excel', 'ms-excel', 'vnd.ms-excel',
|
||||
'vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
|
|
@ -351,11 +398,10 @@
|
|||
'vnd.ms-excel.template.macroEnabled.12',
|
||||
'vnd.ms-excel.addin.macroEnabled.12',
|
||||
'vnd.ms-excel.sheet.binary.macroEnabled.12'
|
||||
]))
|
||||
{
|
||||
sClass = 'icon-file-excel';
|
||||
}
|
||||
else if (-1 < Utils.inArray(aParts[1], [
|
||||
]):
|
||||
sResult = Enums.FileType.Sheet;
|
||||
break;
|
||||
case -1 < Utils.inArray(aMimeTypeParts[1], [
|
||||
'powerpoint', 'ms-powerpoint', 'vnd.ms-powerpoint',
|
||||
'vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'vnd.openxmlformats-officedocument.presentationml.template',
|
||||
|
|
@ -364,29 +410,142 @@
|
|||
'vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'vnd.ms-powerpoint.template.macroEnabled.12',
|
||||
'vnd.ms-powerpoint.slideshow.macroEnabled.12'
|
||||
]))
|
||||
{
|
||||
]):
|
||||
sResult = Enums.FileType.Presentation;
|
||||
break;
|
||||
}
|
||||
|
||||
return sResult;
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} sFileType
|
||||
* @return {string}
|
||||
*/
|
||||
AttachmentModel.staticIconClass = _.memoize(function (sFileType)
|
||||
{
|
||||
var
|
||||
sText = '',
|
||||
sClass = 'icon-file'
|
||||
;
|
||||
|
||||
switch (sFileType)
|
||||
{
|
||||
case Enums.FileType.Text:
|
||||
case Enums.FileType.Eml:
|
||||
case Enums.FileType.WordText:
|
||||
sClass = 'icon-file-text';
|
||||
break;
|
||||
case Enums.FileType.Html:
|
||||
case Enums.FileType.Code:
|
||||
sClass = 'icon-file-code';
|
||||
break;
|
||||
case Enums.FileType.Image:
|
||||
sClass = 'icon-file-image';
|
||||
break;
|
||||
case Enums.FileType.Audio:
|
||||
sClass = 'icon-file-music';
|
||||
break;
|
||||
case Enums.FileType.Video:
|
||||
sClass = 'icon-file-movie';
|
||||
break;
|
||||
case Enums.FileType.Archive:
|
||||
sClass = 'icon-file-zip';
|
||||
break;
|
||||
case Enums.FileType.Certificate:
|
||||
case Enums.FileType.CertificateBin:
|
||||
sClass = 'icon-file-certificate';
|
||||
break;
|
||||
case Enums.FileType.Sheet:
|
||||
sClass = 'icon-file-excel';
|
||||
break;
|
||||
case Enums.FileType.Presentation:
|
||||
sClass = 'icon-file-chart-graph';
|
||||
}
|
||||
break;
|
||||
case Enums.FileType.Pdf:
|
||||
sText = 'pdf';
|
||||
sClass = 'icon-none';
|
||||
break;
|
||||
}
|
||||
|
||||
return [sClass, sText];
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} sFileType
|
||||
* @return {string}
|
||||
*/
|
||||
AttachmentModel.staticCombinedIconClass = function (aData)
|
||||
{
|
||||
var
|
||||
sClass = '',
|
||||
aTypes = []
|
||||
;
|
||||
|
||||
if (Utils.isNonEmptyArray(aData))
|
||||
{
|
||||
sClass = 'icon-attachment';
|
||||
|
||||
aTypes = _.uniq(_.compact(_.map(aData, function (aItem) {
|
||||
return aItem ? AttachmentModel.staticFileType(
|
||||
Utils.getFileExtension(aItem[0]), aItem[1]) : '';
|
||||
})));
|
||||
|
||||
if (aTypes && 1 === aTypes.length && aTypes[0])
|
||||
{
|
||||
switch (aTypes[0])
|
||||
{
|
||||
case Enums.FileType.Text:
|
||||
case Enums.FileType.WordText:
|
||||
sClass = 'icon-file-text';
|
||||
break;
|
||||
case Enums.FileType.Html:
|
||||
case Enums.FileType.Code:
|
||||
sClass = 'icon-file-code';
|
||||
break;
|
||||
case Enums.FileType.Image:
|
||||
sClass = 'icon-file-image';
|
||||
break;
|
||||
case Enums.FileType.Audio:
|
||||
sClass = 'icon-file-music';
|
||||
break;
|
||||
case Enums.FileType.Video:
|
||||
sClass = 'icon-file-movie';
|
||||
break;
|
||||
case Enums.FileType.Archive:
|
||||
sClass = 'icon-file-zip';
|
||||
break;
|
||||
case Enums.FileType.Certificate:
|
||||
case Enums.FileType.CertificateBin:
|
||||
sClass = 'icon-file-certificate';
|
||||
break;
|
||||
case Enums.FileType.Sheet:
|
||||
sClass = 'icon-file-excel';
|
||||
break;
|
||||
case Enums.FileType.Presentation:
|
||||
sClass = 'icon-file-chart-graph';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sClass;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
AttachmentModel.prototype.iconClass = function ()
|
||||
{
|
||||
return AttachmentModel.staticIconClassHelper(this.mimeType)[0];
|
||||
return AttachmentModel.staticIconClass(this.fileType)[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
AttachmentModel.prototype.iconText = function ()
|
||||
{
|
||||
return AttachmentModel.staticIconClassHelper(this.mimeType)[1];
|
||||
return AttachmentModel.staticIconClass(this.fileType)[1];
|
||||
};
|
||||
|
||||
module.exports = AttachmentModel;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,11 @@
|
|||
return Utils.mimeContentType(this.fileName());
|
||||
}, this);
|
||||
|
||||
this.regDisposables([this.friendlySize]);
|
||||
this.fileExt = ko.computed(function () {
|
||||
return Utils.getFileExtension(this.fileName());
|
||||
}, this);
|
||||
|
||||
this.regDisposables([this.progressText, this.progressStyle, this.title, this.friendlySize, this.mimeType, this.fileExt]);
|
||||
}
|
||||
|
||||
_.extend(ComposeAttachmentModel.prototype, AbstractModel.prototype);
|
||||
|
|
@ -108,7 +112,8 @@
|
|||
*/
|
||||
ComposeAttachmentModel.prototype.iconClass = function ()
|
||||
{
|
||||
return AttachmentModel.staticIconClassHelper(this.mimeType())[0];
|
||||
return AttachmentModel.staticIconClass(
|
||||
AttachmentModel.staticFileType(this.fileExt(), this.mimeType()))[0];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -116,7 +121,8 @@
|
|||
*/
|
||||
ComposeAttachmentModel.prototype.iconText = function ()
|
||||
{
|
||||
return AttachmentModel.staticIconClassHelper(this.mimeType())[1];
|
||||
return AttachmentModel.staticIconClass(
|
||||
AttachmentModel.staticFileType(this.fileExt(), this.mimeType()))[1];
|
||||
};
|
||||
|
||||
module.exports = ComposeAttachmentModel;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
EmailModel.prototype.validate = function ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
this.edited = ko.observable(false);
|
||||
this.collapsed = ko.observable(true);
|
||||
this.subScribed = ko.observable(true);
|
||||
this.checkable = ko.observable(false);
|
||||
this.subFolders = ko.observableArray([]);
|
||||
this.deleteAccess = ko.observable(false);
|
||||
this.actionBlink = ko.observable(false).extend({'falseTimeout': 1000});
|
||||
|
|
@ -105,6 +106,7 @@
|
|||
}, this);
|
||||
|
||||
this.hidden = ko.computed(function () {
|
||||
|
||||
var
|
||||
bSystem = this.isSystemFolder(),
|
||||
bSubFolders = this.hasSubScribedSubfolders()
|
||||
|
|
@ -182,6 +184,8 @@
|
|||
return !this.isSystemFolder() && this.selectable && sInboxFolderName !== this.fullNameRaw;
|
||||
}, this);
|
||||
|
||||
this.canBeChecked = this.canBeSubScribed;
|
||||
|
||||
// this.visible.subscribe(function () {
|
||||
// Utils.timeOutAction('folder-list-folder-visibility-change', function () {
|
||||
// Globals.$win.trigger('folder-list-folder-visibility-change');
|
||||
|
|
@ -281,7 +285,8 @@
|
|||
});
|
||||
|
||||
this.hasUnreadMessages = ko.computed(function () {
|
||||
return 0 < this.messageCountUnread();
|
||||
return 0 < this.messageCountUnread() &&
|
||||
(this.isSystemFolder() || this.checkable());
|
||||
}, this);
|
||||
|
||||
this.hasSubScribedUnreadMessagesSubfolders = ko.computed(function () {
|
||||
|
|
@ -352,6 +357,8 @@
|
|||
this.existen = !!oJsonFolder.IsExists;
|
||||
|
||||
this.subScribed(!!oJsonFolder.IsSubscribed);
|
||||
this.checkable(!!oJsonFolder.Checkable);
|
||||
|
||||
this.type(sInboxFolderName === this.fullNameRaw ? Enums.FolderType.Inbox : Enums.FolderType.User);
|
||||
|
||||
bResult = true;
|
||||
|
|
|
|||
|
|
@ -74,30 +74,11 @@
|
|||
this.selected = ko.observable(false);
|
||||
this.checked = ko.observable(false);
|
||||
this.hasAttachments = ko.observable(false);
|
||||
this.attachmentsMainType = ko.observable('');
|
||||
this.attachmentsSpecData = ko.observableArray([]);
|
||||
|
||||
this.attachmentIconClass = ko.computed(function () {
|
||||
var sClass = '';
|
||||
if (this.hasAttachments())
|
||||
{
|
||||
sClass = 'icon-attachment';
|
||||
switch (this.attachmentsMainType())
|
||||
{
|
||||
case 'image':
|
||||
sClass = 'icon-image';
|
||||
break;
|
||||
case 'archive':
|
||||
sClass = 'icon-file-zip';
|
||||
break;
|
||||
case 'doc':
|
||||
sClass = 'icon-file-text';
|
||||
break;
|
||||
case 'certificate':
|
||||
sClass = 'icon-file-certificate';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sClass;
|
||||
return AttachmentModel.staticCombinedIconClass(
|
||||
this.hasAttachments() ? this.attachmentsSpecData() : []);
|
||||
}, this);
|
||||
|
||||
this.body = null;
|
||||
|
|
@ -193,7 +174,7 @@
|
|||
this.selected(false);
|
||||
this.checked(false);
|
||||
this.hasAttachments(false);
|
||||
this.attachmentsMainType('');
|
||||
this.attachmentsSpecData([]);
|
||||
|
||||
this.body = null;
|
||||
this.isHtml(false);
|
||||
|
|
@ -287,7 +268,8 @@
|
|||
|
||||
this.dateTimeStampInUTC(Utils.pInt(oJsonMessage.DateTimeStampInUTC));
|
||||
this.hasAttachments(!!oJsonMessage.HasAttachments);
|
||||
this.attachmentsMainType(oJsonMessage.AttachmentsMainType);
|
||||
this.attachmentsSpecData(Utils.isArray(oJsonMessage.AttachmentsSpecData) ?
|
||||
oJsonMessage.AttachmentsSpecData : []);
|
||||
|
||||
this.fromEmailString(MessageHelper.emailArrayToString(this.from, true));
|
||||
this.fromClearEmailString(MessageHelper.emailArrayToStringClear(this.from));
|
||||
|
|
@ -337,7 +319,8 @@
|
|||
}
|
||||
|
||||
this.hasAttachments(!!oJsonMessage.HasAttachments);
|
||||
this.attachmentsMainType(oJsonMessage.AttachmentsMainType);
|
||||
this.attachmentsSpecData(Utils.isArray(oJsonMessage.AttachmentsSpecData) ?
|
||||
oJsonMessage.AttachmentsSpecData : []);
|
||||
|
||||
this.foundedCIDs = Utils.isArray(oJsonMessage.FoundedCIDs) ? oJsonMessage.FoundedCIDs : [];
|
||||
this.attachments(this.initAttachmentsFromJson(oJsonMessage.Attachments));
|
||||
|
|
@ -524,15 +507,6 @@
|
|||
if (this.hasAttachments())
|
||||
{
|
||||
aResult.push('withAttachments');
|
||||
switch (this.attachmentsMainType())
|
||||
{
|
||||
case 'image':
|
||||
aResult.push('imageOnlyAttachments');
|
||||
break;
|
||||
case 'archive':
|
||||
aResult.push('archiveOnlyAttachments');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.newForAnimation())
|
||||
{
|
||||
|
|
@ -797,7 +771,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
MessageModel.prototype.generateUid = function ()
|
||||
{
|
||||
|
|
@ -850,7 +824,7 @@
|
|||
this.selected(oMessage.selected());
|
||||
this.checked(oMessage.checked());
|
||||
this.hasAttachments(oMessage.hasAttachments());
|
||||
this.attachmentsMainType(oMessage.attachmentsMainType());
|
||||
this.attachmentsSpecData(oMessage.attachmentsSpecData());
|
||||
|
||||
this.body = null;
|
||||
|
||||
|
|
@ -1004,7 +978,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
MessageModel.prototype.storeDataToDom = function ()
|
||||
MessageModel.prototype.storeDataInDom = function ()
|
||||
{
|
||||
if (this.body)
|
||||
{
|
||||
|
|
@ -1023,7 +997,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
MessageModel.prototype.storePgpVerifyDataToDom = function ()
|
||||
MessageModel.prototype.storePgpVerifyDataInDom = function ()
|
||||
{
|
||||
if (this.body && require('Stores/User/Pgp').capaOpenPGP())
|
||||
{
|
||||
|
|
@ -1032,7 +1006,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
MessageModel.prototype.fetchDataToDom = function ()
|
||||
MessageModel.prototype.fetchDataFromDom = function ()
|
||||
{
|
||||
if (this.body)
|
||||
{
|
||||
|
|
@ -1116,7 +1090,7 @@
|
|||
}
|
||||
catch (oExc) {}
|
||||
|
||||
this.storePgpVerifyDataToDom();
|
||||
this.storePgpVerifyDataInDom();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1188,7 +1162,7 @@
|
|||
}
|
||||
catch (oExc) {}
|
||||
|
||||
this.storePgpVerifyDataToDom();
|
||||
this.storePgpVerifyDataInDom();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@
|
|||
MessageFullModel.prototype.requestHash = '';
|
||||
MessageFullModel.prototype.proxy = false;
|
||||
MessageFullModel.prototype.hasAttachments = false;
|
||||
MessageFullModel.prototype.attachmentsMainType = '';
|
||||
MessageFullModel.prototype.attachmentsClass = '';
|
||||
|
||||
MessageFullModel.prototype.clear = function ()
|
||||
{
|
||||
|
|
@ -43,37 +41,6 @@
|
|||
this.proxy = false;
|
||||
|
||||
this.hasAttachments = false;
|
||||
this.attachmentsMainType = '';
|
||||
this.attachmentsClass = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
MessageFullModel.prototype.getAttachmentsClass = function ()
|
||||
{
|
||||
var sClass = '';
|
||||
if (this.hasAttachments)
|
||||
{
|
||||
sClass = 'icon-attachment';
|
||||
switch (this.attachmentsMainType)
|
||||
{
|
||||
case 'image':
|
||||
sClass = 'icon-image';
|
||||
break;
|
||||
case 'archive':
|
||||
sClass = 'icon-file-zip';
|
||||
break;
|
||||
case 'doc':
|
||||
sClass = 'icon-file-text';
|
||||
break;
|
||||
case 'certificate':
|
||||
sClass = 'icon-file-certificate';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sClass;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -98,8 +65,6 @@
|
|||
this.proxy = !!oJson.ExternalProxy;
|
||||
|
||||
this.hasAttachments = !!oJson.HasAttachments;
|
||||
this.attachmentsMainType = Utils.pString(oJson.AttachmentsMainType);
|
||||
this.attachmentsClass = this.getAttachmentsClass();
|
||||
|
||||
bResult = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
var
|
||||
_ = require('_'),
|
||||
ko = require('ko'),
|
||||
|
||||
Utils = require('Common/Utils'),
|
||||
|
||||
|
|
@ -20,11 +21,18 @@
|
|||
function MessageSimpleModel(sSuperName)
|
||||
{
|
||||
AbstractModel.call(this, sSuperName || 'MessageSimpleModel');
|
||||
|
||||
this.flagged = ko.observable(false);
|
||||
this.selected = ko.observable(false);
|
||||
}
|
||||
|
||||
_.extend(MessageSimpleModel.prototype, AbstractModel.prototype);
|
||||
|
||||
MessageSimpleModel.prototype.__simple_message__ = true;
|
||||
|
||||
MessageSimpleModel.prototype.folder = '';
|
||||
MessageSimpleModel.prototype.folderFullNameRaw = '';
|
||||
|
||||
MessageSimpleModel.prototype.uid = '';
|
||||
MessageSimpleModel.prototype.subject = '';
|
||||
|
||||
|
|
@ -48,6 +56,8 @@
|
|||
MessageSimpleModel.prototype.clear = function ()
|
||||
{
|
||||
this.folder = '';
|
||||
this.folderFullNameRaw = '';
|
||||
|
||||
this.uid = '';
|
||||
|
||||
this.subject = '';
|
||||
|
|
@ -68,6 +78,9 @@
|
|||
|
||||
this.size = 0;
|
||||
this.timestamp = 0;
|
||||
|
||||
this.flagged(false);
|
||||
this.selected(false);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -81,6 +94,8 @@
|
|||
if (oJson && 'Object/Message' === oJson['@Object'])
|
||||
{
|
||||
this.folder = Utils.pString(oJson.Folder);
|
||||
this.folderFullNameRaw = this.folder;
|
||||
|
||||
this.uid = Utils.pString(oJson.Uid);
|
||||
|
||||
this.subject = Utils.pString(oJson.Subject);
|
||||
|
|
@ -112,6 +127,9 @@
|
|||
this.toAsString = MessageHelper.emailArrayToString(this.to, true);
|
||||
this.toAsStringClear = MessageHelper.emailArrayToStringClear(this.to);
|
||||
|
||||
this.flagged(false);
|
||||
this.selected(false);
|
||||
|
||||
this.populateSenderEmail();
|
||||
|
||||
bResult = true;
|
||||
|
|
@ -132,6 +150,14 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Array}
|
||||
*/
|
||||
MessageSimpleModel.prototype.threads = function ()
|
||||
{
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @param {Object} oJson
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
// AppStore = require('Stores/User/App'),
|
||||
// SettingsStore = require('Stores/User/Settings'),
|
||||
|
||||
MessageSimpleModel = require('Model/MessageSimple'),
|
||||
|
||||
PromisesPopulator = require('Promises/User/Populator'),
|
||||
AbstractAjaxPromises = require('Promises/AbstractAjax')
|
||||
;
|
||||
|
|
@ -29,44 +27,10 @@
|
|||
function UserAjaxUserPromises()
|
||||
{
|
||||
AbstractAjaxPromises.call(this);
|
||||
|
||||
this.messageListSimpleHash = '';
|
||||
this.messageListSimpleCache = null;
|
||||
}
|
||||
|
||||
_.extend(UserAjaxUserPromises.prototype, AbstractAjaxPromises.prototype);
|
||||
|
||||
UserAjaxUserPromises.prototype.messageListSimple = function (sFolder, aUids, fTrigger)
|
||||
{
|
||||
var self = this, sHash = sFolder + '~' + aUids.join('/');
|
||||
if (sHash === this.messageListSimpleHash && this.messageListSimpleCache)
|
||||
{
|
||||
return this.fastResolve(this.messageListSimpleCache);
|
||||
}
|
||||
|
||||
return this.abort('MessageListSimple')
|
||||
.postRequest('MessageListSimple', fTrigger, {
|
||||
'Folder': sFolder,
|
||||
'Uids': aUids
|
||||
}).then(function (oData) {
|
||||
|
||||
self.messageListSimpleHash = sHash;
|
||||
self.messageListSimpleCache = _.compact(_.map(oData.Result, function (aItem) {
|
||||
return MessageSimpleModel.newInstanceFromJson(aItem);
|
||||
}));
|
||||
|
||||
return self.messageListSimpleCache;
|
||||
|
||||
}, function (iError) {
|
||||
|
||||
self.messageListSimpleHash = '';
|
||||
self.messageListSimpleCache = null;
|
||||
|
||||
return self.fastReject(iError);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
UserAjaxUserPromises.prototype.foldersReload = function (fTrigger)
|
||||
{
|
||||
return this.abort('Folders')
|
||||
|
|
@ -112,6 +76,14 @@
|
|||
});
|
||||
};
|
||||
|
||||
UserAjaxUserPromises.prototype.attachmentsActions = function (sAction, aHashes, fTrigger)
|
||||
{
|
||||
return this.postRequest('AttachmentsActions', fTrigger, {
|
||||
'Do': sAction,
|
||||
'Hashes': aHashes
|
||||
});
|
||||
};
|
||||
|
||||
UserAjaxUserPromises.prototype.welcomeClose = function ()
|
||||
{
|
||||
return this.postRequest('WelcomeClose');
|
||||
|
|
|
|||
|
|
@ -313,39 +313,30 @@
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sFolderFullNameRaw
|
||||
* @param {Array} aUids
|
||||
*/
|
||||
RemoteUserAjax.prototype.messageListSimple = function (fCallback, sFolderFullNameRaw, aUids)
|
||||
{
|
||||
return this.defaultRequest(fCallback, 'MessageListSimple', {
|
||||
'Folder': Utils.pString(sFolderFullNameRaw),
|
||||
'Uids': aUids
|
||||
}, Consts.Defaults.DefaultAjaxTimeout, '', ['MessageListSimple']);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sFolderFullNameRaw
|
||||
* @param {number=} iOffset = 0
|
||||
* @param {number=} iLimit = 20
|
||||
* @param {string=} sSearch = ''
|
||||
* @param {string=} sThreadUid = ''
|
||||
* @param {boolean=} bSilent = false
|
||||
*/
|
||||
RemoteUserAjax.prototype.messageList = function (fCallback, sFolderFullNameRaw, iOffset, iLimit, sSearch, bSilent)
|
||||
RemoteUserAjax.prototype.messageList = function (fCallback, sFolderFullNameRaw, iOffset, iLimit, sSearch, sThreadUid, bSilent)
|
||||
{
|
||||
sFolderFullNameRaw = Utils.pString(sFolderFullNameRaw);
|
||||
|
||||
var
|
||||
sFolderHash = Cache.getFolderHash(sFolderFullNameRaw)
|
||||
sFolderHash = Cache.getFolderHash(sFolderFullNameRaw),
|
||||
bUseThreads = AppStore.threadsAllowed() && SettingsStore.useThreads(),
|
||||
sInboxUidNext = Cache.getFolderInboxName() === sFolderFullNameRaw ? Cache.getFolderUidNext(sFolderFullNameRaw) : ''
|
||||
;
|
||||
|
||||
bSilent = Utils.isUnd(bSilent) ? false : !!bSilent;
|
||||
iOffset = Utils.isUnd(iOffset) ? 0 : Utils.pInt(iOffset);
|
||||
iLimit = Utils.isUnd(iOffset) ? 20 : Utils.pInt(iLimit);
|
||||
sSearch = Utils.pString(sSearch);
|
||||
sThreadUid = Utils.pString(sThreadUid);
|
||||
|
||||
if ('' !== sFolderHash && ('' === sSearch || -1 === sSearch.indexOf('is:')))
|
||||
{
|
||||
|
|
@ -358,9 +349,9 @@
|
|||
sSearch,
|
||||
AppStore.projectHash(),
|
||||
sFolderHash,
|
||||
Cache.getFolderInboxName() === sFolderFullNameRaw ? Cache.getFolderUidNext(sFolderFullNameRaw) : '',
|
||||
AppStore.threadsAllowed() && SettingsStore.useThreads() ? '1' : '0',
|
||||
''
|
||||
sInboxUidNext,
|
||||
bUseThreads ? '1' : '0',
|
||||
bUseThreads ? sThreadUid : ''
|
||||
].join(String.fromCharCode(0))), bSilent ? [] : ['MessageList']);
|
||||
}
|
||||
else
|
||||
|
|
@ -370,9 +361,11 @@
|
|||
'Offset': iOffset,
|
||||
'Limit': iLimit,
|
||||
'Search': sSearch,
|
||||
'UidNext': Cache.getFolderInboxName() === sFolderFullNameRaw ? Cache.getFolderUidNext(sFolderFullNameRaw) : '',
|
||||
'UseThreads': AppStore.threadsAllowed() && SettingsStore.useThreads() ? '1' : '0'
|
||||
}, '' === sSearch ? Consts.Defaults.DefaultAjaxTimeout : Consts.Defaults.SearchAjaxTimeout, '', bSilent ? [] : ['MessageList']);
|
||||
'UidNext': sInboxUidNext,
|
||||
'UseThreads': bUseThreads ? '1' : '0',
|
||||
'ThreadUid': bUseThreads ? sThreadUid : ''
|
||||
}, '' === sSearch ? Consts.Defaults.DefaultAjaxTimeout : Consts.Defaults.SearchAjaxTimeout,
|
||||
'', bSilent ? [] : ['MessageList']);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -414,35 +407,6 @@
|
|||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sFolderFullNameRaw
|
||||
* @param {number} iUid
|
||||
* @return {boolean}
|
||||
*/
|
||||
RemoteUserAjax.prototype.messageThreadsFromCache = function (fCallback, sFolderFullNameRaw, iUid)
|
||||
{
|
||||
sFolderFullNameRaw = Utils.pString(sFolderFullNameRaw);
|
||||
iUid = Utils.pInt(iUid);
|
||||
|
||||
if (Cache.getFolderFromCacheList(sFolderFullNameRaw) && 0 < iUid)
|
||||
{
|
||||
var sFolderHash = Cache.getFolderHash(sFolderFullNameRaw);
|
||||
if (sFolderHash)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'MessageThreadsFromCache', {
|
||||
'Folder': sFolderFullNameRaw,
|
||||
'FolderHash': sFolderHash,
|
||||
'Uid': iUid
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {Array} aExternals
|
||||
|
|
@ -744,6 +708,19 @@
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sFolderFullNameRaw
|
||||
* @param {boolean} bCheckable
|
||||
*/
|
||||
RemoteUserAjax.prototype.folderSetCheckable = function (fCallback, sFolderFullNameRaw, bCheckable)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'FolderCheckable', {
|
||||
'Folder': sFolderFullNameRaw,
|
||||
'Checkable': bCheckable ? '1' : '0'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sFolder
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@
|
|||
// show
|
||||
if (self.oCurrentSubScreen)
|
||||
{
|
||||
Utils.delegateRun(self.oCurrentSubScreen, 'onBeforeShow');
|
||||
self.oCurrentSubScreen.viewModelDom.show();
|
||||
Utils.delegateRun(self.oCurrentSubScreen, 'onShow');
|
||||
Utils.delegateRun(self.oCurrentSubScreen, 'onShowWithDelay', [], 200);
|
||||
|
|
|
|||
|
|
@ -75,15 +75,23 @@
|
|||
MailBoxUserScreen.prototype.onRoute = function (sFolderHash, iPage, sSearch)
|
||||
{
|
||||
var
|
||||
sFolderFullNameRaw = Cache.getFolderFullNameRaw(sFolderHash),
|
||||
oFolder = Cache.getFolderFromCacheList(sFolderFullNameRaw)
|
||||
sThreadUid = sFolderHash.replace(/^(.+)~([\d]+)$/, '$2'),
|
||||
oFolder = Cache.getFolderFromCacheList(Cache.getFolderFullNameRaw(
|
||||
sFolderHash.replace(/~([\d]+)$/, '')))
|
||||
;
|
||||
|
||||
if (oFolder)
|
||||
{
|
||||
if (sFolderHash === sThreadUid)
|
||||
{
|
||||
sThreadUid = '';
|
||||
}
|
||||
|
||||
FolderStore.currentFolder(oFolder);
|
||||
|
||||
MessageStore.messageListPage(iPage);
|
||||
MessageStore.messageListSearch(sSearch);
|
||||
MessageStore.messageListThreadUid(sThreadUid);
|
||||
|
||||
require('App/User').reloadMessageList();
|
||||
}
|
||||
|
|
@ -163,9 +171,9 @@
|
|||
;
|
||||
|
||||
return [
|
||||
[/^([a-zA-Z0-9]+)\/p([1-9][0-9]*)\/(.+)\/?$/, {'normalize_': fNormS}],
|
||||
[/^([a-zA-Z0-9]+)\/p([1-9][0-9]*)$/, {'normalize_': fNormS}],
|
||||
[/^([a-zA-Z0-9]+)\/(.+)\/?$/, {'normalize_': fNormD}],
|
||||
[/^([a-zA-Z0-9~]+)\/p([1-9][0-9]*)\/(.+)\/?$/, {'normalize_': fNormS}],
|
||||
[/^([a-zA-Z0-9~]+)\/p([1-9][0-9]*)$/, {'normalize_': fNormS}],
|
||||
[/^([a-zA-Z0-9~]+)\/(.+)\/?$/, {'normalize_': fNormD}],
|
||||
[/^([^\/]*)$/, {'normalize_': fNormS}]
|
||||
];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -144,29 +144,27 @@
|
|||
|
||||
}, 50);
|
||||
}
|
||||
else
|
||||
{
|
||||
_.delay(function () {
|
||||
|
||||
var
|
||||
f1 = Utils.settingsSaveHelperSimpleFunction(self.title.trigger, self),
|
||||
f2 = Utils.settingsSaveHelperSimpleFunction(self.loadingDesc.trigger, self)
|
||||
;
|
||||
_.delay(function () {
|
||||
|
||||
self.title.subscribe(function (sValue) {
|
||||
Remote.saveAdminConfig(f1, {
|
||||
'Title': Utils.trim(sValue)
|
||||
});
|
||||
var
|
||||
f1 = Utils.settingsSaveHelperSimpleFunction(self.title.trigger, self),
|
||||
f2 = Utils.settingsSaveHelperSimpleFunction(self.loadingDesc.trigger, self)
|
||||
;
|
||||
|
||||
self.title.subscribe(function (sValue) {
|
||||
Remote.saveAdminConfig(f1, {
|
||||
'Title': Utils.trim(sValue)
|
||||
});
|
||||
});
|
||||
|
||||
self.loadingDesc.subscribe(function (sValue) {
|
||||
Remote.saveAdminConfig(f2, {
|
||||
'LoadingDescription': Utils.trim(sValue)
|
||||
});
|
||||
self.loadingDesc.subscribe(function (sValue) {
|
||||
Remote.saveAdminConfig(f2, {
|
||||
'LoadingDescription': Utils.trim(sValue)
|
||||
});
|
||||
});
|
||||
|
||||
}, 50);
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
|
||||
module.exports = BrandingAdminSettings;
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
LicensingAdminSettings.prototype.licenseIsUnlim = function ()
|
||||
{
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
LicensingAdminSettings.prototype.licenseExpiredMomentValue = function ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
{
|
||||
this.folderList = FolderStore.folderList;
|
||||
|
||||
this.folderListHelp = ko.observable('').extend({'throttle': 100});
|
||||
|
||||
this.loading = ko.computed(function () {
|
||||
|
||||
var
|
||||
|
|
@ -95,6 +97,25 @@
|
|||
FolderStore.folderList.error('');
|
||||
};
|
||||
|
||||
FoldersUserSettings.prototype.onBuild = function (oDom)
|
||||
{
|
||||
var self = this;
|
||||
oDom
|
||||
.on('mouseover', '.delete-folder-parent', function () {
|
||||
self.folderListHelp(Translator.i18n('SETTINGS_FOLDERS/HELP_DELETE_FOLDER'));
|
||||
})
|
||||
.on('mouseover', '.subscribe-folder-parent', function () {
|
||||
self.folderListHelp(Translator.i18n('SETTINGS_FOLDERS/HELP_SHOW_HIDE_FOLDER'));
|
||||
})
|
||||
.on('mouseover', '.check-folder-parent', function () {
|
||||
self.folderListHelp(Translator.i18n('SETTINGS_FOLDERS/HELP_SHOW_HIDE_FOLDER_COUNT'));
|
||||
})
|
||||
.on('mouseout', '.subscribe-folder-parent, .check-folder-parent, .delete-folder-parent', function () {
|
||||
self.folderListHelp('');
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
FoldersUserSettings.prototype.createFolder = function ()
|
||||
{
|
||||
require('Knoin/Knoin').showScreenPopup(require('View/Popup/FolderCreate'));
|
||||
|
|
@ -160,6 +181,20 @@
|
|||
oFolder.subScribed(false);
|
||||
};
|
||||
|
||||
FoldersUserSettings.prototype.checkableTrueFolder = function (oFolder)
|
||||
{
|
||||
Remote.folderSetCheckable(Utils.emptyFunction, oFolder.fullNameRaw, true);
|
||||
|
||||
oFolder.checkable(true);
|
||||
};
|
||||
|
||||
FoldersUserSettings.prototype.checkableFalseFolder = function (oFolder)
|
||||
{
|
||||
Remote.folderSetCheckable(Utils.emptyFunction, oFolder.fullNameRaw, false);
|
||||
|
||||
oFolder.checkable(false);
|
||||
};
|
||||
|
||||
module.exports = FoldersUserSettings;
|
||||
|
||||
}());
|
||||
|
|
@ -22,14 +22,11 @@
|
|||
this.language = ko.observable('')
|
||||
.extend({'limitedList': this.languages});
|
||||
|
||||
this.userLanguage = ko.observable('')
|
||||
.extend({'limitedList': this.languages});
|
||||
|
||||
this.languageAdmin = ko.observable('')
|
||||
.extend({'limitedList': this.languagesAdmin});
|
||||
|
||||
this.userLanguageAdmin = ko.observable('')
|
||||
.extend({'limitedList': this.languagesAdmin});
|
||||
this.userLanguage = ko.observable('');
|
||||
this.userLanguageAdmin = ko.observable('');
|
||||
}
|
||||
|
||||
LanguageStore.prototype.populate = function ()
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
AccountUserStore.prototype.isRootAccount = function ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
Enums = require('Common/Enums'),
|
||||
Globals = require('Common/Globals'),
|
||||
Utils = require('Common/Utils'),
|
||||
|
||||
Settings = require('Storage/Settings'),
|
||||
|
||||
|
|
@ -52,6 +53,8 @@
|
|||
|
||||
this.contactsIsAllowed = ko.observable(false);
|
||||
|
||||
this.attahcmentsActions = ko.observableArray([]);
|
||||
|
||||
this.devEmail = '';
|
||||
this.devPassword = '';
|
||||
}
|
||||
|
|
@ -67,6 +70,9 @@
|
|||
|
||||
this.contactsIsAllowed(!!Settings.settingsGet('ContactsIsAllowed'));
|
||||
|
||||
var mAttahcmentsActions = Settings.settingsGet('AttahcmentsActions');
|
||||
this.attahcmentsActions(Utils.isNonEmptyArray(mAttahcmentsActions) ? mAttahcmentsActions : []);
|
||||
|
||||
this.devEmail = Settings.settingsGet('DevEmail');
|
||||
this.devPassword = Settings.settingsGet('DevPassword');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -181,13 +181,10 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @param {boolean=} bBoot = false
|
||||
* @returns {Array}
|
||||
* @return {Array}
|
||||
*/
|
||||
FolderUserStore.prototype.getNextFolderNames = function (bBoot)
|
||||
FolderUserStore.prototype.getNextFolderNames = function ()
|
||||
{
|
||||
bBoot = Utils.isUnd(bBoot) ? false : !!bBoot;
|
||||
|
||||
var
|
||||
aResult = [],
|
||||
iLimit = 5,
|
||||
|
|
@ -200,7 +197,8 @@
|
|||
if (oFolder && sInboxFolderName !== oFolder.fullNameRaw &&
|
||||
oFolder.selectable && oFolder.existen &&
|
||||
iTimeout > oFolder.interval &&
|
||||
(!bBoot || oFolder.subScribed()))
|
||||
(oFolder.isSystemFolder() || (oFolder.subScribed() && oFolder.checkable()))
|
||||
)
|
||||
{
|
||||
aTimeouts.push([oFolder.interval, oFolder.fullNameRaw]);
|
||||
}
|
||||
|
|
@ -236,7 +234,9 @@
|
|||
return iLimit <= aResult.length;
|
||||
});
|
||||
|
||||
return _.uniq(aResult);
|
||||
aResult = _.uniq(aResult);
|
||||
|
||||
return aResult;
|
||||
};
|
||||
|
||||
module.exports = new FolderUserStore();
|
||||
|
|
|
|||
|
|
@ -41,11 +41,14 @@
|
|||
|
||||
this.messageListCount = ko.observable(0);
|
||||
this.messageListSearch = ko.observable('');
|
||||
this.messageListThreadUid = ko.observable('');
|
||||
this.messageListPage = ko.observable(1);
|
||||
this.messageListPageBeforeThread = ko.observable(1);
|
||||
this.messageListError = ko.observable('');
|
||||
|
||||
this.messageListEndFolder = ko.observable('');
|
||||
this.messageListEndSearch = ko.observable('');
|
||||
this.messageListEndThreadUid = ko.observable('');
|
||||
this.messageListEndPage = ko.observable(1);
|
||||
|
||||
this.messageListLoading = ko.observable(false);
|
||||
|
|
@ -63,15 +66,12 @@
|
|||
|
||||
this.message.viewTrigger = ko.observable(false);
|
||||
|
||||
this.messageLastThreadUidsData = ko.observable(null);
|
||||
|
||||
this.messageError = ko.observable('');
|
||||
|
||||
this.messageCurrentLoading = ko.observable(false);
|
||||
this.messageThreadLoading = ko.observable(false);
|
||||
|
||||
this.messageLoading = ko.computed(function () {
|
||||
return !!(this.messageCurrentLoading() || this.messageThreadLoading());
|
||||
return this.messageCurrentLoading();
|
||||
}, this);
|
||||
|
||||
this.messageLoadingThrottle = ko.observable(false).extend({'throttle': 50});
|
||||
|
|
@ -91,8 +91,12 @@
|
|||
|
||||
MessageUserStore.prototype.computers = function ()
|
||||
{
|
||||
var self = this;
|
||||
|
||||
this.messageListEndHash = ko.computed(function () {
|
||||
return this.messageListEndFolder() + '|' + this.messageListEndSearch() + '|' + this.messageListEndPage();
|
||||
return this.messageListEndFolder() + '|' + this.messageListEndSearch() +
|
||||
'|' + this.messageListEndThreadUid() +
|
||||
'|' + this.messageListEndPage();
|
||||
}, this);
|
||||
|
||||
this.messageListPageCount = ko.computed(function () {
|
||||
|
|
@ -105,7 +109,8 @@
|
|||
'read': this.messageListSearch,
|
||||
'write': function (sValue) {
|
||||
kn.setHash(Links.mailBox(
|
||||
FolderStore.currentFolderFullNameHash(), 1, Utils.trim(sValue.toString())
|
||||
FolderStore.currentFolderFullNameHash(), 1,
|
||||
Utils.trim(sValue.toString()), self.messageListThreadUid()
|
||||
));
|
||||
},
|
||||
'owner': this
|
||||
|
|
@ -322,6 +327,7 @@
|
|||
var
|
||||
self = this,
|
||||
iUnseenCount = 0,
|
||||
oMessage = null,
|
||||
aMessageList = this.messageList(),
|
||||
oFromFolder = Cache.getFolderFromCacheList(sFromFolderFullNameRaw),
|
||||
oToFolder = '' === sToFolderFullNameRaw ? null : Cache.getFolderFromCacheList(sToFolderFullNameRaw || ''),
|
||||
|
|
@ -401,6 +407,56 @@
|
|||
{
|
||||
Cache.setFolderHash(sToFolderFullNameRaw, '');
|
||||
}
|
||||
|
||||
if ('' !== this.messageListThreadUid())
|
||||
{
|
||||
aMessageList = this.messageList();
|
||||
|
||||
if (aMessageList && 0 < aMessageList.length && !!_.find(aMessageList, function (oMessage) {
|
||||
return !!(oMessage && oMessage.deleted() && oMessage.uid === self.messageListThreadUid());
|
||||
}))
|
||||
{
|
||||
oMessage = _.find(aMessageList, function (oMessage) {
|
||||
return oMessage && !oMessage.deleted();
|
||||
});
|
||||
|
||||
if (oMessage && this.messageListThreadUid() !== Utils.pString(oMessage.uid))
|
||||
{
|
||||
this.messageListThreadUid(Utils.pString(oMessage.uid));
|
||||
|
||||
kn.setHash(Links.mailBox(
|
||||
FolderStore.currentFolderFullNameHash(),
|
||||
this.messageListPage(),
|
||||
this.messageListSearch(),
|
||||
this.messageListThreadUid()
|
||||
), true, true);
|
||||
}
|
||||
else if (!oMessage)
|
||||
{
|
||||
if (1 < this.messageListPage())
|
||||
{
|
||||
this.messageListPage(this.messageListPage() - 1);
|
||||
|
||||
kn.setHash(Links.mailBox(
|
||||
FolderStore.currentFolderFullNameHash(),
|
||||
this.messageListPage(),
|
||||
this.messageListSearch(),
|
||||
this.messageListThreadUid()
|
||||
), true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.messageListThreadUid('');
|
||||
|
||||
kn.setHash(Links.mailBox(
|
||||
FolderStore.currentFolderFullNameHash(),
|
||||
this.messageListPageBeforeThread(),
|
||||
this.messageListSearch()
|
||||
), true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MessageUserStore.prototype.addBlockquoteSwitcherCallback = function ()
|
||||
|
|
@ -569,7 +625,7 @@
|
|||
oMessagesBodiesDom.append(oMessage.body);
|
||||
}
|
||||
|
||||
oMessage.storeDataToDom();
|
||||
oMessage.storeDataInDom();
|
||||
|
||||
if (bHasInternals)
|
||||
{
|
||||
|
|
@ -589,7 +645,7 @@
|
|||
if (oMessage.body)
|
||||
{
|
||||
oMessage.body.data('rl-cache-count', ++Globals.iMessageBodyCacheCount);
|
||||
oMessage.fetchDataToDom();
|
||||
oMessage.fetchDataFromDom();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -663,14 +719,6 @@
|
|||
}
|
||||
};
|
||||
|
||||
MessageUserStore.prototype.selectThreadMessage = function (sFolder, sUid)
|
||||
{
|
||||
if (Remote.message(this.onMessageResponse, sFolder, sUid))
|
||||
{
|
||||
this.messageThreadLoading(true);
|
||||
}
|
||||
};
|
||||
|
||||
MessageUserStore.prototype.populateMessageBody = function (oMessage)
|
||||
{
|
||||
if (oMessage)
|
||||
|
|
@ -692,7 +740,6 @@
|
|||
this.hideMessageBodies();
|
||||
|
||||
this.messageCurrentLoading(false);
|
||||
this.messageThreadLoading(false);
|
||||
|
||||
if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
|
||||
{
|
||||
|
|
@ -714,7 +761,7 @@
|
|||
|
||||
/**
|
||||
* @param {Array} aList
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
MessageUserStore.prototype.calculateMessageListHash = function (aList)
|
||||
{
|
||||
|
|
@ -729,7 +776,6 @@
|
|||
oData.Result['@Collection'] && Utils.isArray(oData.Result['@Collection']))
|
||||
{
|
||||
var
|
||||
self = this,
|
||||
iIndex = 0,
|
||||
iLen = 0,
|
||||
iCount = 0,
|
||||
|
|
@ -811,9 +857,11 @@
|
|||
this.messageListCount(iCount);
|
||||
this.messageListSearch(Utils.isNormal(oData.Result.Search) ? oData.Result.Search : '');
|
||||
this.messageListPage(window.Math.ceil((iOffset / SettingsStore.messagesPerPage()) + 1));
|
||||
this.messageListThreadUid(Utils.isNormal(oData.Result.ThreadUid) ? Utils.pString(oData.Result.ThreadUid) : '');
|
||||
|
||||
this.messageListEndFolder(Utils.isNormal(oData.Result.Folder) ? oData.Result.Folder : '');
|
||||
this.messageListEndSearch(Utils.isNormal(oData.Result.Search) ? oData.Result.Search : '');
|
||||
this.messageListEndSearch(this.messageListSearch());
|
||||
this.messageListEndThreadUid(this.messageListThreadUid());
|
||||
this.messageListEndPage(this.messageListPage());
|
||||
|
||||
this.messageListDisableAutoSelect(true);
|
||||
|
|
@ -823,26 +871,6 @@
|
|||
|
||||
Cache.clearNewMessageCache();
|
||||
|
||||
if (AppStore.threadsAllowed() && SettingsStore.useThreads())
|
||||
{
|
||||
oMessage = this.message();
|
||||
if (oMessage)
|
||||
{
|
||||
Remote.messageThreadsFromCache(function (sResult, oData) {
|
||||
|
||||
if (Enums.StorageResultType.Success === sResult && oData && oData.Result && oData.Result.ThreadUids)
|
||||
{
|
||||
self.messageLastThreadUidsData({
|
||||
'Folder': oData.Result.Folder,
|
||||
'Uid': oData.Result.Uid,
|
||||
'Uids': oData.Result.ThreadUids
|
||||
});
|
||||
}
|
||||
|
||||
}, oMessage.folderFullNameRaw, oMessage.uid);
|
||||
}
|
||||
}
|
||||
|
||||
if (oFolder && (bCached || bUnreadCountChange || SettingsStore.useThreads()))
|
||||
{
|
||||
require('App/User').folderInformation(oFolder.fullNameRaw, aList);
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@
|
|||
|
||||
NotificationUserStore.prototype.initNotificationPlayer = function ()
|
||||
{
|
||||
if (Audio && Audio.supported)
|
||||
if (Audio && Audio.supportedNotification)
|
||||
{
|
||||
this.soundNotificationIsSupported(true);
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@
|
|||
|
||||
NotificationUserStore.prototype.playSoundNotification = function (bSkipSetting)
|
||||
{
|
||||
if (Audio && Audio.supported && (bSkipSetting ? true : this.enableSoundNotification()))
|
||||
if (Audio && Audio.supportedNotification && (bSkipSetting ? true : this.enableSoundNotification()))
|
||||
{
|
||||
Audio.playNotification();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
/**
|
||||
* @param {string} sEmail
|
||||
* @param {string=} sPassword
|
||||
* @returns {?}
|
||||
* @return {?}
|
||||
*/
|
||||
PgpUserStore.prototype.findPrivateKeyByEmail = function (sEmail, sPassword)
|
||||
{
|
||||
|
|
@ -109,7 +109,7 @@
|
|||
|
||||
/**
|
||||
* @param {string=} sPassword
|
||||
* @returns {?}
|
||||
* @return {?}
|
||||
*/
|
||||
PgpUserStore.prototype.findSelfPrivateKey = function (sPassword)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,8 +6,15 @@
|
|||
}
|
||||
|
||||
.flag-name {
|
||||
|
||||
border-bottom: 1px dashed #555;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
|
||||
&:focus {
|
||||
outline: 1px;
|
||||
outline-style: dotted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,15 @@
|
|||
0% {background-position: 0 0;} 100% {background-position: 60px 0;}
|
||||
}
|
||||
|
||||
@keyframes login-form-shake {
|
||||
0% {transform: translateX(0);}
|
||||
12.5% {transform: translateX(-6px) rotateY(-5deg)}
|
||||
37.5% {transform: translateX(5px) rotateY(4deg)}
|
||||
62.5% {transform: translateX(-3px) rotateY(-2deg)}
|
||||
87.5% {transform: translateX(2px) rotateY(1deg)}
|
||||
100% {transform: translateX(0)}
|
||||
}
|
||||
|
||||
html.csstransitions.rl-started-trigger.no-mobile .b-login-content .loginFormWrapper {
|
||||
/*transform: scale(1.1);*/
|
||||
transform: translateY(-20px);
|
||||
|
|
@ -56,6 +65,14 @@ html.csstransitions.rl-started-trigger.no-mobile .b-login-content .loginFormWrap
|
|||
}
|
||||
}
|
||||
|
||||
&.cssanimations.csstransitions.no-mobile .b-login-content .errorAnimated {
|
||||
animation: login-form-shake 400ms ease-in-out;
|
||||
}
|
||||
|
||||
&.cssanimations.csstransitions.no-mobile .b-login-content .afterLoginHide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* &.csstransitions.no-mobile #rl-content {
|
||||
.transition(opacity 0.3s ease-out);
|
||||
}*/
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04), 0 1px 5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
|
||||
&.waiting {
|
||||
opacity: 0.6;
|
||||
|
|
@ -120,6 +120,15 @@
|
|||
color: #aaa;
|
||||
}
|
||||
|
||||
.attachmentIcon {
|
||||
&.icon-none {
|
||||
display: none;
|
||||
}
|
||||
&.icon-file-certificate {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.attachmentIconText {
|
||||
display: inline-block;
|
||||
font-size: 28px;
|
||||
|
|
@ -131,15 +140,13 @@
|
|||
font-style: normal;
|
||||
}
|
||||
|
||||
.attachmentIcon.icon-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*
|
||||
.attachmentIconParent.hasPreview.isImage {
|
||||
.iconMain {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
.attachmentIconParent.hasPreview:hover {
|
||||
.iconPreview {
|
||||
|
|
|
|||
|
|
@ -37,13 +37,36 @@
|
|||
|
||||
.e-component {
|
||||
|
||||
&.e-select {
|
||||
|
||||
select:focus {
|
||||
outline: 1px;
|
||||
outline-style: dotted;
|
||||
}
|
||||
}
|
||||
|
||||
&.e-checkbox {
|
||||
|
||||
margin-bottom: 6px;
|
||||
margin-left: -2px;
|
||||
padding: 2px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: 1px;
|
||||
outline-style: dotted;
|
||||
}
|
||||
|
||||
.e-checkbox-icon {
|
||||
padding: 1px 0 0 1px;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
color: #999;
|
||||
outline: 0;
|
||||
outline-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,11 +83,12 @@
|
|||
|
||||
.e-component.material-design {
|
||||
|
||||
margin-top: 2px;
|
||||
margin-bottom: 6px;
|
||||
|
||||
&.e-checkbox {
|
||||
|
||||
margin-top: 2px;
|
||||
padding: 2px 2px 1px 2px;
|
||||
|
||||
.sub-checkbox-container {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
perspective: 500px;
|
||||
|
||||
.descWrapper {
|
||||
|
||||
|
|
@ -59,7 +60,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.signMeLabel, .languageLabel {
|
||||
.languageLabel {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.signMeLabel .e-checkbox {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,17 @@ html.rl-no-preview-pane {
|
|||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.listThreadUidDesc {
|
||||
font-size: 16px;
|
||||
padding: 7px 20px 6px 20px;
|
||||
background-color: #aaa;
|
||||
border-bottom: 1px solid #888;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
text-shadow: 0 1px 0 #000;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.delimiter {
|
||||
display: block;
|
||||
height: 1px;
|
||||
|
|
@ -313,6 +324,12 @@ html.rl-no-preview-pane {
|
|||
&.e-single-line .dateParent {
|
||||
}
|
||||
|
||||
.threadsParent {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.attachmentParent {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
|
|
@ -322,7 +339,7 @@ html.rl-no-preview-pane {
|
|||
|
||||
&.e-single-line .attachmentParent {
|
||||
float: left;
|
||||
margin: 0 5px 0 0;
|
||||
margin: 0 8px 0 0;
|
||||
}
|
||||
|
||||
.senderParent {
|
||||
|
|
@ -395,11 +412,25 @@ html.rl-no-preview-pane {
|
|||
|
||||
.threads-len {
|
||||
.threads-len-data {
|
||||
background-color: #999;
|
||||
color: #fff;
|
||||
|
||||
background-color: #eee;
|
||||
color: #666;
|
||||
border-radius: 6px;
|
||||
padding: 1px 6px 1px 5px;
|
||||
margin-right: 5px;
|
||||
padding: 2px 0px 1px 6px;
|
||||
margin-right: 2px;
|
||||
border: 1px solid #ccc;
|
||||
|
||||
[class^="icon-"],
|
||||
[class*=" icon-"] {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #aaa;
|
||||
border-color: #666;
|
||||
text-shadow: 0 1px 0 #999;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,23 @@ html.rl-no-preview-pane {
|
|||
right: 10px;
|
||||
}
|
||||
|
||||
.flagParent {
|
||||
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
|
||||
.flagOn {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.flagOff {
|
||||
opacity: 0.5;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.messageItemHeader {
|
||||
|
||||
position: absolute;
|
||||
|
|
@ -120,23 +137,6 @@ html.rl-no-preview-pane {
|
|||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.flagParent {
|
||||
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
|
||||
.flagOn {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.flagOff {
|
||||
opacity: 0.5;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.informationShort {
|
||||
margin-left: 22px;
|
||||
a {
|
||||
|
|
@ -263,6 +263,14 @@ html.rl-no-preview-pane {
|
|||
}
|
||||
}
|
||||
|
||||
&.unselectedAttachmentsError {
|
||||
.attachmentItem {
|
||||
box-shadow: 0 1px 4px red;
|
||||
box-shadow: 0 1px 5px rgba(255, 0, 0, 0.4);
|
||||
box-shadow: 0 0 0 1px rgba(255, 0, 0, 0.2), 0 1px 5px rgba(255, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.controls-handle {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.delete-folder, .subscribe-folder, .unsubscribe-folder {
|
||||
.delete-folder, .subscribe-folder, .unsubscribe-folder, .check-folder, .uncheck-folder {
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
|
@ -94,6 +94,10 @@
|
|||
.unsubscribe-folder {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.uncheck-folder {
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
.folder-padding.deep-1 {
|
||||
|
|
|
|||
|
|
@ -22,5 +22,11 @@
|
|||
.flag-name {
|
||||
border-bottom: 1px dashed #555;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
|
||||
&:focus {
|
||||
outline: 1px;
|
||||
outline-style: dotted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@
|
|||
color: #336699;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
|
||||
&:focus {
|
||||
outline: 1px;
|
||||
outline-style: dotted;
|
||||
}
|
||||
}
|
||||
|
||||
.g-ui-min-height-300 {
|
||||
|
|
@ -181,12 +187,21 @@ e-spinner {
|
|||
}
|
||||
|
||||
.e-languages {
|
||||
|
||||
margin-top: 8px;
|
||||
color: #333;
|
||||
|
||||
.flag-name {
|
||||
|
||||
color: #333;
|
||||
/*text-decoration: underline;*/
|
||||
border-bottom: 1px dashed #333;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
|
||||
&:focus {
|
||||
outline: 1px;
|
||||
outline-style: dotted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,4 +245,4 @@ html.cssanimations {
|
|||
|
||||
.command.command-disabled.hide-on-disabled-command {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ label {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
label.inline {
|
||||
label.inline, span.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
}
|
||||
|
||||
.opentip-container {
|
||||
|
||||
z-index: 2001 !important;
|
||||
.ot-content {
|
||||
font-size: 13px;
|
||||
|
|
@ -41,4 +42,30 @@
|
|||
&.style-rainloopErrorTip .ot-content {
|
||||
color: red;
|
||||
}
|
||||
|
||||
&.ot-show-effect-none, &.ot-hide-effect-none {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
&.ot-show-effect-fade {
|
||||
|
||||
transition: none;
|
||||
|
||||
&.ot-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.ot-going-to-show {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
&.ot-showing {
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
&.ot-visible {
|
||||
opacity: 1;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@
|
|||
border-color: transparent;
|
||||
border-top-color: #999;
|
||||
|
||||
animation: rotation .8s infinite linear;
|
||||
animation: rotation .8s infinite ease-in-out;
|
||||
}
|
||||
|
||||
&.big {
|
||||
|
|
|
|||
|
|
@ -34,8 +34,17 @@
|
|||
this.loginError = ko.observable(false);
|
||||
this.passwordError = ko.observable(false);
|
||||
|
||||
this.loginErrorAnimation = ko.observable(false).extend({'falseTimeout': 500});
|
||||
this.passwordErrorAnimation = ko.observable(false).extend({'falseTimeout': 500});
|
||||
|
||||
this.loginFocus = ko.observable(false);
|
||||
|
||||
this.formHidden = ko.observable(false);
|
||||
|
||||
this.formError = ko.computed(function () {
|
||||
return this.loginErrorAnimation() || this.passwordErrorAnimation();
|
||||
}, this);
|
||||
|
||||
this.login.subscribe(function () {
|
||||
this.loginError(false);
|
||||
}, this);
|
||||
|
|
@ -44,6 +53,14 @@
|
|||
this.passwordError(false);
|
||||
}, this);
|
||||
|
||||
this.loginError.subscribe(function (bV) {
|
||||
this.loginErrorAnimation(!!bV);
|
||||
}, this);
|
||||
|
||||
this.passwordError.subscribe(function (bV) {
|
||||
this.passwordErrorAnimation(!!bV);
|
||||
}, this);
|
||||
|
||||
this.submitRequest = ko.observable(false);
|
||||
this.submitError = ko.observable('');
|
||||
|
||||
|
|
@ -51,6 +68,9 @@
|
|||
|
||||
Utils.triggerAutocompleteInputChange();
|
||||
|
||||
this.loginError(false);
|
||||
this.passwordError(false);
|
||||
|
||||
this.loginError('' === Utils.trim(this.login()));
|
||||
this.passwordError('' === Utils.trim(this.password()));
|
||||
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
* @return {boolean}
|
||||
*/
|
||||
ActivatePopupView.prototype.validateSubscriptionKey = function ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
ko = require('ko'),
|
||||
|
||||
Utils = require('Common/Utils'),
|
||||
Translator = require('Common/Translator'),
|
||||
|
||||
MessageStore = require('Stores/User/Message'),
|
||||
|
||||
|
|
@ -46,6 +47,19 @@
|
|||
this.cancelCommand();
|
||||
});
|
||||
|
||||
this.selectedDates = ko.computed(function () {
|
||||
Translator.trigger();
|
||||
return [
|
||||
{'id': -1, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_ALL')},
|
||||
{'id': 3, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_3_DAYS')},
|
||||
{'id': 7, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_7_DAYS')},
|
||||
{'id': 30, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_MONTH')},
|
||||
{'id': 90, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_3_MONTHS')},
|
||||
{'id': 180, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_6_MONTHS')},
|
||||
{'id': 365, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_YEAR')}
|
||||
];
|
||||
}, this);
|
||||
|
||||
kn.constructorEnd(this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
/* global require, module */
|
||||
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
|
|
@ -523,7 +525,6 @@
|
|||
if (window.Dropbox)
|
||||
{
|
||||
window.Dropbox.choose({
|
||||
//'iframe': true,
|
||||
'success': function(aFiles) {
|
||||
|
||||
if (aFiles && aFiles[0] && aFiles[0]['link'])
|
||||
|
|
@ -995,7 +996,7 @@
|
|||
*
|
||||
* @param {Array} aList
|
||||
* @param {boolean} bFriendly
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
ComposePopupView.prototype.emailArrayToStringLineHelper = function (aList, bFriendly)
|
||||
{
|
||||
|
|
@ -1097,6 +1098,26 @@
|
|||
oText.find('.rlBlockquoteSwitcher').off('.rlBlockquoteSwitcher').remove();
|
||||
oText.find('[data-html-editor-font-wrapper]').removeAttr('data-html-editor-font-wrapper');
|
||||
|
||||
(function () {
|
||||
|
||||
var oTmp = null, iLimit = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
iLimit++;
|
||||
|
||||
oTmp = oText.children();
|
||||
if (10 > iLimit && oTmp.is('div') && 1 === oTmp.length)
|
||||
{
|
||||
oTmp.children().unwrap();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}());
|
||||
|
||||
sText = oText.html();
|
||||
}
|
||||
|
||||
|
|
@ -1414,11 +1435,11 @@
|
|||
Events.sub('window.resize.real', this.resizerTrigger);
|
||||
Events.sub('window.resize.real', _.debounce(this.resizerTrigger, 50));
|
||||
|
||||
if (this.dropboxEnabled())
|
||||
if (this.dropboxEnabled() && this.dropboxApiKey() && !window.Dropbox)
|
||||
{
|
||||
oScript = window.document.createElement('script');
|
||||
oScript.type = 'text/javascript';
|
||||
oScript.src = 'https://www.dropbox.com/static/api/1/dropins.js';
|
||||
oScript.src = 'https://www.dropbox.com/static/api/2/dropins.js';
|
||||
$(oScript).attr('id', 'dropboxjs').attr('data-app-key', self.dropboxApiKey());
|
||||
|
||||
window.document.body.appendChild(oScript);
|
||||
|
|
|
|||
|
|
@ -24,15 +24,16 @@
|
|||
var self = this;
|
||||
|
||||
this.fLang = null;
|
||||
this.sUserLanguage = '';
|
||||
this.userLanguage = ko.observable('');
|
||||
|
||||
this.langs = ko.observableArray([]);
|
||||
|
||||
this.languages = ko.computed(function () {
|
||||
var sUserLanguage = self.userLanguage();
|
||||
return _.map(self.langs(), function (sLanguage) {
|
||||
return {
|
||||
'key': sLanguage,
|
||||
'user': sLanguage === self.sUserLanguage,
|
||||
'user': sLanguage === sUserLanguage,
|
||||
'selected': ko.observable(false),
|
||||
'fullName': Utils.convertLangName(sLanguage)
|
||||
};
|
||||
|
|
@ -63,21 +64,22 @@
|
|||
});
|
||||
};
|
||||
|
||||
LanguagesPopupView.prototype.onBeforeShow = function ()
|
||||
{
|
||||
this.fLang = null;
|
||||
this.userLanguage('');
|
||||
|
||||
this.langs([]);
|
||||
};
|
||||
|
||||
LanguagesPopupView.prototype.onShow = function (fLanguage, aLangs, sUserLanguage)
|
||||
{
|
||||
this.fLang = fLanguage;
|
||||
this.sUserLanguage = sUserLanguage || '';
|
||||
this.userLanguage(sUserLanguage || '');
|
||||
|
||||
this.langs(aLangs);
|
||||
};
|
||||
|
||||
LanguagesPopupView.prototype.onHide = function ()
|
||||
{
|
||||
this.fLang = null;
|
||||
this.sUserLanguage = '';
|
||||
this.langs([]);
|
||||
};
|
||||
|
||||
LanguagesPopupView.prototype.changeLanguage = function (sLang)
|
||||
{
|
||||
if (this.fLang)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
this.additionalCode = ko.observable('');
|
||||
this.additionalCode.error = ko.observable(false);
|
||||
this.additionalCode.errorAnimation = ko.observable(false).extend({'falseTimeout': 500});
|
||||
this.additionalCode.focused = ko.observable(false);
|
||||
this.additionalCode.visibility = ko.observable(false);
|
||||
this.additionalCodeSignMe = ko.observable(false);
|
||||
|
|
@ -56,6 +57,16 @@
|
|||
this.emailError = ko.observable(false);
|
||||
this.passwordError = ko.observable(false);
|
||||
|
||||
this.emailErrorAnimation = ko.observable(false).extend({'falseTimeout': 500});
|
||||
this.passwordErrorAnimation = ko.observable(false).extend({'falseTimeout': 500});
|
||||
|
||||
this.formHidden = ko.observable(false);
|
||||
|
||||
this.formError = ko.computed(function () {
|
||||
return this.emailErrorAnimation() || this.passwordErrorAnimation() ||
|
||||
(this.additionalCode.visibility() && this.additionalCode.errorAnimation());
|
||||
}, this);
|
||||
|
||||
this.emailFocus = ko.observable(false);
|
||||
this.passwordFocus = ko.observable(false);
|
||||
this.submitFocus = ko.observable(false);
|
||||
|
|
@ -78,6 +89,18 @@
|
|||
this.additionalCode.error(false);
|
||||
}, this);
|
||||
|
||||
this.emailError.subscribe(function (bV) {
|
||||
this.emailErrorAnimation(!!bV);
|
||||
}, this);
|
||||
|
||||
this.passwordError.subscribe(function (bV) {
|
||||
this.passwordErrorAnimation(!!bV);
|
||||
}, this);
|
||||
|
||||
this.additionalCode.error.subscribe(function (bV) {
|
||||
this.additionalCode.errorAnimation(!!bV);
|
||||
}, this);
|
||||
|
||||
this.submitRequest = ko.observable(false);
|
||||
this.submitError = ko.observable('');
|
||||
this.submitErrorAddidional = ko.observable('');
|
||||
|
|
@ -115,16 +138,34 @@
|
|||
|
||||
Utils.triggerAutocompleteInputChange();
|
||||
|
||||
this.emailError(false);
|
||||
this.passwordError(false);
|
||||
|
||||
this.emailError('' === Utils.trim(this.email()));
|
||||
this.passwordError('' === Utils.trim(this.password()));
|
||||
|
||||
if (this.additionalCode.visibility())
|
||||
{
|
||||
this.additionalCode.error(false);
|
||||
this.additionalCode.error('' === Utils.trim(this.additionalCode()));
|
||||
}
|
||||
|
||||
if (this.emailError() || this.passwordError() || this.additionalCode.error())
|
||||
if (this.emailError() || this.passwordError() ||
|
||||
(this.additionalCode.visibility() && this.additionalCode.error()))
|
||||
{
|
||||
switch (true)
|
||||
{
|
||||
case this.emailError():
|
||||
this.emailFocus(true);
|
||||
break;
|
||||
case this.passwordError():
|
||||
this.passwordFocus(true);
|
||||
break;
|
||||
case this.additionalCode.visibility() && this.additionalCode.error():
|
||||
this.additionalCode.focused(true);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -426,6 +467,21 @@
|
|||
]);
|
||||
};
|
||||
|
||||
LoginUserView.prototype.selectLanguageOnTab = function (bShift)
|
||||
{
|
||||
if (!bShift)
|
||||
{
|
||||
var self = this;
|
||||
_.delay(function () {
|
||||
self.emailFocus(true);
|
||||
}, 5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = LoginUserView;
|
||||
|
||||
}());
|
||||
|
|
@ -62,6 +62,7 @@
|
|||
this.selectorMessageFocused = MessageStore.selectorMessageFocused;
|
||||
this.isMessageSelected = MessageStore.isMessageSelected;
|
||||
this.messageListSearch = MessageStore.messageListSearch;
|
||||
this.messageListThreadUid = MessageStore.messageListThreadUid;
|
||||
this.messageListError = MessageStore.messageListError;
|
||||
this.folderMenuForMove = FolderStore.folderMenuForMove;
|
||||
|
||||
|
|
@ -69,6 +70,7 @@
|
|||
|
||||
this.mainMessageListSearch = MessageStore.mainMessageListSearch;
|
||||
this.messageListEndFolder = MessageStore.messageListEndFolder;
|
||||
this.messageListEndThreadUid = MessageStore.messageListEndThreadUid;
|
||||
|
||||
this.messageListChecked = MessageStore.messageListChecked;
|
||||
this.messageListCheckedOrSelected = MessageStore.messageListCheckedOrSelected;
|
||||
|
|
@ -374,7 +376,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
MessageListMailBoxUserView.prototype.printableMessageCountForDeletion = function ()
|
||||
{
|
||||
|
|
@ -388,6 +390,15 @@
|
|||
this.inputMessageListSearchFocus(false);
|
||||
};
|
||||
|
||||
MessageListMailBoxUserView.prototype.cancelThreadUid = function ()
|
||||
{
|
||||
kn.setHash(Links.mailBox(
|
||||
FolderStore.currentFolderFullNameHash(),
|
||||
MessageStore.messageListPageBeforeThread(),
|
||||
MessageStore.messageListSearch()
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sToFolderFullNameRaw
|
||||
* @param {boolean} bCopy
|
||||
|
|
@ -611,11 +622,34 @@
|
|||
kn.setHash(Links.mailBox(
|
||||
FolderStore.currentFolderFullNameHash(),
|
||||
oPage.value,
|
||||
MessageStore.messageListSearch()
|
||||
MessageStore.messageListSearch(),
|
||||
MessageStore.messageListThreadUid()
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
MessageListMailBoxUserView.prototype.gotoThread = function (oMessage)
|
||||
{
|
||||
if (oMessage && 0 < oMessage.threadsLen())
|
||||
{
|
||||
MessageStore.messageListPageBeforeThread(MessageStore.messageListPage());
|
||||
|
||||
kn.setHash(Links.mailBox(
|
||||
FolderStore.currentFolderFullNameHash(),
|
||||
1,
|
||||
MessageStore.messageListSearch(),
|
||||
oMessage.uid
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
MessageListMailBoxUserView.prototype.clearListIsVisible = function ()
|
||||
{
|
||||
return '' === this.messageListSearchDesc() && '' === this.messageListError() &&
|
||||
'' === MessageStore.messageListEndThreadUid() &&
|
||||
0 < this.messageList().length && (this.isSpamFolder() || this.isTrashFolder());
|
||||
};
|
||||
|
||||
MessageListMailBoxUserView.prototype.onBuild = function (oDom)
|
||||
{
|
||||
var self = this;
|
||||
|
|
@ -641,6 +675,12 @@
|
|||
.on('click', '.messageList .messageListItem .flagParent', function () {
|
||||
self.flagMessages(ko.dataFor(this));
|
||||
})
|
||||
.on('click', '.messageList .messageListItem .threads-len', function () {
|
||||
self.gotoThread(ko.dataFor(this));
|
||||
})
|
||||
.on('dblclick', '.messageList .messageListItem .actionHandle', function () {
|
||||
self.gotoThread(ko.dataFor(this));
|
||||
})
|
||||
;
|
||||
|
||||
this.initUploaderForAppend();
|
||||
|
|
@ -723,6 +763,22 @@
|
|||
return false;
|
||||
});
|
||||
|
||||
key('t', [Enums.KeyState.MessageList], function () {
|
||||
|
||||
var oMessage = self.selectorMessageSelected();
|
||||
if (!oMessage)
|
||||
{
|
||||
oMessage = self.selectorMessageFocused();
|
||||
}
|
||||
|
||||
if (oMessage && 0 < oMessage.threadsLen())
|
||||
{
|
||||
self.gotoThread(oMessage);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// move
|
||||
key('m', Enums.KeyState.MessageList, function () {
|
||||
self.moveDropdownTrigger(true);
|
||||
|
|
@ -759,6 +815,11 @@
|
|||
self.cancelSearch();
|
||||
return false;
|
||||
}
|
||||
else if ('' !== self.messageListEndThreadUid())
|
||||
{
|
||||
self.cancelThreadUid();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// change focused state
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
'use strict';
|
||||
|
||||
var
|
||||
window = require('window'),
|
||||
_ = require('_'),
|
||||
$ = require('$'),
|
||||
ko = require('ko'),
|
||||
|
|
@ -19,9 +20,11 @@
|
|||
Events = require('Common/Events'),
|
||||
Translator = require('Common/Translator'),
|
||||
Audio = require('Common/Audio'),
|
||||
Links = require('Common/Links'),
|
||||
|
||||
Cache = require('Common/Cache'),
|
||||
|
||||
SocialStore = require('Stores/Social'),
|
||||
AppStore = require('Stores/User/App'),
|
||||
SettingsStore = require('Stores/User/Settings'),
|
||||
AccountStore = require('Stores/User/Account'),
|
||||
|
|
@ -56,11 +59,14 @@
|
|||
}
|
||||
;
|
||||
|
||||
this.oDom = null;
|
||||
this.oHeaderDom = null;
|
||||
this.oMessageScrollerDom = null;
|
||||
|
||||
this.pswp = null;
|
||||
|
||||
this.attahcmentsActions = AppStore.attahcmentsActions;
|
||||
|
||||
this.message = MessageStore.message;
|
||||
this.messageListChecked = MessageStore.messageListChecked;
|
||||
this.hasCheckedMessages = MessageStore.hasCheckedMessages;
|
||||
|
|
@ -79,9 +85,65 @@
|
|||
|
||||
this.messageListOfThreadsLoading = ko.observable(false).extend({'rateLimit': 1});
|
||||
|
||||
this.allowAttachmnetControls = ko.observable(false);
|
||||
this.highlightUnselectedAttachments = ko.observable(false).extend({'falseTimeout': 2000});
|
||||
|
||||
this.showAttachmnetControls = ko.observable(false);
|
||||
|
||||
this.allowAttachmnetControls = ko.computed(function () {
|
||||
return 0 < this.attahcmentsActions().length;
|
||||
}, this);
|
||||
|
||||
this.downloadAsZipAllowed = ko.computed(function () {
|
||||
return -1 < Utils.inArray('zip', this.attahcmentsActions());
|
||||
}, this);
|
||||
|
||||
this.downloadAsZipLoading = ko.observable(false);
|
||||
this.downloadAsZipError = ko.observable(false).extend({'falseTimeout': 7000});
|
||||
|
||||
this.saveToOwnCloudAllowed = ko.computed(function () {
|
||||
return -1 < Utils.inArray('owncloud', this.attahcmentsActions());
|
||||
}, this);
|
||||
|
||||
this.saveToOwnCloudLoading = ko.observable(false);
|
||||
this.saveToOwnCloudSuccess = ko.observable(false).extend({'falseTimeout': 2000});
|
||||
this.saveToOwnCloudError = ko.observable(false).extend({'falseTimeout': 7000});
|
||||
|
||||
this.saveToOwnCloudSuccess.subscribe(function (bV) {
|
||||
if (bV)
|
||||
{
|
||||
this.saveToOwnCloudError(false);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.saveToOwnCloudError.subscribe(function (bV) {
|
||||
if (bV)
|
||||
{
|
||||
this.saveToOwnCloudSuccess(false);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.saveToDropboxAllowed = ko.computed(function () {
|
||||
return -1 < Utils.inArray('dropbox', this.attahcmentsActions());
|
||||
}, this);
|
||||
|
||||
this.saveToDropboxLoading = ko.observable(false);
|
||||
this.saveToDropboxSuccess = ko.observable(false).extend({'falseTimeout': 2000});
|
||||
this.saveToDropboxError = ko.observable(false).extend({'falseTimeout': 7000});
|
||||
|
||||
this.saveToDropboxSuccess.subscribe(function (bV) {
|
||||
if (bV)
|
||||
{
|
||||
this.saveToDropboxError(false);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.saveToDropboxError.subscribe(function (bV) {
|
||||
if (bV)
|
||||
{
|
||||
this.saveToDropboxSuccess(false);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.showAttachmnetControls.subscribe(function (bV) {
|
||||
if (this.message())
|
||||
{
|
||||
|
|
@ -115,12 +177,6 @@
|
|||
this.moreDropdownTrigger = ko.observable(false);
|
||||
this.messageDomFocused = ko.observable(false).extend({'rateLimit': 0});
|
||||
|
||||
// TODO
|
||||
// ko.computed(function () {
|
||||
// window.console.log('focus:' + AppStore.focusedState() + ', dom:' +
|
||||
// this.messageDomFocused() + ', key:' + Globals.keyScope() + ' ~ ' + Globals.keyScopeReal());
|
||||
// }, this).extend({'throttle': 1});
|
||||
|
||||
this.messageVisibility = ko.computed(function () {
|
||||
return !this.messageLoadingThrottle() && !!this.message();
|
||||
}, this);
|
||||
|
|
@ -204,6 +260,9 @@
|
|||
}
|
||||
}, this.messageVisibility);
|
||||
|
||||
this.dropboxEnabled = SocialStore.dropbox.enabled;
|
||||
this.dropboxApiKey = SocialStore.dropbox.apiKey;
|
||||
|
||||
// viewer
|
||||
|
||||
this.viewBodyTopValue = ko.observable(0);
|
||||
|
|
@ -230,140 +289,6 @@
|
|||
this.viewIsImportant = ko.observable(false);
|
||||
this.viewIsFlagged = ko.observable(false);
|
||||
|
||||
// THREADS
|
||||
this.viewThreads = ko.observableArray([]);
|
||||
this.viewThreads.trigger = ko.observable(false);
|
||||
|
||||
this.viewThreadMessages = ko.observableArray([]);
|
||||
this.viewThreadMessages.error = ko.observable('');
|
||||
this.viewThreadMessages.showMore = ko.observable(false);
|
||||
this.viewThreadMessages.limit = 6;
|
||||
|
||||
this.viewThreadMessages.subscribe(function (aList) {
|
||||
this.viewThreadMessages.error('');
|
||||
this.viewThreadMessages.showMore(this.viewThreadMessages.limit >= aList.length);
|
||||
}, this);
|
||||
|
||||
MessageStore.messageLastThreadUidsData.subscribe(function (oData) {
|
||||
if (oData && oData['Uids'])
|
||||
{
|
||||
oData['Uid'] = Utils.pString(oData['Uid']);
|
||||
if (this.viewFolder === oData['Folder'] && this.viewUid === oData['Uid'])
|
||||
{
|
||||
this.viewThreads(oData['Uids']);
|
||||
this.viewThreads.trigger(!this.viewThreads.trigger());
|
||||
}
|
||||
|
||||
var oMessage = MessageStore.message();
|
||||
if (oMessage && oMessage.folderFullNameRaw === oData['Folder'] && oMessage.uid === oData['Uid'])
|
||||
{
|
||||
oMessage.threads(oData['Uids']);
|
||||
}
|
||||
|
||||
oMessage = _.find(MessageStore.messageList(), function (oMessage) {
|
||||
return oMessage && oMessage.folderFullNameRaw === oData['Folder'] && oMessage.uid === oData['Uid'];
|
||||
});
|
||||
|
||||
if (oMessage && oMessage.folderFullNameRaw === oData['Folder'] && oMessage.uid === oData['Uid'])
|
||||
{
|
||||
oMessage.threads(oData['Uids']);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.viewThreads.status = ko.computed(function () {
|
||||
|
||||
this.viewThreads.trigger();
|
||||
|
||||
var
|
||||
iIndex = 0,
|
||||
aResult = [false, '', '', '', ''],
|
||||
aThreads = this.viewThreads.peek(),
|
||||
iLen = aThreads.length
|
||||
;
|
||||
|
||||
if (1 < iLen)
|
||||
{
|
||||
iIndex = Utils.inArray(this.viewUid, aThreads);
|
||||
if (-1 < iIndex)
|
||||
{
|
||||
aResult[0] = true;
|
||||
aResult[1] = (iIndex + 1) + '/' + iLen;
|
||||
aResult[2] = aThreads[iIndex];
|
||||
aResult[3] = 0 < iIndex && aThreads[iIndex - 1] ? aThreads[iIndex - 1] : '';
|
||||
aResult[4] = aThreads[iIndex + 1] ? aThreads[iIndex + 1] : '';
|
||||
}
|
||||
}
|
||||
|
||||
return aResult;
|
||||
|
||||
}, this).extend({'notify': 'always'});
|
||||
|
||||
this.viewThreadsControlVisibility = ko.computed(function () {
|
||||
return !!this.viewThreads.status()[0];
|
||||
}, this);
|
||||
|
||||
this.viewThreadsControlDesc = ko.computed(function () {
|
||||
return this.viewThreads.status()[1];
|
||||
}, this);
|
||||
|
||||
this.viewThreadsControlBackAllow = ko.computed(function () {
|
||||
return '' !== this.viewThreads.status()[4] && !this.messageLoadingThrottle();
|
||||
}, this);
|
||||
|
||||
this.viewThreadsControlForwardAllow = ko.computed(function () {
|
||||
return '' !== this.viewThreads.status()[3] && !this.messageLoadingThrottle();
|
||||
}, this);
|
||||
|
||||
this.threadBackCommand = Utils.createCommand(this, function () {
|
||||
var aStatus = this.viewThreads.status();
|
||||
this.openThreadMessage(aStatus[4]);
|
||||
}, this.viewThreadsControlBackAllow);
|
||||
|
||||
this.threadForwardCommand = Utils.createCommand(this, function () {
|
||||
var aStatus = this.viewThreads.status();
|
||||
this.openThreadMessage(aStatus[3]);
|
||||
}, this.viewThreadsControlForwardAllow);
|
||||
|
||||
this.threadsDropdownTrigger = ko.observable(false);
|
||||
|
||||
this.threadListCommand = Utils.createCommand(this, function () {
|
||||
|
||||
var
|
||||
self = this,
|
||||
sFolder = this.viewFolder,
|
||||
sUid = this.viewUid,
|
||||
aUids = this.viewThreads(),
|
||||
aStatus = this.viewThreads.status()
|
||||
;
|
||||
|
||||
if (aStatus && aStatus[0])
|
||||
{
|
||||
self.viewThreadMessages([]);
|
||||
|
||||
Promises.messageListSimple(sFolder, aUids, this.messageListOfThreadsLoading).then(function (aList) {
|
||||
|
||||
_.each(aList, function (oItem) {
|
||||
if (oItem && oItem.uid)
|
||||
{
|
||||
oItem.selected = sUid === oItem.uid;
|
||||
}
|
||||
});
|
||||
|
||||
self.viewThreadMessages(aList);
|
||||
|
||||
}).fail(function (iErrorCode) {
|
||||
self.viewThreadMessages([]);
|
||||
self.viewThreadMessages.error(Translator.getNotification(
|
||||
iErrorCode, '', Enums.Notification.CantGetMessageList));
|
||||
}).done();
|
||||
}
|
||||
|
||||
}, function () {
|
||||
return !this.messageLoadingThrottle() &&
|
||||
!this.messageListOfThreadsLoading();
|
||||
});
|
||||
|
||||
// PGP
|
||||
this.viewPgpPassword = ko.observable('');
|
||||
this.viewPgpSignedVerifyStatus = ko.computed(function () {
|
||||
|
|
@ -449,9 +374,6 @@
|
|||
this.viewIsImportant(oMessage.isImportant());
|
||||
this.viewIsFlagged(oMessage.flagged());
|
||||
|
||||
this.viewThreads(oMessage.threads());
|
||||
this.viewThreads.trigger(!this.viewThreads.trigger());
|
||||
|
||||
sLastEmail = oMessage.fromAsSingleEmail();
|
||||
Cache.getUserPic(sLastEmail, function (sPic, sEmail) {
|
||||
if (sPic !== self.viewUserPic() && sLastEmail === sEmail)
|
||||
|
|
@ -472,8 +394,6 @@
|
|||
this.viewUid = '';
|
||||
this.viewHash = '';
|
||||
|
||||
this.viewThreads([]);
|
||||
|
||||
this.scrollMessageToTop();
|
||||
}
|
||||
|
||||
|
|
@ -532,24 +452,6 @@
|
|||
kn.extendAsViewModel(['View/User/MailBox/MessageView', 'View/App/MailBox/MessageView', 'MailBoxMessageViewViewModel'], MessageViewMailBoxUserView);
|
||||
_.extend(MessageViewMailBoxUserView.prototype, AbstractView.prototype);
|
||||
|
||||
MessageViewMailBoxUserView.prototype.updateViewFlagsFromCache = function ()
|
||||
{
|
||||
var aFlags = this.getMessageFlagsFromCache(this.viewFolder, this.viewUid);
|
||||
if (aFlags)
|
||||
{
|
||||
this.viewIsFlagged(!!aFlags[1]);
|
||||
}
|
||||
};
|
||||
|
||||
MessageViewMailBoxUserView.prototype.openThreadMessage = function (sUid)
|
||||
{
|
||||
var oMessage = this.message();
|
||||
if (oMessage && sUid)
|
||||
{
|
||||
MessageStore.selectThreadMessage(oMessage.folderFullNameRaw, sUid);
|
||||
}
|
||||
};
|
||||
|
||||
MessageViewMailBoxUserView.prototype.isPgpActionVisible = function ()
|
||||
{
|
||||
return Enums.SignedVerifyStatus.Success !== this.viewPgpSignedVerifyStatus();
|
||||
|
|
@ -633,10 +535,13 @@
|
|||
{
|
||||
var
|
||||
self = this,
|
||||
oScript = null,
|
||||
sErrorMessage = Translator.i18n('PREVIEW_POPUP/IMAGE_ERROR'),
|
||||
fCheckHeaderHeight = _.bind(this.checkHeaderHeight, this)
|
||||
;
|
||||
|
||||
this.oDom = oDom;
|
||||
|
||||
this.fullScreenMode.subscribe(function (bValue) {
|
||||
if (bValue && self.message())
|
||||
{
|
||||
|
|
@ -659,6 +564,16 @@
|
|||
Utils.windowResize(250);
|
||||
});
|
||||
|
||||
if (this.dropboxEnabled() && this.dropboxApiKey() && !window.Dropbox)
|
||||
{
|
||||
oScript = window.document.createElement('script');
|
||||
oScript.type = 'text/javascript';
|
||||
oScript.src = 'https://www.dropbox.com/static/api/2/dropins.js';
|
||||
$(oScript).attr('id', 'dropboxjs').attr('data-app-key', self.dropboxApiKey());
|
||||
|
||||
window.document.body.appendChild(oScript);
|
||||
}
|
||||
|
||||
this.oHeaderDom = $('.messageItemHeader', oDom);
|
||||
this.oHeaderDom = this.oHeaderDom[0] ? this.oHeaderDom : null;
|
||||
|
||||
|
|
@ -667,9 +582,10 @@
|
|||
if (this.pswpDom)
|
||||
{
|
||||
oDom
|
||||
.on('click', '.attachmentImagePreview[data-index]', function (oEvent) {
|
||||
.on('click', '.attachmentImagePreview.visible', function (oEvent) {
|
||||
|
||||
var
|
||||
iIndex = 0,
|
||||
oPs = null,
|
||||
oEl = oEvent.currentTarget || null,
|
||||
aItems = []
|
||||
|
|
@ -686,14 +602,18 @@
|
|||
// }
|
||||
;
|
||||
|
||||
oDom.find('.attachmentImagePreview[data-index]').each(function (index, oSubElement) {
|
||||
oDom.find('.attachmentImagePreview.visible').each(function (index, oSubElement) {
|
||||
|
||||
var
|
||||
$oItem = $(oSubElement)
|
||||
;
|
||||
|
||||
if (oEl === oSubElement)
|
||||
{
|
||||
iIndex = index;
|
||||
}
|
||||
|
||||
aItems.push({
|
||||
// 'el': $oItem,
|
||||
'w': 600, 'h': 400,
|
||||
'src': $oItem.attr('href'),
|
||||
'title': $oItem.attr('title') || ''
|
||||
|
|
@ -705,7 +625,7 @@
|
|||
Globals.useKeyboardShortcuts(false);
|
||||
|
||||
oPs = new PhotoSwipe(self.pswpDom, PhotoSwipeUI_Default, aItems, {
|
||||
'index': Utils.pInt($(oEl).data('index')),
|
||||
'index': iIndex,
|
||||
'bgOpacity': 0.85,
|
||||
'loadingIndicatorDelay': 500,
|
||||
'errorMsg': '<div class="pswp__error-msg">' + sErrorMessage + '</div>',
|
||||
|
|
@ -759,35 +679,20 @@
|
|||
}
|
||||
|
||||
var oAttachment = ko.dataFor(this);
|
||||
if (oAttachment && oAttachment.isMp3() && Audio.supported)
|
||||
if (oAttachment && Audio.supported)
|
||||
{
|
||||
Audio.playMp3(oAttachment.linkDownload(), oAttachment.fileName);
|
||||
}
|
||||
})
|
||||
.on('click', '.thread-list .more-threads', function (e) {
|
||||
|
||||
var oLast = null;
|
||||
if (!e || 0 === e.clientX) // probably enter
|
||||
{
|
||||
// It's a bad bad hack :(
|
||||
oLast = $('.thread-list .e-item.thread-list-message.real-msg.more-that:first a.e-link', oDom);
|
||||
}
|
||||
|
||||
self.viewThreadMessages.showMore(true);
|
||||
self.threadsDropdownTrigger(true);
|
||||
|
||||
if (oLast)
|
||||
{
|
||||
oLast.focus();
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
.on('click', '.thread-list .thread-list-message', function () {
|
||||
var oMessage = ko.dataFor(this);
|
||||
if (oMessage && oMessage.folder && oMessage.uid)
|
||||
{
|
||||
self.openThreadMessage(oMessage.uid);
|
||||
switch (true)
|
||||
{
|
||||
case Audio.supportedMp3 && oAttachment.isMp3():
|
||||
Audio.playMp3(oAttachment.linkDownload(), oAttachment.fileName);
|
||||
break;
|
||||
case Audio.supportedOgg && oAttachment.isOgg():
|
||||
Audio.playOgg(oAttachment.linkDownload(), oAttachment.fileName);
|
||||
break;
|
||||
case Audio.supportedWav && oAttachment.isWav():
|
||||
Audio.playWav(oAttachment.linkDownload(), oAttachment.fileName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('click', '.attachmentsPlace .attachmentItem .attachmentNameParent', function () {
|
||||
|
|
@ -801,8 +706,26 @@
|
|||
require('App/User').download(oAttachment.linkDownload());
|
||||
}
|
||||
})
|
||||
.on('click', '.messageItemHeader .flagParent', function () {
|
||||
self.flagViewMessage();
|
||||
.on('click', '.messageItemHeader .subjectParent .flagParent', function () {
|
||||
var oMessage = self.message();
|
||||
if (oMessage)
|
||||
{
|
||||
require('App/User').messageListAction(oMessage.folderFullNameRaw, oMessage.uid,
|
||||
oMessage.flagged() ? Enums.MessageSetAction.UnsetFlag : Enums.MessageSetAction.SetFlag, [oMessage]);
|
||||
}
|
||||
})
|
||||
.on('click', '.thread-list .flagParent', function () {
|
||||
var oMessage = ko.dataFor(this);
|
||||
if (oMessage && oMessage.folder && oMessage.uid)
|
||||
{
|
||||
require('App/User').messageListAction(
|
||||
oMessage.folder, oMessage.uid,
|
||||
oMessage.flagged() ? Enums.MessageSetAction.UnsetFlag : Enums.MessageSetAction.SetFlag, [oMessage]);
|
||||
}
|
||||
|
||||
self.threadsDropdownTrigger(true);
|
||||
|
||||
return false;
|
||||
})
|
||||
;
|
||||
|
||||
|
|
@ -824,17 +747,16 @@
|
|||
this.initShortcuts();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
MessageViewMailBoxUserView.prototype.flagViewMessage = function ()
|
||||
MessageViewMailBoxUserView.prototype.selectSelectedThreadMessage = function ()
|
||||
{
|
||||
var oMessage = this.message();
|
||||
if (oMessage)
|
||||
{
|
||||
require('App/User').messageListAction(oMessage.folderFullNameRaw, oMessage.uid,
|
||||
oMessage.flagged() ? Enums.MessageSetAction.UnsetFlag : Enums.MessageSetAction.SetFlag, [oMessage]);
|
||||
}
|
||||
var self = this;
|
||||
_.delay(function () {
|
||||
var oLast = self.oDom ? $('.thread-list .e-item.thread-list-message.real-msg.selected a.e-link', self.oDom) : null;
|
||||
if (oLast && oLast[0])
|
||||
{
|
||||
oLast.focus();
|
||||
}
|
||||
}, 30);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -926,35 +848,16 @@
|
|||
}
|
||||
});
|
||||
|
||||
key('t', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
if (MessageStore.message() && self.viewThreadsControlVisibility())
|
||||
{
|
||||
self.threadsDropdownTrigger(true);
|
||||
self.threadListCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+up, command+up', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
key('ctrl+up, command+up, ctrl+left, command+left', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.goUpCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
key('ctrl+down, command+down', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
key('ctrl+down, command+down, ctrl+right, command+right', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.goDownCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
key('ctrl+left, command+left', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.threadForwardCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
key('ctrl+right, command+right', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
self.threadBackCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
// print
|
||||
key('ctrl+p, command+p', Enums.KeyState.MessageView, function () {
|
||||
if (self.message())
|
||||
|
|
@ -1107,6 +1010,121 @@
|
|||
}
|
||||
};
|
||||
|
||||
MessageViewMailBoxUserView.prototype.getAttachmentsHashes = function ()
|
||||
{
|
||||
return _.compact(_.map(this.message() ? this.message().attachments() : [], function (oItem) {
|
||||
return oItem && oItem.checked() ? oItem.download : '';
|
||||
}));
|
||||
};
|
||||
|
||||
MessageViewMailBoxUserView.prototype.downloadAsZip = function ()
|
||||
{
|
||||
var self = this, aHashes = this.getAttachmentsHashes();
|
||||
if (0 < aHashes.length)
|
||||
{
|
||||
Promises.attachmentsActions('Zip', aHashes, this.downloadAsZipLoading).then(function (oResult) {
|
||||
if (oResult && oResult.Result && oResult.Result.Files &&
|
||||
oResult.Result.Files[0] && oResult.Result.Files[0].Hash)
|
||||
{
|
||||
require('App/User').download(
|
||||
Links.attachmentDownload(oResult.Result.Files[0].Hash));
|
||||
}
|
||||
else
|
||||
{
|
||||
self.downloadAsZipError(true);
|
||||
}
|
||||
}).fail(function () {
|
||||
self.downloadAsZipError(true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.highlightUnselectedAttachments(true);
|
||||
}
|
||||
};
|
||||
|
||||
MessageViewMailBoxUserView.prototype.saveToOwnCloud = function ()
|
||||
{
|
||||
var self = this, aHashes = this.getAttachmentsHashes();
|
||||
if (0 < aHashes.length)
|
||||
{
|
||||
Promises.attachmentsActions('OwnCloud', aHashes, this.saveToOwnCloudLoading).then(function (oResult) {
|
||||
if (oResult && oResult.Result)
|
||||
{
|
||||
self.saveToOwnCloudSuccess(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.saveToOwnCloudError(true);
|
||||
}
|
||||
}).fail(function () {
|
||||
self.saveToOwnCloudError(true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.highlightUnselectedAttachments(true);
|
||||
}
|
||||
};
|
||||
|
||||
MessageViewMailBoxUserView.prototype.saveToDropbox = function ()
|
||||
{
|
||||
var self = this, aFiles = [], aHashes = this.getAttachmentsHashes();
|
||||
if (0 < aHashes.length)
|
||||
{
|
||||
if (window.Dropbox)
|
||||
{
|
||||
Promises.attachmentsActions('Dropbox', aHashes, this.saveToDropboxLoading).then(function (oResult) {
|
||||
if (oResult && oResult.Result && oResult.Result.Url && oResult.Result.ShortLife && oResult.Result.Files)
|
||||
{
|
||||
if (window.Dropbox && Utils.isArray(oResult.Result.Files))
|
||||
{
|
||||
_.each(oResult.Result.Files, function (oItem) {
|
||||
aFiles.push({
|
||||
'url': oResult.Result.Url +
|
||||
Links.attachmentDownload(oItem.Hash, oResult.Result.ShortLife),
|
||||
'filename': oItem.FileName
|
||||
});
|
||||
});
|
||||
|
||||
window.Dropbox.save({
|
||||
'files': aFiles,
|
||||
'progress': function () {
|
||||
self.saveToDropboxLoading(true);
|
||||
self.saveToDropboxError(false);
|
||||
self.saveToDropboxSuccess(false);
|
||||
},
|
||||
'cancel': function () {
|
||||
self.saveToDropboxSuccess(false);
|
||||
self.saveToDropboxError(false);
|
||||
self.saveToDropboxLoading(false);
|
||||
},
|
||||
'success': function () {
|
||||
self.saveToDropboxSuccess(true);
|
||||
self.saveToDropboxLoading(false);
|
||||
},
|
||||
'error': function () {
|
||||
self.saveToDropboxError(true);
|
||||
self.saveToDropboxLoading(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
self.saveToDropboxError(true);
|
||||
}
|
||||
}
|
||||
}).fail(function () {
|
||||
self.saveToDropboxError(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.highlightUnselectedAttachments(true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {MessageModel} oMessage
|
||||
*/
|
||||
|
|
@ -1119,7 +1137,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
MessageViewMailBoxUserView.prototype.printableCheckedMessageCount = function ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "RainLoop",
|
||||
"title": "RainLoop Webmail",
|
||||
"version": "1.8.3",
|
||||
"release": "298",
|
||||
"version": "1.8.4",
|
||||
"release": "312",
|
||||
"description": "Simple, modern & fast web-based email client",
|
||||
"homepage": "http://rainloop.net",
|
||||
"main": "gulpfile.js",
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
"plugins"
|
||||
],
|
||||
"readmeFilename": "README.md",
|
||||
"ownCloudPackageVersion": "3.2",
|
||||
"ownCloudPackageVersion": "3.3",
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
|
|
|
|||
20
plugins/vesta-change-password/LICENSE
Normal file
20
plugins/vesta-change-password/LICENSE
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 RainLoop Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
1
plugins/vesta-change-password/README
Normal file
1
plugins/vesta-change-password/README
Normal file
|
|
@ -0,0 +1 @@
|
|||
Plugin that adds functionality to change the email account password (Vesta Control Panel).
|
||||
1
plugins/vesta-change-password/VERSION
Normal file
1
plugins/vesta-change-password/VERSION
Normal file
|
|
@ -0,0 +1 @@
|
|||
1.0
|
||||
146
plugins/vesta-change-password/VestaChangePasswordDriver.php
Normal file
146
plugins/vesta-change-password/VestaChangePasswordDriver.php
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
class VestaChangePasswordDriver implements \RainLoop\Providers\ChangePassword\ChangePasswordInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sHost = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $iPort = 8083;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sAllowedEmails = '';
|
||||
|
||||
/**
|
||||
* @var \MailSo\Log\Logger
|
||||
*/
|
||||
private $oLogger = null;
|
||||
|
||||
/**
|
||||
* @param string $sHost
|
||||
* @param int $iPort
|
||||
*
|
||||
* @return \VestaChangePasswordDriver
|
||||
*/
|
||||
public function SetConfig($sHost, $iPort)
|
||||
{
|
||||
$this->sHost = $sHost;
|
||||
$this->iPort = $iPort;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sAllowedEmails
|
||||
*
|
||||
* @return \VestaChangePasswordDriver
|
||||
*/
|
||||
public function SetAllowedEmails($sAllowedEmails)
|
||||
{
|
||||
$this->sAllowedEmails = $sAllowedEmails;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \MailSo\Log\Logger $oLogger
|
||||
*
|
||||
* @return \VestaChangePasswordDriver
|
||||
*/
|
||||
public function SetLogger($oLogger)
|
||||
{
|
||||
if ($oLogger instanceof \MailSo\Log\Logger)
|
||||
{
|
||||
$this->oLogger = $oLogger;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Account $oAccount
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function PasswordChangePossibility($oAccount)
|
||||
{
|
||||
return $oAccount && $oAccount->Email() &&
|
||||
\RainLoop\Plugins\Helper::ValidateWildcardValues($oAccount->Email(), $this->sAllowedEmails);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Account $oAccount
|
||||
* @param string $sPrevPassword
|
||||
* @param string $sNewPassword
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword)
|
||||
{
|
||||
if ($this->oLogger)
|
||||
{
|
||||
$this->oLogger->Write('Vesta: Try to change password for '.$oAccount->Email());
|
||||
}
|
||||
|
||||
$bResult = false;
|
||||
if (!empty($this->sHost) && 0 < $this->iPort && $oAccount)
|
||||
{
|
||||
$sEmail = \trim(\strtolower($oAccount->Email()));
|
||||
|
||||
$sHost = \trim($this->sHost);
|
||||
$sHost = \str_replace('{user:host-imap}', $oAccount->Domain()->IncHost(), $sHost);
|
||||
$sHost = \str_replace('{user:host-smtp}', $oAccount->Domain()->OutHost(), $sHost);
|
||||
$sHost = \str_replace('{user:domain}', \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sHost);
|
||||
$sHost = \rtrim($this->sHost, '/\\');
|
||||
$sHost = 'https://'.$sHost;
|
||||
|
||||
$sUrl = $sHost.':'.$this->iPort.'/reset/mail/';
|
||||
|
||||
$iCode = 0;
|
||||
$oHttp = \MailSo\Base\Http::SingletonInstance();
|
||||
|
||||
if ($this->oLogger)
|
||||
{
|
||||
$this->oLogger->Write('Vesta[Api Request]:'.$sUrl);
|
||||
}
|
||||
|
||||
$mResult = $oHttp->SendPostRequest($sUrl,
|
||||
array(
|
||||
'email' => $sEmail,
|
||||
'password' => $sPrevPassword,
|
||||
'new' => $sNewPassword,
|
||||
), 'MailSo Http User Agent (v1)', $iCode, $this->oLogger);
|
||||
|
||||
if (false !== $mResult && 200 === $iCode)
|
||||
{
|
||||
$aRes = null;
|
||||
@\parse_str($mResult, $aRes);
|
||||
if (is_array($aRes) && (!isset($aRes['error']) || (int) $aRes['error'] !== 1))
|
||||
{
|
||||
$bResult = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->oLogger)
|
||||
{
|
||||
$this->oLogger->Write('Vesta[Error]: Response: '.$mResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->oLogger)
|
||||
{
|
||||
$this->oLogger->Write('Vesta[Error]: Empty Response: Code:'.$iCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
}
|
||||
55
plugins/vesta-change-password/index.php
Normal file
55
plugins/vesta-change-password/index.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
class VestaChangePasswordPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
{
|
||||
public function Init()
|
||||
{
|
||||
$this->addHook('main.fabrica', 'MainFabrica');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sName
|
||||
* @param mixed $oProvider
|
||||
*/
|
||||
public function MainFabrica($sName, &$oProvider)
|
||||
{
|
||||
switch ($sName)
|
||||
{
|
||||
case 'change-password':
|
||||
|
||||
$sHost = \trim($this->Config()->Get('plugin', 'vesta_host', ''));
|
||||
$iPort = (int) $this->Config()->Get('plugin', 'vesta_port', 8083);
|
||||
|
||||
if (!empty($sHost) && 0 < $iPort)
|
||||
{
|
||||
include_once __DIR__.'/VestaChangePasswordDriver.php';
|
||||
|
||||
$oProvider = new VestaChangePasswordDriver();
|
||||
$oProvider->SetLogger($this->Manager()->Actions()->Logger());
|
||||
$oProvider->SetConfig($sHost, $iPort);
|
||||
$oProvider->SetAllowedEmails(\strtolower(\trim($this->Config()->Get('plugin', 'allowed_emails', ''))));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function configMapping()
|
||||
{
|
||||
return array(
|
||||
\RainLoop\Plugins\Property::NewInstance('vesta_host')->SetLabel('Vesta Host')
|
||||
->SetDefaultValue('')
|
||||
->SetDescription('Allowed patterns: {user:host-imap}, {user:host-smtp}, {user:domain}'),
|
||||
\RainLoop\Plugins\Property::NewInstance('Vesta_port')->SetLabel('Vesta Port')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::INT)
|
||||
->SetDefaultValue(8083),
|
||||
\RainLoop\Plugins\Property::NewInstance('allowed_emails')->SetLabel('Allowed emails')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT)
|
||||
->SetDescription('Allowed emails, space as delimiter, wildcard supported. Example: user1@domain1.net user2@domain1.net *@domain2.net')
|
||||
->SetDefaultValue('*')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -34,4 +34,5 @@ class Charset
|
|||
const ISO_8859_8 = 'iso-8859-8';
|
||||
const ISO_8859_8_I = 'iso-8859-8-i';
|
||||
const ISO_2022_JP = 'iso-2022-jp';
|
||||
const CP858 = 'cp858';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -567,6 +567,12 @@ class Http
|
|||
return false;
|
||||
}
|
||||
|
||||
$sUrl = \trim($sUrl);
|
||||
if ('//' === substr($sUrl, 0, 2))
|
||||
{
|
||||
$sUrl = 'http:'.$sUrl;
|
||||
}
|
||||
|
||||
$aOptions = array(
|
||||
CURLOPT_URL => $sUrl,
|
||||
CURLOPT_HEADER => false,
|
||||
|
|
@ -679,10 +685,10 @@ class Http
|
|||
{
|
||||
if ($bSetCacheHeader)
|
||||
{
|
||||
\header('Cache-Control: public', true);
|
||||
\header('Pragma: public', true);
|
||||
\header('Last-Modified: '.\gmdate('D, d M Y H:i:s', $iUtcTimeStamp - $iExpireTime).' UTC', true);
|
||||
\header('Expires: '.\gmdate('D, j M Y H:i:s', $iUtcTimeStamp + $iExpireTime).' UTC', true);
|
||||
@\header('Cache-Control: public', true);
|
||||
@\header('Pragma: public', true);
|
||||
@\header('Last-Modified: '.\gmdate('D, d M Y H:i:s', $iUtcTimeStamp - $iExpireTime).' UTC', true);
|
||||
@\header('Expires: '.\gmdate('D, j M Y H:i:s', $iUtcTimeStamp + $iExpireTime).' UTC', true);
|
||||
|
||||
if (0 < strlen($sEtag))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -235,6 +235,56 @@ END;
|
|||
return $sCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sFilePath
|
||||
* @param function $fFileExistsCallback = null
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function SmartFileExists($sFilePath, $fFileExistsCallback = null)
|
||||
{
|
||||
$sFilePath = \str_replace('\\', '/', \trim($sFilePath));
|
||||
if (!$fFileExistsCallback)
|
||||
{
|
||||
$fFileExistsCallback = function ($sPath) {
|
||||
return \file_exists($sPath);
|
||||
};
|
||||
}
|
||||
|
||||
if (!\call_user_func($fFileExistsCallback, $sFilePath))
|
||||
{
|
||||
return $sFilePath;
|
||||
}
|
||||
|
||||
$aFileInfo = \pathinfo($sFilePath);
|
||||
|
||||
$iIndex = 0;
|
||||
|
||||
do
|
||||
{
|
||||
$iIndex++;
|
||||
|
||||
$sFilePathNew = $aFileInfo['dirname'].'/'.
|
||||
\preg_replace('/\(\d{1,2}\)$/', '', $aFileInfo['filename']).
|
||||
' ('.$iIndex.')'.
|
||||
(empty($aFileInfo['extension']) ? '' : '.'.$aFileInfo['extension'])
|
||||
;
|
||||
|
||||
if (!\call_user_func($fFileExistsCallback, $sFilePathNew))
|
||||
{
|
||||
$sFilePath = $sFilePathNew;
|
||||
break;
|
||||
}
|
||||
else if (10 < $iIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
|
||||
return $sFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ abstract class Driver
|
|||
$this->bTypedPrefix = true;
|
||||
$this->bGuidPrefix = true;
|
||||
|
||||
$this->iTimeOffset = 0;
|
||||
|
||||
$this->iWriteOnTimeoutOnly = 0;
|
||||
$this->bWriteOnErrorOnly = false;
|
||||
$this->bWriteOnPhpErrorOnly = false;
|
||||
|
|
@ -106,6 +108,17 @@ abstract class Driver
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iTimeOffset
|
||||
*
|
||||
* @return \MailSo\Log\Driver
|
||||
*/
|
||||
public function SetTimeOffset($iTimeOffset)
|
||||
{
|
||||
$this->iTimeOffset = $iTimeOffset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \MailSo\Log\Driver
|
||||
*/
|
||||
|
|
@ -217,7 +230,7 @@ abstract class Driver
|
|||
protected function getTimeWithMicroSec()
|
||||
{
|
||||
$aMicroTimeItems = \explode(' ', \microtime());
|
||||
return \gmdate($this->sDatePattern, $aMicroTimeItems[1]).'.'.
|
||||
return \MailSo\Log\Logger::DateHelper($this->sDatePattern, $this->iTimeOffset, $aMicroTimeItems[1]).'.'.
|
||||
\str_pad((int) ($aMicroTimeItems[0] * 1000), 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +260,7 @@ abstract class Driver
|
|||
if (!$this->bFlushCache && ($this->bWriteOnErrorOnly || $this->bWriteOnPhpErrorOnly || 0 < $this->iWriteOnTimeoutOnly))
|
||||
{
|
||||
$bErrorPhp = false;
|
||||
|
||||
|
||||
$bError = $this->bWriteOnErrorOnly && \in_array($iType, array(
|
||||
\MailSo\Log\Enumerations\Type::NOTICE,
|
||||
\MailSo\Log\Enumerations\Type::NOTICE_PHP,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,21 @@ class Logger extends \MailSo\Base\Collection
|
|||
return $oInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sFormat
|
||||
* @param int $iTimeOffset = 0
|
||||
* @param int $iTimestamp = 0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function DateHelper($sFormat, $iTimeOffset = 0, $iTimestamp = null)
|
||||
{
|
||||
$iTimestamp = null === $iTimestamp ? \time() : (int) $iTimestamp;
|
||||
$iTimeOffset = (int) $iTimeOffset;
|
||||
|
||||
return \gmdate($sFormat, $iTimestamp + $iTimeOffset * 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -58,62 +58,17 @@ class AttachmentCollection extends \MailSo\Base\Collection
|
|||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @return array
|
||||
*/
|
||||
public function ImageCount()
|
||||
public function SpecData()
|
||||
{
|
||||
$aList = $this->FilterList(function ($oAttachment) {
|
||||
return $oAttachment && $oAttachment->IsImage();
|
||||
return $this->MapList(function ($oAttachment) {
|
||||
if ($oAttachment)
|
||||
{
|
||||
return array($oAttachment->FileName(true), $oAttachment->MimeType());
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return \is_array($aList) ? \count($aList) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function ArchiveCount()
|
||||
{
|
||||
$aList = $this->FilterList(function ($oAttachment) {
|
||||
return $oAttachment && $oAttachment->IsArchive();
|
||||
});
|
||||
|
||||
return \is_array($aList) ? \count($aList) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function PdfCount()
|
||||
{
|
||||
$aList = $this->FilterList(function ($oAttachment) {
|
||||
return $oAttachment && $oAttachment->IsPdf();
|
||||
});
|
||||
|
||||
return \is_array($aList) ? \count($aList) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function DocCount()
|
||||
{
|
||||
$aList = $this->FilterList(function ($oAttachment) {
|
||||
return $oAttachment && $oAttachment->IsDoc();
|
||||
});
|
||||
|
||||
return \is_array($aList) ? \count($aList) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function CertificateCount()
|
||||
{
|
||||
$aList = $this->FilterList(function ($oAttachment) {
|
||||
return $oAttachment && $oAttachment->IsPgpSignature();
|
||||
});
|
||||
|
||||
return \is_array($aList) ? \count($aList) : 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1911,6 +1911,89 @@ class MailClient
|
|||
return $oMessageCollection->GetAsArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \MailSo\Cache\CacheClient|null $oCacher
|
||||
* @param string $sSearch
|
||||
* @param string $sFolderName
|
||||
* @param string $sFolderHash
|
||||
* @param bool $bUseSortIfSupported = false
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \MailSo\Base\Exceptions\InvalidArgumentException
|
||||
* @throws \MailSo\Net\Exceptions\Exception
|
||||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
public function GetUids($oCacher, $sSearch, $sFolderName, $sFolderHash, $bUseSortIfSupported = false)
|
||||
{
|
||||
$aResultUids = false;
|
||||
$bUidsFromCacher = false;
|
||||
$bUseCacheAfterSearch = true;
|
||||
|
||||
$sSerializedHash = '';
|
||||
$sSerializedLog = '';
|
||||
|
||||
$bUseSortIfSupported = $bUseSortIfSupported ? !!$this->oImapClient->IsSupported('SORT') : false;
|
||||
|
||||
if (0 < \strlen($sSearch))
|
||||
{
|
||||
$bUseSortIfSupported = false;
|
||||
}
|
||||
|
||||
$sSearchCriterias = $this->getImapSearchCriterias($sSearch, 0, $bUseCacheAfterSearch);
|
||||
if ($bUseCacheAfterSearch && $oCacher && $oCacher->IsInited())
|
||||
{
|
||||
$sSerializedHash = 'GetUids/'.
|
||||
($bUseSortIfSupported ? 'S': 'N').'/'.
|
||||
$this->GenerateImapClientHash().'/'.
|
||||
$sFolderName.'/'.$sSearchCriterias;
|
||||
|
||||
$sSerializedLog = '"'.$sFolderName.'" / '.$sSearchCriterias.'';
|
||||
|
||||
$sSerialized = $oCacher->Get($sSerializedHash);
|
||||
if (!empty($sSerialized))
|
||||
{
|
||||
$aSerialized = @\json_decode($sSerialized, true);
|
||||
if (\is_array($aSerialized) && isset($aSerialized['FolderHash'], $aSerialized['Uids']) &&
|
||||
$sFolderHash === $aSerialized['FolderHash'] &&
|
||||
\is_array($aSerialized['Uids'])
|
||||
)
|
||||
{
|
||||
if ($this->oLogger)
|
||||
{
|
||||
$this->oLogger->Write('Get Serialized UIDS from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']');
|
||||
}
|
||||
|
||||
$aResultUids = $aSerialized['Uids'];
|
||||
$bUidsFromCacher = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!\is_array($aResultUids))
|
||||
{
|
||||
$aResultUids = $bUseSortIfSupported ?
|
||||
$this->oImapClient->MessageSimpleSort(array('REVERSE ARRIVAL'), $sSearchCriterias, true) :
|
||||
$this->oImapClient->MessageSimpleSearch($sSearchCriterias, true)
|
||||
;
|
||||
|
||||
if (!$bUidsFromCacher && $bUseCacheAfterSearch && \is_array($aResultUids) && $oCacher && $oCacher->IsInited() && 0 < \strlen($sSerializedHash))
|
||||
{
|
||||
$oCacher->Set($sSerializedHash, @\json_encode(array(
|
||||
'FolderHash' => $sFolderHash,
|
||||
'Uids' => $aResultUids
|
||||
)));
|
||||
|
||||
if ($this->oLogger)
|
||||
{
|
||||
$this->oLogger->Write('Save Serialized UIDS to cache ('.$sSerializedLog.') [count:'.\count($aResultUids).']');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aResultUids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sFolderName
|
||||
* @param int $iOffset = 0
|
||||
|
|
@ -1921,6 +2004,7 @@ class MailClient
|
|||
* @param bool $bUseSortIfSupported = false
|
||||
* @param bool $bUseThreadSortIfSupported = false
|
||||
* @param bool $bUseESearchOrESortRequest = false
|
||||
* @param string $sThreadUid = ''
|
||||
*
|
||||
* @return \MailSo\Mail\MessageCollection
|
||||
*
|
||||
|
|
@ -1929,7 +2013,7 @@ class MailClient
|
|||
* @throws \MailSo\Imap\Exceptions\Exception
|
||||
*/
|
||||
public function MessageList($sFolderName, $iOffset = 0, $iLimit = 10, $sSearch = '', $sPrevUidNext = '', $oCacher = null,
|
||||
$bUseSortIfSupported = false, $bUseThreadSortIfSupported = false, $bUseESearchOrESortRequest = false)
|
||||
$bUseSortIfSupported = false, $bUseThreadSortIfSupported = false, $sThreadUid = '')
|
||||
{
|
||||
$sSearch = \trim($sSearch);
|
||||
if (!\MailSo\Base\Validator::RangeInt($iOffset, 0) ||
|
||||
|
|
@ -1945,10 +2029,14 @@ class MailClient
|
|||
$oMessageCollection->Offset = $iOffset;
|
||||
$oMessageCollection->Limit = $iLimit;
|
||||
$oMessageCollection->Search = $sSearch;
|
||||
$oMessageCollection->ThreadUid = $sThreadUid;
|
||||
|
||||
$aThreads = array();
|
||||
$aUids = array();
|
||||
$mAllSortedUids = null;
|
||||
$mAllThreads = null;
|
||||
|
||||
$iThreadUid = empty($sThreadUid) ? 0 : (int) $sThreadUid;
|
||||
|
||||
$iMessageCount = 0;
|
||||
$iMessageRealCount = 0;
|
||||
$iMessageUnseenCount = 0;
|
||||
$sUidNext = '0';
|
||||
|
|
@ -1958,17 +2046,26 @@ class MailClient
|
|||
$bUseThreadSortIfSupported = $bUseThreadSortIfSupported ?
|
||||
($this->oImapClient->IsSupported('THREAD=REFS') || $this->oImapClient->IsSupported('THREAD=REFERENCES') || $this->oImapClient->IsSupported('THREAD=ORDEREDSUBJECT')) : false;
|
||||
|
||||
if (!empty($sThreadUid) && !$bUseThreadSortIfSupported)
|
||||
{
|
||||
throw new \MailSo\Base\Exceptions\InvalidArgumentException();
|
||||
}
|
||||
|
||||
if (!$oCacher || !($oCacher instanceof \MailSo\Cache\CacheClient))
|
||||
{
|
||||
$oCacher = null;
|
||||
}
|
||||
|
||||
$this->initFolderValues($sFolderName, $iMessageRealCount, $iMessageUnseenCount, $sUidNext);
|
||||
$iMessageCount = $iMessageRealCount;
|
||||
|
||||
$oMessageCollection->FolderHash = $this->GenerateFolderHash($sFolderName, $iMessageRealCount, $iMessageUnseenCount, $sUidNext);
|
||||
$oMessageCollection->UidNext = $sUidNext;
|
||||
$oMessageCollection->NewMessages = $this->getFolderNextMessageInformation($sFolderName, $sPrevUidNext, $sUidNext);
|
||||
|
||||
if (empty($sThreadUid) && 0 < \strlen($sPrevUidNext) && 'INBOX' === $sFolderName)
|
||||
{
|
||||
$oMessageCollection->NewMessages = $this->getFolderNextMessageInformation(
|
||||
$sFolderName, $sPrevUidNext, $sUidNext);
|
||||
}
|
||||
|
||||
$bSearch = false;
|
||||
$bMessageListOptimization = 0 < \MailSo\Config::$MessageListCountLimitTrigger &&
|
||||
|
|
@ -1978,98 +2075,119 @@ class MailClient
|
|||
{
|
||||
$bUseSortIfSupported = false;
|
||||
$bUseThreadSortIfSupported = false;
|
||||
$bUseESearchOrESortRequest = false;
|
||||
}
|
||||
|
||||
if (0 < $iMessageRealCount)
|
||||
{
|
||||
$bIndexAsUid = false;
|
||||
$aIndexOrUids = array();
|
||||
$mAllSortedUids = $this->GetUids($oCacher, '',
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash, $bUseSortIfSupported);
|
||||
|
||||
if (0 < \strlen($sSearch))
|
||||
{
|
||||
$aIndexOrUids = $this->getSearchUidsResult($sSearch,
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash,
|
||||
$bUseSortIfSupported, $bUseESearchOrESortRequest, $oCacher);
|
||||
$mAllThreads = $bUseThreadSortIfSupported ? $this->MessageListThreadsMap(
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash, $mAllSortedUids, $oCacher) : null;
|
||||
|
||||
$bIndexAsUid = true;
|
||||
}
|
||||
else
|
||||
if ($bUseThreadSortIfSupported && 0 < $iThreadUid && \is_array($mAllThreads))
|
||||
{
|
||||
if ($bUseThreadSortIfSupported && 1 < $iMessageCount)
|
||||
$aUids = array();
|
||||
$iResultRootUid = 0;
|
||||
|
||||
if (isset($mAllThreads[$iThreadUid]))
|
||||
{
|
||||
$aIndexOrUids = $this->getSearchUidsResult('',
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash,
|
||||
$bUseSortIfSupported, $bUseESearchOrESortRequest, $oCacher);
|
||||
|
||||
$aThreads = $this->MessageListThreadsMap(
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash,
|
||||
$aIndexOrUids, $oCacher);
|
||||
|
||||
$aNewIndexOrUids = array();
|
||||
foreach ($aIndexOrUids as $iUid)
|
||||
$iResultRootUid = $iThreadUid;
|
||||
if (\is_array($mAllThreads[$iThreadUid]))
|
||||
{
|
||||
if (isset($aThreads[$iUid]))
|
||||
{
|
||||
$aNewIndexOrUids[] = $iUid;
|
||||
}
|
||||
$aUids = $mAllThreads[$iThreadUid];
|
||||
}
|
||||
|
||||
$aIndexOrUids = $aNewIndexOrUids;
|
||||
unset($aNewIndexOrUids);
|
||||
|
||||
$iMessageCount = \count($aIndexOrUids);
|
||||
$bIndexAsUid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aIndexOrUids = array(1);
|
||||
$bIndexAsUid = false;
|
||||
|
||||
if (1 < $iMessageCount)
|
||||
foreach ($mAllThreads as $iRootUid => $mSubUids)
|
||||
{
|
||||
if (0 === \MailSo\Config::$MessageListDateFilter &&
|
||||
($bMessageListOptimization || !$bUseSortIfSupported))
|
||||
if (\is_array($mSubUids) && \in_array($iThreadUid, $mSubUids))
|
||||
{
|
||||
$aIndexOrUids = \array_reverse(\range(1, $iMessageCount));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aIndexOrUids = $this->getSearchUidsResult('',
|
||||
$oMessageCollection->FolderName, $oMessageCollection->FolderHash,
|
||||
$bUseSortIfSupported, $bUseESearchOrESortRequest, $oCacher);
|
||||
|
||||
$bIndexAsUid = true;
|
||||
$iResultRootUid = $iRootUid;
|
||||
$aUids = $mSubUids;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < $iResultRootUid && \in_array($iResultRootUid, $mAllSortedUids))
|
||||
{
|
||||
\array_unshift($aUids, $iResultRootUid);
|
||||
}
|
||||
}
|
||||
else if ($bUseThreadSortIfSupported && \is_array($mAllThreads))
|
||||
{
|
||||
$aUids = \array_keys($mAllThreads);
|
||||
}
|
||||
else
|
||||
{
|
||||
$bUseThreadSortIfSupported = false;
|
||||
$aUids = $mAllSortedUids;
|
||||
}
|
||||
|
||||
if (\is_array($aIndexOrUids))
|
||||
if (0 < \strlen($sSearch) && \is_array($aUids))
|
||||
{
|
||||
$aSearchedUids = $this->GetUids($oCacher, $sSearch, $oMessageCollection->FolderName, $oMessageCollection->FolderHash);
|
||||
if (\is_array($aSearchedUids) && 0 < \count($aSearchedUids))
|
||||
{
|
||||
$aFlippedSearchedUids = \array_flip($aSearchedUids);
|
||||
|
||||
$bSearch = true;
|
||||
$aNewUids = array();
|
||||
|
||||
foreach ($aUids as $iUid)
|
||||
{
|
||||
if (isset($aFlippedSearchedUids[$iUid]))
|
||||
{
|
||||
$aNewUids[] = $iUid;
|
||||
}
|
||||
else if ($bUseThreadSortIfSupported && 0 === $iThreadUid && isset($mAllThreads[$iUid]) && \is_array($mAllThreads[$iUid]))
|
||||
{
|
||||
foreach ($mAllThreads[$iUid] as $iSubUid)
|
||||
{
|
||||
if (isset($aFlippedSearchedUids[$iSubUid]))
|
||||
{
|
||||
$aNewUids[] = $iUid;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aUids = \array_unique($aNewUids);
|
||||
unset($aNewUids);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aUids = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_array($aUids))
|
||||
{
|
||||
$oMessageCollection->MessageCount = $iMessageRealCount;
|
||||
$oMessageCollection->MessageUnseenCount = $iMessageUnseenCount;
|
||||
$oMessageCollection->MessageResultCount = 0 === \strlen($sSearch)
|
||||
? $iMessageCount : \count($aIndexOrUids);
|
||||
$oMessageCollection->MessageResultCount = \count($aUids);
|
||||
|
||||
if (0 < \count($aIndexOrUids))
|
||||
if (0 < \count($aUids))
|
||||
{
|
||||
$iOffset = (0 > $iOffset) ? 0 : $iOffset;
|
||||
$aRequestIndexOrUids = \array_slice($aIndexOrUids, $iOffset, $iLimit);
|
||||
$aRequestUids = \array_slice($aUids, $iOffset, $iLimit);
|
||||
|
||||
$this->MessageListByRequestIndexOrUids($oMessageCollection, $aRequestIndexOrUids, $bIndexAsUid);
|
||||
$this->MessageListByRequestIndexOrUids($oMessageCollection, $aRequestUids, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bSearch && $bUseThreadSortIfSupported && 0 < \count($aThreads))
|
||||
if ($bUseThreadSortIfSupported && 0 === $iThreadUid && \is_array($mAllThreads) && 0 < \count($mAllThreads))
|
||||
{
|
||||
$oMessageCollection->ForeachList(function (/* @var $oMessage \MailSo\Mail\Message */ $oMessage) use ($aThreads) {
|
||||
$oMessageCollection->ForeachList(function (/* @var $oMessage \MailSo\Mail\Message */ $oMessage) use ($mAllThreads) {
|
||||
|
||||
$iUid = $oMessage->Uid();
|
||||
if (isset($aThreads[$iUid]) && \is_array($aThreads[$iUid]) && 0 < \count($aThreads[$iUid]))
|
||||
if (isset($mAllThreads[$iUid]) && \is_array($mAllThreads[$iUid]) && 0 < \count($mAllThreads[$iUid]))
|
||||
{
|
||||
$aSubThreads = $aThreads[$iUid];
|
||||
$aSubThreads = $mAllThreads[$iUid];
|
||||
\array_unshift($aSubThreads, $iUid);
|
||||
|
||||
$oMessage->SetThreads(\array_map('trim', $aSubThreads));
|
||||
|
|
@ -2263,7 +2381,10 @@ class MailClient
|
|||
{
|
||||
$aSubscribedFolders = $this->oImapClient->FolderSubscribeList($sParent, $sListPattern);
|
||||
}
|
||||
catch (\Exception $oException) {}
|
||||
catch (\Exception $oException)
|
||||
{
|
||||
unset($oException);
|
||||
}
|
||||
}
|
||||
|
||||
$aImapSubscribedFoldersHelper = null;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ class MessageCollection extends \MailSo\Base\Collection
|
|||
*/
|
||||
public $UidNext;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $ThreadUid;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
|
@ -108,8 +113,9 @@ class MessageCollection extends \MailSo\Base\Collection
|
|||
$this->Limit = 0;
|
||||
$this->Search = '';
|
||||
$this->UidNext = '';
|
||||
$this->ThreadUid = '';
|
||||
$this->NewMessages = array();
|
||||
|
||||
|
||||
$this->LastCollapsedThreadUids = array();
|
||||
|
||||
return $this;
|
||||
|
|
|
|||
|
|
@ -407,8 +407,9 @@ class Actions
|
|||
{
|
||||
if (false !== \strpos($sLine, '{date:'))
|
||||
{
|
||||
$sLine = \preg_replace_callback('/\{date:([^}]+)\}/', function ($aMatch) {
|
||||
return \gmdate($aMatch[1]);
|
||||
$iTimeOffset = (int) $this->Config()->Get('logs', 'time_offset', 0);
|
||||
$sLine = \preg_replace_callback('/\{date:([^}]+)\}/', function ($aMatch) use ($iTimeOffset) {
|
||||
return \MailSo\Log\Logger::DateHelper($aMatch[1], $iTimeOffset);
|
||||
}, $sLine);
|
||||
|
||||
$sLine = \preg_replace('/\{date:([^}]*)\}/', 'date', $sLine);
|
||||
|
|
@ -939,6 +940,8 @@ class Actions
|
|||
|
||||
if (!!$this->Config()->Get('logs', 'enable', false))
|
||||
{
|
||||
$iTimeOffset = (int) $this->Config()->Get('logs', 'time_offset', 0);
|
||||
|
||||
$this->oLogger->SetShowSecter(!$this->Config()->Get('logs', 'hide_passwords', true));
|
||||
|
||||
$sLogFileFullPath = \APP_PRIVATE_DATA.'logs/'.$this->compileLogFileName(
|
||||
|
|
@ -956,6 +959,7 @@ class Actions
|
|||
->WriteOnErrorOnly($this->Config()->Get('logs', 'write_on_error_only', false))
|
||||
->WriteOnPhpErrorOnly($this->Config()->Get('logs', 'write_on_php_error_only', false))
|
||||
->WriteOnTimeoutOnly($this->Config()->Get('logs', 'write_on_timeout_only', 0))
|
||||
->SetTimeOffset($iTimeOffset)
|
||||
);
|
||||
|
||||
if (!$this->Config()->Get('debug', 'enable', false))
|
||||
|
|
@ -967,7 +971,10 @@ class Actions
|
|||
|
||||
$oHttp = $this->Http();
|
||||
|
||||
$this->oLogger->Write('[DATE:'.\gmdate('d.m.y').'][RL:'.APP_VERSION.'][PHP:'.PHP_VERSION.'][IP:'.
|
||||
$this->oLogger->Write('[DATE:'.\MailSo\Log\Logger::DateHelper('d.m.y', $iTimeOffset).
|
||||
(0 !== $iTimeOffset ? '][OFFSET:'.(0 < $iTimeOffset ? '+' : '-').
|
||||
\str_pad((string) \abs($iTimeOffset), 2, '0', STR_PAD_LEFT) : '').
|
||||
'][RL:'.APP_VERSION.'][PHP:'.PHP_VERSION.'][IP:'.
|
||||
$oHttp->GetClientIp($this->Config()->Get('labs', 'http_client_ip_check_proxy', false)).'][PID:'.
|
||||
(\MailSo\Base\Utils::FunctionExistsAndEnabled('getmypid') ? \getmypid() : 'unknown').']['.
|
||||
$oHttp->GetServer('SERVER_SOFTWARE', '~').']['.
|
||||
|
|
@ -1315,12 +1322,34 @@ class Actions
|
|||
'AllowAppendMessage' => (bool) $oConfig->Get('labs', 'allow_message_append', false),
|
||||
'MaterialDesign' => (bool) $oConfig->Get('labs', 'use_material_design', true),
|
||||
'PremType' => $this->PremType(),
|
||||
'ZipSupported' => !!\class_exists('ZipArchive'),
|
||||
'Admin' => array(),
|
||||
'Capa' => array(),
|
||||
'AttahcmentsActions' => array(),
|
||||
'Plugins' => array()
|
||||
);
|
||||
|
||||
if ($oConfig->Get('capa', 'attachments_actions', false))
|
||||
{
|
||||
if (!!\class_exists('ZipArchive'))
|
||||
{
|
||||
$aResult['AttahcmentsActions'][] = 'zip';
|
||||
}
|
||||
|
||||
if (\RainLoop\Utils::IsOwnCloudLoggedIn() && \class_exists('\\OCP\\Files'))
|
||||
{
|
||||
$aResult['AttahcmentsActions'][] = 'owncloud';
|
||||
}
|
||||
|
||||
if ($oConfig->Get('social', 'dropbox_enable', false) && 0 < \strlen(\trim($oConfig->Get('social', 'dropbox_api_key', ''))))
|
||||
{
|
||||
$aResult['AttahcmentsActions'][] = 'dropbox';
|
||||
}
|
||||
}
|
||||
|
||||
$aResult['AllowDropboxSocial'] = (bool) $oConfig->Get('social', 'dropbox_enable', false);
|
||||
$aResult['DropboxApiKey'] = \trim($oConfig->Get('social', 'dropbox_api_key', ''));
|
||||
|
||||
|
||||
if ($aResult['UseRsaEncryption'] &&
|
||||
\file_exists(APP_PRIVATE_DATA.'rsa/public') && \file_exists(APP_PRIVATE_DATA.'rsa/private'))
|
||||
{
|
||||
|
|
@ -1709,8 +1738,9 @@ class Actions
|
|||
$aResult['LanguageAdmin'] = $this->ValidateLanguage($sLanguageAdmin, '', true);
|
||||
|
||||
$aResult['UserLanguageRaw'] = $this->detectUserLanguage();
|
||||
$aResult['UserLanguage'] = $this->ValidateLanguage($aResult['UserLanguageRaw'], $aResult['Language'], false);
|
||||
$aResult['UserLanguageAdmin'] = $this->ValidateLanguage($aResult['UserLanguageRaw'], $aResult['LanguageAdmin'], true);
|
||||
|
||||
$aResult['UserLanguage'] = $this->ValidateLanguage($aResult['UserLanguageRaw'], '', false, true, true);
|
||||
$aResult['UserLanguageAdmin'] = $this->ValidateLanguage($aResult['UserLanguageRaw'], '', true, true, true);
|
||||
|
||||
$aResult['LangLink'] = './?/Lang/0/'.($bAdmin ? 'Admin' : 'App').'/'.
|
||||
($bAdmin ? $aResult['LanguageAdmin'] : $aResult['Language']).'/'.$sStaticCache.'/';
|
||||
|
|
@ -1755,14 +1785,30 @@ class Actions
|
|||
return \preg_replace('/[^a-zA-Z0-9]+/', '-', $this->getUserLanguageFromHeader());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iWait = 1
|
||||
* @param int $iDelay = 1
|
||||
*/
|
||||
private function requestSleep($iWait = 1, $iDelay = 1)
|
||||
{
|
||||
if (0 < $iDelay && 0 < $iWait)
|
||||
{
|
||||
if ($iWait > \time() - APP_START_TIME)
|
||||
{
|
||||
\sleep($iDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function loginErrorDelay()
|
||||
{
|
||||
$iDelay = (int) $this->Config()->Get('labs', 'login_fault_delay', 0);
|
||||
if (0 < $iDelay)
|
||||
{
|
||||
\sleep($iDelay);
|
||||
$this->requestSleep(1, $iDelay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Model\Account $oAccount
|
||||
*/
|
||||
|
|
@ -2630,6 +2676,209 @@ class Actions
|
|||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* @throws \MailSo\Base\Exceptions\Exception
|
||||
*/
|
||||
public function DoAttachmentsActions()
|
||||
{
|
||||
if (!$this->Config()->Get('capa', 'attachments_actions', false))
|
||||
{
|
||||
return $this->FalseResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
$sAction = $this->GetActionParam('Do', '');
|
||||
$aHashes = $this->GetActionParam('Hashes', null);
|
||||
|
||||
$mResult = false;
|
||||
$bError = false;
|
||||
$aData = false;
|
||||
|
||||
if (\is_array($aHashes) && 0 < \count($aHashes))
|
||||
{
|
||||
$aData = array();
|
||||
foreach ($aHashes as $sZipHash)
|
||||
{
|
||||
$aResult = $this->getMimeFileByHash($oAccount, $sZipHash);
|
||||
if (\is_array($aResult) && !empty($aResult['FileHash']))
|
||||
{
|
||||
$aData[] = $aResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oFilesProvider = $this->FilesProvider();
|
||||
if (!empty($sAction) && !$bError && \is_array($aData) && 0 < \count($aData) &&
|
||||
$oFilesProvider && $oFilesProvider->IsActive())
|
||||
{
|
||||
|
||||
$bError = false;
|
||||
switch (\strtolower($sAction))
|
||||
{
|
||||
case 'zip':
|
||||
|
||||
if (\class_exists('ZipArchive'))
|
||||
{
|
||||
$sZipHash = \MailSo\Base\Utils::Md5Rand();
|
||||
$sZipFileName = $oFilesProvider->GenerateLocalFullFileName($oAccount, $sZipHash);
|
||||
|
||||
if (!empty($sZipFileName))
|
||||
{
|
||||
$oZip = new \ZipArchive();
|
||||
$oZip->open($sZipFileName, \ZIPARCHIVE::OVERWRITE);
|
||||
$oZip->setArchiveComment('RainLoop/'.APP_VERSION);
|
||||
|
||||
foreach ($aData as $aItem)
|
||||
{
|
||||
$sFileName = (string) (isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat');
|
||||
$sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : '');
|
||||
|
||||
if (!empty($sFileHash))
|
||||
{
|
||||
$sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $sFileHash);
|
||||
if (!$oZip->addFile($sFullFileNameHash, $sFileName))
|
||||
{
|
||||
$bError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bError)
|
||||
{
|
||||
$bError = !$oZip->close();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oZip->close();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aData as $aItem)
|
||||
{
|
||||
$sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : '');
|
||||
if (!empty($sFileHash))
|
||||
{
|
||||
$oFilesProvider->Clear($oAccount, $sFileHash);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bError)
|
||||
{
|
||||
$mResult = array(
|
||||
'Files' => array(array(
|
||||
'FileName' => 'attachments.zip',
|
||||
'Hash' => \RainLoop\Utils::EncodeKeyValues(array(
|
||||
'V' => APP_VERSION,
|
||||
'Account' => $oAccount ? \md5($oAccount->Hash()) : '',
|
||||
'FileName' => 'attachments.zip',
|
||||
'MimeType' => 'application/zip',
|
||||
'FileHash' => $sZipHash
|
||||
))
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'owncloud':
|
||||
|
||||
$mResult = false;
|
||||
|
||||
if (\RainLoop\Utils::IsOwnCloudLoggedIn() && \class_exists('\\OCP\\Files'))
|
||||
{
|
||||
$sSaveFolder = $this->Config()->Get('labs', 'owncloud_save_folder', '');
|
||||
if (!empty($sSaveFolder))
|
||||
{
|
||||
$sSaveFolder = 'Attachments';
|
||||
}
|
||||
|
||||
$oFiles = \OCP\Files::getStorage('files');
|
||||
|
||||
if ($oFilesProvider && $oFiles && $oFilesProvider->IsActive() &&
|
||||
\method_exists($oFiles, 'file_put_contents'))
|
||||
{
|
||||
if (!$oFiles->is_dir($sSaveFolder))
|
||||
{
|
||||
$oFiles->mkdir($sSaveFolder);
|
||||
}
|
||||
|
||||
$mResult = true;
|
||||
foreach ($aData as $aItem)
|
||||
{
|
||||
$sSavedFileName = isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat';
|
||||
$sSavedFileHash = !empty($aItem['FileHash']) ? $aItem['FileHash'] : '';
|
||||
|
||||
if (!empty($sSavedFileHash))
|
||||
{
|
||||
$fFile = $oFilesProvider->GetFile($oAccount, $sSavedFileHash, 'rb');
|
||||
if (\is_resource($fFile))
|
||||
{
|
||||
$sSavedFileNameFull = \MailSo\Base\Utils::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, function ($sPath) use ($oFiles) {
|
||||
return $oFiles->file_exists($sPath);
|
||||
});
|
||||
|
||||
if (!$oFiles->file_put_contents($sSavedFileNameFull, $fFile))
|
||||
{
|
||||
$mResult = false;
|
||||
}
|
||||
|
||||
if (\is_resource($fFile))
|
||||
{
|
||||
@\fclose($fFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aData as $aItem)
|
||||
{
|
||||
$sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : '');
|
||||
if (!empty($sFileHash))
|
||||
{
|
||||
$oFilesProvider->Clear($oAccount, $sFileHash);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'dropbox':
|
||||
|
||||
$mResult = array(
|
||||
'ShortLife' => '_'.$this->GetShortLifeSpecAuthToken(),
|
||||
'Url' => \preg_replace('/\?(.*)$/', '', $this->Http()->GetFullUrl()),
|
||||
'Files' => array()
|
||||
);
|
||||
|
||||
foreach ($aData as $aItem)
|
||||
{
|
||||
$mResult['Files'][] = array(
|
||||
'FileName' => isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat',
|
||||
'Hash' => \RainLoop\Utils::EncodeKeyValues($aItem)
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$bError = true;
|
||||
}
|
||||
|
||||
$this->requestSleep();
|
||||
return $this->DefaultResponse(__FUNCTION__, $bError ? false : $mResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
|
|
@ -3531,7 +3780,6 @@ class Actions
|
|||
*/
|
||||
public function DoAdminLicensing()
|
||||
{
|
||||
$iStart = \time();
|
||||
$this->IsAdminLoggined();
|
||||
|
||||
$bForce = '1' === (string) $this->GetActionParam('Force', '0');
|
||||
|
|
@ -3543,10 +3791,7 @@ class Actions
|
|||
{
|
||||
$sValue = $this->licenseHelper($bForce);
|
||||
|
||||
if ($iStart === \time())
|
||||
{
|
||||
\sleep(1);
|
||||
}
|
||||
$this->requestSleep();
|
||||
|
||||
$iExpired = 0;
|
||||
if ($this->licenseParser($sValue, $iExpired))
|
||||
|
|
@ -3583,7 +3828,6 @@ class Actions
|
|||
*/
|
||||
public function DoAdminLicensingActivate()
|
||||
{
|
||||
$iStart = \time();
|
||||
$this->IsAdminLoggined();
|
||||
|
||||
$sDomain = (string) $this->GetActionParam('Domain', '');
|
||||
|
|
@ -3599,7 +3843,6 @@ class Actions
|
|||
|
||||
$oHttp = \MailSo\Base\Http::SingletonInstance();
|
||||
|
||||
\sleep(1);
|
||||
$sValue = $oHttp->GetUrlAsString(APP_API_PATH.'activate/'.\urlencode($sDomain).'/'.\urlencode($sKey),
|
||||
'RainLoop/'.APP_VERSION, $sContentType, $iCode, $this->Logger(), 10,
|
||||
$this->Config()->Get('labs', 'curl_proxy', ''), $this->Config()->Get('labs', 'curl_proxy_auth', ''),
|
||||
|
|
@ -3611,10 +3854,7 @@ class Actions
|
|||
$sValue = '';
|
||||
}
|
||||
|
||||
if ($iStart + 2 > \time())
|
||||
{
|
||||
\sleep(1);
|
||||
}
|
||||
$this->requestSleep();
|
||||
|
||||
$aMatch = array();
|
||||
if ('OK' === $sValue)
|
||||
|
|
@ -5240,12 +5480,12 @@ class Actions
|
|||
{
|
||||
$this->initMailClientConnection();
|
||||
|
||||
$sFolderNameInUtf = $this->GetActionParam('Folder', '');
|
||||
$sFolderFullNameRaw = $this->GetActionParam('Folder', '');
|
||||
$bSubscribe = '1' === (string) $this->GetActionParam('Subscribe', '0');
|
||||
|
||||
try
|
||||
{
|
||||
$this->MailClient()->FolderSubscribe($sFolderNameInUtf, !!$bSubscribe);
|
||||
$this->MailClient()->FolderSubscribe($sFolderFullNameRaw, !!$bSubscribe);
|
||||
}
|
||||
catch (\Exception $oException)
|
||||
{
|
||||
|
|
@ -5262,6 +5502,51 @@ class Actions
|
|||
return $this->TrueResponse(__FUNCTION__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function DoFolderCheckable()
|
||||
{
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
|
||||
$sFolderFullNameRaw = $this->GetActionParam('Folder', '');
|
||||
$bCheckable = '1' === (string) $this->GetActionParam('Checkable', '0');
|
||||
|
||||
$oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount);
|
||||
|
||||
$sCheckableFolder = $oSettingsLocal->GetConf('CheckableFolder', '[]');
|
||||
$aCheckableFolder = @\json_decode($sCheckableFolder);
|
||||
|
||||
if (!\is_array($aCheckableFolder))
|
||||
{
|
||||
$aCheckableFolder = array();
|
||||
}
|
||||
|
||||
if ($bCheckable)
|
||||
{
|
||||
$aCheckableFolder[] = $sFolderFullNameRaw;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCheckableFolderNew = array();
|
||||
foreach ($aCheckableFolder as $sFolder)
|
||||
{
|
||||
if ($sFolder !== $sFolderFullNameRaw)
|
||||
{
|
||||
$aCheckableFolderNew[] = $sFolder;
|
||||
}
|
||||
}
|
||||
$aCheckableFolder = $aCheckableFolderNew;
|
||||
}
|
||||
|
||||
$aCheckableFolder = \array_unique($aCheckableFolder);
|
||||
|
||||
$oSettingsLocal->SetConf('CheckableFolder', @\json_encode($aCheckableFolder));
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__,
|
||||
$this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
|
|
@ -5443,45 +5728,6 @@ class Actions
|
|||
return $this->DefaultResponse(__FUNCTION__, $aResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* @throws \MailSo\Base\Exceptions\Exception
|
||||
*/
|
||||
public function DoMessageThreadsFromCache()
|
||||
{
|
||||
$sFolder = $this->GetActionParam('Folder', '');
|
||||
$sFolderHash = $this->GetActionParam('FolderHash', '');
|
||||
$sUid = (string) $this->GetActionParam('Uid', '');
|
||||
|
||||
if (0 === \strlen($sFolder) || 0 === \strlen($sUid))
|
||||
{
|
||||
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::InvalidInputArgument);
|
||||
}
|
||||
|
||||
$aResult = array(
|
||||
'Folder' => $sFolder,
|
||||
'Uid' => $sUid,
|
||||
'FolderHash' => $sFolderHash,
|
||||
'ThreadUids' => null
|
||||
);
|
||||
|
||||
$oCache = $this->cacherForUids();
|
||||
if ($oCache && $this->Config()->Get('labs', 'use_imap_thread', false))
|
||||
{
|
||||
$aThreadUids = $this->MailClient()->MessageThreadUidsFromCache(
|
||||
$sFolder, $sFolderHash, $sUid, $oCache
|
||||
);
|
||||
|
||||
if (\is_array($aThreadUids) && 1 < \count($aThreadUids))
|
||||
{
|
||||
$aResult['ThreadUids'] = $aThreadUids;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__, $aResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
|
|
@ -5498,11 +5744,12 @@ class Actions
|
|||
$sSearch = '';
|
||||
$sUidNext = '';
|
||||
$bUseThreads = false;
|
||||
$sThreadUid = '';
|
||||
|
||||
$sRawKey = $this->GetActionParam('RawKey', '');
|
||||
$aValues = $this->getDecodedClientRawKeyValue($sRawKey, 9);
|
||||
|
||||
if (\is_array($aValues) && 9 === \count($aValues))
|
||||
if (\is_array($aValues) && 7 < \count($aValues))
|
||||
{
|
||||
$sFolder =(string) $aValues[0];
|
||||
$iOffset = (int) $aValues[1];
|
||||
|
|
@ -5511,6 +5758,11 @@ class Actions
|
|||
$sUidNext = (string) $aValues[6];
|
||||
$bUseThreads = (bool) $aValues[7];
|
||||
|
||||
if ($bUseThreads)
|
||||
{
|
||||
$sThreadUid = isset($aValues[8]) ? (string) $aValues[8] : '';
|
||||
}
|
||||
|
||||
$this->verifyCacheByKey($sRawKey);
|
||||
}
|
||||
else
|
||||
|
|
@ -5521,6 +5773,11 @@ class Actions
|
|||
$sSearch = $this->GetActionParam('Search', '');
|
||||
$sUidNext = $this->GetActionParam('UidNext', '');
|
||||
$bUseThreads = '1' === (string) $this->GetActionParam('UseThreads', '0');
|
||||
|
||||
if ($bUseThreads)
|
||||
{
|
||||
$sThreadUid = (string) $this->GetActionParam('ThreadUid', '');
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === strlen($sFolder))
|
||||
|
|
@ -5542,7 +5799,7 @@ class Actions
|
|||
$this->cacherForUids(),
|
||||
!!$this->Config()->Get('labs', 'use_imap_sort', false),
|
||||
$bUseThreads,
|
||||
!!$this->Config()->Get('labs', 'use_imap_esearch_esort', false)
|
||||
$sThreadUid
|
||||
);
|
||||
}
|
||||
catch (\Exception $oException)
|
||||
|
|
@ -5558,40 +5815,6 @@ class Actions
|
|||
return $this->DefaultResponse(__FUNCTION__, $oMessageList);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* @throws \MailSo\Base\Exceptions\Exception
|
||||
*/
|
||||
public function DoMessageListSimple()
|
||||
{
|
||||
// \sleep(2);
|
||||
// throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantGetMessageList);
|
||||
|
||||
$sFolder = $this->GetActionParam('Folder', '');
|
||||
$aUids = $this->GetActionParam('Uids', null);
|
||||
|
||||
if (0 === \strlen($sFolder) || !\is_array($aUids))
|
||||
{
|
||||
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::InvalidInputArgument);
|
||||
}
|
||||
|
||||
$this->initMailClientConnection();
|
||||
|
||||
$aMessageList = array();
|
||||
|
||||
try
|
||||
{
|
||||
$aMessageList = $this->MailClient()->MessageListSimple($sFolder, $aUids);
|
||||
}
|
||||
catch (\Exception $oException)
|
||||
{
|
||||
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantGetMessageList, $oException);
|
||||
}
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__, $aMessageList);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Model\Account $oAccount
|
||||
* @param bool $bWithDraftInfo = true
|
||||
|
|
@ -6565,7 +6788,8 @@ class Actions
|
|||
))
|
||||
);
|
||||
|
||||
\sleep(1);
|
||||
$this->requestSleep();
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__,
|
||||
$this->getTwoFactorInfo($oAccount));
|
||||
}
|
||||
|
|
@ -6649,7 +6873,8 @@ class Actions
|
|||
// $this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode)
|
||||
// ));
|
||||
|
||||
\sleep(1);
|
||||
$this->requestSleep();
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__,
|
||||
$this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode));
|
||||
}
|
||||
|
|
@ -7713,10 +7938,10 @@ class Actions
|
|||
$iUid = (int) (isset($aValues['Uid']) ? $aValues['Uid'] : 0);
|
||||
$sMimeIndex = (string) (isset($aValues['MimeIndex']) ? $aValues['MimeIndex'] : '');
|
||||
|
||||
header('Content-Type: text/plain', true);
|
||||
\header('Content-Type: text/plain', true);
|
||||
|
||||
return $this->MailClient()->MessageMimeStream(function ($rResource) {
|
||||
if (is_resource($rResource))
|
||||
if (\is_resource($rResource))
|
||||
{
|
||||
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
|
||||
}
|
||||
|
|
@ -7944,6 +8169,57 @@ class Actions
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Model\Account $oAccount
|
||||
* @param string $sHash
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getMimeFileByHash($oAccount, $sHash)
|
||||
{
|
||||
$aValues = $this->getDecodedRawKeyValue($sHash);
|
||||
|
||||
$sFolder = isset($aValues['Folder']) ? $aValues['Folder'] : '';
|
||||
$iUid = (int) isset($aValues['Uid']) ? $aValues['Uid'] : 0;
|
||||
$sMimeIndex = (string) isset($aValues['MimeIndex']) ? $aValues['MimeIndex'] : '';
|
||||
|
||||
$sContentTypeIn = (string) isset($aValues['MimeType']) ? $aValues['MimeType'] : '';
|
||||
$sFileNameIn = (string) isset($aValues['FileName']) ? $aValues['FileName'] : '';
|
||||
|
||||
$oFileProvider = $this->FilesProvider();
|
||||
|
||||
$sResultHash = '';
|
||||
|
||||
$mResult = $this->MailClient()->MessageMimeStream(function($rResource, $sContentType, $sFileName, $sMimeIndex = '')
|
||||
use ($oAccount, $oFileProvider, $sFileNameIn, $sContentTypeIn, &$sResultHash) {
|
||||
|
||||
if ($oAccount && \is_resource($rResource))
|
||||
{
|
||||
$sHash = \MailSo\Base\Utils::Md5Rand($sFileNameIn.'~'.$sContentTypeIn);
|
||||
$rTempResource = $oFileProvider->GetFile($oAccount, $sHash, 'wb+');
|
||||
|
||||
if (@\is_resource($rTempResource))
|
||||
{
|
||||
if (false !== \MailSo\Base\Utils::MultipleStreamWriter($rResource, array($rTempResource)))
|
||||
{
|
||||
$sResultHash = $sHash;
|
||||
}
|
||||
|
||||
@\fclose($rTempResource);
|
||||
}
|
||||
}
|
||||
|
||||
}, $sFolder, $iUid, true, $sMimeIndex);
|
||||
|
||||
$aValues['FileHash'] = '';
|
||||
if ($mResult)
|
||||
{
|
||||
$aValues['FileHash'] = $sResultHash;
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bDownload
|
||||
* @param bool $bThumbnail = false
|
||||
|
|
@ -7956,15 +8232,50 @@ class Actions
|
|||
$aValues = $this->getDecodedRawKeyValue($sRawKey);
|
||||
|
||||
$sFolder = isset($aValues['Folder']) ? $aValues['Folder'] : '';
|
||||
$iUid = (int) isset($aValues['Uid']) ? $aValues['Uid'] : 0;
|
||||
$sMimeIndex = (string) isset($aValues['MimeIndex']) ? $aValues['MimeIndex'] : '';
|
||||
$iUid = isset($aValues['Uid']) ? (int) $aValues['Uid'] : 0;
|
||||
$sMimeIndex = isset($aValues['MimeIndex']) ? (string) $aValues['MimeIndex'] : '';
|
||||
|
||||
$sContentTypeIn = (string) isset($aValues['MimeType']) ? $aValues['MimeType'] : '';
|
||||
$sFileNameIn = (string) isset($aValues['FileName']) ? $aValues['FileName'] : '';
|
||||
$sContentTypeIn = isset($aValues['MimeType']) ? (string) $aValues['MimeType'] : '';
|
||||
$sFileNameIn = isset($aValues['FileName']) ? (string) $aValues['FileName'] : '';
|
||||
$sFileHashIn = isset($aValues['FileHash']) ? (string) $aValues['FileHash'] : '';
|
||||
|
||||
if (!empty($sFolder) && 0 < $iUid)
|
||||
if (!empty($sFileHashIn))
|
||||
{
|
||||
$this->verifyCacheByKey($sRawKey);
|
||||
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
|
||||
$sContentTypeOut = empty($sContentTypeIn) ?
|
||||
\MailSo\Base\Utils::MimeContentType($sFileNameIn) : $sContentTypeIn;
|
||||
|
||||
$sFileNameOut = $this->MainClearFileName($sFileNameIn, $sContentTypeIn, $sMimeIndex);
|
||||
|
||||
$rResource = $this->FilesProvider()->GetFile($oAccount, $sFileHashIn);
|
||||
if (\is_resource($rResource))
|
||||
{
|
||||
$sFileNameOut = \MailSo\Base\Utils::ConvertEncoding(
|
||||
$sFileNameOut, \MailSo\Base\Enumerations\Charset::UTF_8,
|
||||
\MailSo\Base\Enumerations\Charset::CP858);
|
||||
|
||||
\header('Content-Type: '.$sContentTypeOut);
|
||||
\header('Content-Disposition: attachment; '.
|
||||
\trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut)), true);
|
||||
|
||||
\header('Accept-Ranges: none', true);
|
||||
\header('Content-Transfer-Encoding: binary');
|
||||
|
||||
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!empty($sFolder) && 0 < $iUid)
|
||||
{
|
||||
$this->verifyCacheByKey($sRawKey);
|
||||
}
|
||||
}
|
||||
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
|
@ -8358,10 +8669,11 @@ class Actions
|
|||
* @param string $sDefault = ''
|
||||
* @param bool $bAdmin = false
|
||||
* @param bool $bSearchShortName = false
|
||||
* @param bool $bAllowEmptyResult = false
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function ValidateLanguage($sLanguage, $sDefault = '', $bAdmin = false, $bSearchShortName = false)
|
||||
public function ValidateLanguage($sLanguage, $sDefault = '', $bAdmin = false, $bSearchShortName = false, $bAllowEmptyResult = false)
|
||||
{
|
||||
$sResult = '';
|
||||
$aLang = $this->GetLanguages($bAdmin);
|
||||
|
|
@ -8387,7 +8699,7 @@ class Actions
|
|||
$sResult = $sDefault;
|
||||
}
|
||||
|
||||
if (empty($sResult))
|
||||
if (empty($sResult) && !$bAllowEmptyResult)
|
||||
{
|
||||
$sResult = $this->Config()->Get('webmail', $bAdmin ? 'language_admin' : 'language', 'en');
|
||||
$sResult = \in_array($sResult, $aLang) ? $sResult : 'en';
|
||||
|
|
@ -9039,17 +9351,49 @@ class Actions
|
|||
if (\is_object($mResponse))
|
||||
{
|
||||
$bHook = true;
|
||||
$self = $this;
|
||||
$sClassName = \get_class($mResponse);
|
||||
$bHasSimpleJsonFunc = \method_exists($mResponse, 'ToSimpleJSON');
|
||||
$bThumb = $this->GetCapa(false, \RainLoop\Enumerations\Capa::ATTACHMENT_THUMBNAILS);
|
||||
|
||||
$oAccountCache = null;
|
||||
$fGetAccount = function () use ($self, &$oAccountCache) {
|
||||
if (null === $oAccountCache)
|
||||
{
|
||||
$oAccount = $self->getAccountFromToken(false);
|
||||
$oAccountCache = $oAccount;
|
||||
}
|
||||
|
||||
return $oAccountCache;
|
||||
};
|
||||
|
||||
$aCheckableFoldersCache = null;
|
||||
$fGetCheckableFolder = function () use ($self, &$aCheckableFoldersCache) {
|
||||
if (null === $aCheckableFoldersCache)
|
||||
{
|
||||
$oAccount = $self->getAccountFromToken(false);
|
||||
|
||||
$oSettingsLocal = $self->SettingsProvider(true)->Load($oAccount);
|
||||
$sCheckable = $oSettingsLocal->GetConf('CheckableFolder', '[]');
|
||||
$aCheckable = @\json_decode($sCheckable);
|
||||
if (!\is_array($aCheckable))
|
||||
{
|
||||
$aCheckable = array();
|
||||
}
|
||||
|
||||
$aCheckableFoldersCache = $aCheckable;
|
||||
}
|
||||
|
||||
return $aCheckableFoldersCache;
|
||||
};
|
||||
|
||||
if ($bHasSimpleJsonFunc)
|
||||
{
|
||||
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), $mResponse->ToSimpleJSON(true));
|
||||
}
|
||||
else if ('MailSo\Mail\Message' === $sClassName)
|
||||
{
|
||||
$oAccount = $this->getAccountFromToken(false);
|
||||
$oAccount = call_user_func($fGetAccount);
|
||||
|
||||
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array(
|
||||
'Folder' => $mResponse->Folder(),
|
||||
|
|
@ -9079,28 +9423,7 @@ class Actions
|
|||
$iAttachmentsCount = $oAttachments ? $oAttachments->Count() : 0;
|
||||
|
||||
$mResult['HasAttachments'] = 0 < $iAttachmentsCount;
|
||||
$mResult['AttachmentsMainType'] = '';
|
||||
if (0 < $iAttachmentsCount)
|
||||
{
|
||||
switch (true)
|
||||
{
|
||||
case $iAttachmentsCount === $oAttachments->ImageCount():
|
||||
$mResult['AttachmentsMainType'] = 'image';
|
||||
break;
|
||||
case $iAttachmentsCount === $oAttachments->ArchiveCount():
|
||||
$mResult['AttachmentsMainType'] = 'archive';
|
||||
break;
|
||||
case $iAttachmentsCount === $oAttachments->PdfCount():
|
||||
$mResult['AttachmentsMainType'] = 'pdf';
|
||||
break;
|
||||
case $iAttachmentsCount === $oAttachments->DocCount():
|
||||
$mResult['AttachmentsMainType'] = 'doc';
|
||||
break;
|
||||
case $iAttachmentsCount === $oAttachments->CertificateCount():
|
||||
$mResult['AttachmentsMainType'] = 'certificate';
|
||||
break;
|
||||
}
|
||||
}
|
||||
$mResult['AttachmentsSpecData'] = $mResult['HasAttachments'] ? $oAttachments->SpecData() : array();
|
||||
|
||||
$sSubject = $mResult['Subject'];
|
||||
$mResult['Hash'] = \md5($mResult['Folder'].$mResult['Uid']);
|
||||
|
|
@ -9345,6 +9668,12 @@ class Actions
|
|||
);
|
||||
}
|
||||
|
||||
$aCheckableFolder = \call_user_func($fGetCheckableFolder);
|
||||
if (!\is_array($aCheckableFolder))
|
||||
{
|
||||
$aCheckableFolder = array();
|
||||
}
|
||||
|
||||
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array(
|
||||
'Name' => $mResponse->Name(),
|
||||
'FullName' => $mResponse->FullName(),
|
||||
|
|
@ -9356,6 +9685,7 @@ class Actions
|
|||
'IsExists' => $mResponse->IsExists(),
|
||||
'IsSelectable' => $mResponse->IsSelectable(),
|
||||
'Flags' => $mResponse->FlagsLowerCase(),
|
||||
'Checkable' => \in_array($mResponse->FullNameRaw(), $aCheckableFolder),
|
||||
'Extended' => $aExtended,
|
||||
'SubFolders' => $this->responseObject($mResponse->SubFolders(), $sParent, $aParameters)
|
||||
));
|
||||
|
|
@ -9369,8 +9699,8 @@ class Actions
|
|||
'Folder' => $mResponse->FolderName,
|
||||
'FolderHash' => $mResponse->FolderHash,
|
||||
'UidNext' => $mResponse->UidNext,
|
||||
'ThreadUid' => $mResponse->ThreadUid,
|
||||
'NewMessages' => $this->responseObject($mResponse->NewMessages),
|
||||
// 'LastCollapsedThreadUids' => $mResponse->LastCollapsedThreadUids,
|
||||
'Offset' => $mResponse->Offset,
|
||||
'Limit' => $mResponse->Limit,
|
||||
'Search' => $mResponse->Search
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ class Application extends \RainLoop\Config\AbstractConfig
|
|||
|
||||
'capa' => array(
|
||||
'filters' => array(true),
|
||||
'templates' => array(true)
|
||||
'templates' => array(true),
|
||||
'attachments_actions' => array(true)
|
||||
),
|
||||
|
||||
'login' => array(
|
||||
|
|
@ -186,6 +187,8 @@ Values:
|
|||
'hide_passwords' => array(true, 'Required for development purposes only.
|
||||
Disabling this option is not recommended.'),
|
||||
|
||||
'time_offset' => array(0),
|
||||
|
||||
'filename' => array('log-{date:Y-m-d}.txt',
|
||||
'Log filename.
|
||||
For security reasons, some characters are removed from filename.
|
||||
|
|
@ -282,7 +285,6 @@ Enables caching in the system'),
|
|||
'use_app_debug_js' => array(false),
|
||||
'use_app_debug_css' => array(false),
|
||||
'use_imap_sort' => array(true),
|
||||
'use_imap_esearch_esort' => array(true),
|
||||
'use_imap_force_selection' => array(false),
|
||||
'use_imap_list_subscribe' => array(true),
|
||||
'use_imap_thread' => array(true),
|
||||
|
|
@ -301,6 +303,7 @@ Enables caching in the system'),
|
|||
'smtp_show_server_errors' => array(false),
|
||||
'sieve_allow_raw_script' => array(false),
|
||||
'sieve_utf8_folder_name' => array(true),
|
||||
'owncloud_save_folder' => array('Attachments'),
|
||||
'curl_proxy' => array(''),
|
||||
'curl_proxy_auth' => array(''),
|
||||
'in_iframe' => array(false),
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ class Account extends \RainLoop\Account // for backward compatibility
|
|||
\RainLoop\Utils::GetShortToken(), // 7
|
||||
$this->sProxyAuthUser, // 8
|
||||
$this->sProxyAuthPassword, // 9
|
||||
0 // 10
|
||||
0 // 10 // timelife
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,17 @@ class Files extends \RainLoop\Providers\AbstractProvider
|
|||
$this->oDriver->CloseAllOpenedFiles() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Model\Account $oAccount
|
||||
* @param string $sKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GenerateLocalFullFileName($oAccount, $sKey)
|
||||
{
|
||||
return $this->oDriver ? $this->oDriver->GenerateLocalFullFileName($oAccount, $sKey) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -25,6 +25,17 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
$this->sDataPath = \rtrim(\trim($sStoragePath), '\\/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Model\Account $oAccount
|
||||
* @param string $sKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GenerateLocalFullFileName($oAccount, $sKey)
|
||||
{
|
||||
return $this->generateFullFileName($oAccount, $sKey, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Model\Account $oAccount
|
||||
* @param string $sKey
|
||||
|
|
@ -37,7 +48,7 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
$bResult = false;
|
||||
if ($rSource)
|
||||
{
|
||||
$rOpenOutput = @\fopen($this->generateFileName($oAccount, $sKey, true), 'w+b');
|
||||
$rOpenOutput = @\fopen($this->generateFullFileName($oAccount, $sKey, true), 'w+b');
|
||||
if ($rOpenOutput)
|
||||
{
|
||||
$bResult = (false !== \MailSo\Base\Utils::MultipleStreamWriter($rSource, array($rOpenOutput)));
|
||||
|
|
@ -57,7 +68,7 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
public function MoveUploadedFile($oAccount, $sKey, $sSource)
|
||||
{
|
||||
return @\move_uploaded_file($sSource,
|
||||
$this->generateFileName($oAccount, $sKey, true));
|
||||
$this->generateFullFileName($oAccount, $sKey, true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,7 +83,7 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
$mResult = false;
|
||||
$bCreate = !!\preg_match('/[wac]/', $sOpenMode);
|
||||
|
||||
$sFileName = $this->generateFileName($oAccount, $sKey, $bCreate);
|
||||
$sFileName = $this->generateFullFileName($oAccount, $sKey, $bCreate);
|
||||
if ($bCreate || \file_exists($sFileName))
|
||||
{
|
||||
$mResult = @\fopen($sFileName, $sOpenMode);
|
||||
|
|
@ -95,7 +106,7 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
public function GetFileName($oAccount, $sKey)
|
||||
{
|
||||
$mResult = false;
|
||||
$sFileName = $this->generateFileName($oAccount, $sKey);
|
||||
$sFileName = $this->generateFullFileName($oAccount, $sKey);
|
||||
if (\file_exists($sFileName))
|
||||
{
|
||||
$mResult = $sFileName;
|
||||
|
|
@ -113,7 +124,7 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
public function Clear($oAccount, $sKey)
|
||||
{
|
||||
$mResult = true;
|
||||
$sFileName = $this->generateFileName($oAccount, $sKey);
|
||||
$sFileName = $this->generateFullFileName($oAccount, $sKey);
|
||||
if (\file_exists($sFileName))
|
||||
{
|
||||
if (isset($this->aResources[$sFileName]) && \is_resource($this->aResources[$sFileName]))
|
||||
|
|
@ -136,7 +147,7 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
public function FileSize($oAccount, $sKey)
|
||||
{
|
||||
$mResult = false;
|
||||
$sFileName = $this->generateFileName($oAccount, $sKey);
|
||||
$sFileName = $this->generateFullFileName($oAccount, $sKey);
|
||||
if (\file_exists($sFileName))
|
||||
{
|
||||
$mResult = \filesize($sFileName);
|
||||
|
|
@ -153,7 +164,7 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
*/
|
||||
public function FileExists($oAccount, $sKey)
|
||||
{
|
||||
return @\file_exists($this->generateFileName($oAccount, $sKey));
|
||||
return @\file_exists($this->generateFullFileName($oAccount, $sKey));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -198,7 +209,7 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generateFileName($oAccount, $sKey, $bMkDir = false)
|
||||
private function generateFullFileName($oAccount, $sKey, $bMkDir = false)
|
||||
{
|
||||
$sEmail = $sSubEmail = '';
|
||||
if ($oAccount instanceof \RainLoop\Model\Account)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,14 @@ namespace RainLoop\Providers\Files;
|
|||
|
||||
interface IFiles
|
||||
{
|
||||
/**
|
||||
* @param \RainLoop\Model\Account $oAccount
|
||||
* @param string $sKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GenerateLocalFullFileName($oAccount, $sKey);
|
||||
|
||||
/**
|
||||
* @param \RainLoop\Model\Account $oAccount
|
||||
* @param string $sKey
|
||||
|
|
|
|||
|
|
@ -18,13 +18,14 @@ class OwnCloudSuggestions implements \RainLoop\Providers\Suggestions\ISuggestion
|
|||
*/
|
||||
public function Process($oAccount, $sQuery, $iLimit = 20)
|
||||
{
|
||||
$iInputLimit = $iLimit;
|
||||
$aResult = array();
|
||||
$sQuery = \trim($sQuery);
|
||||
|
||||
try
|
||||
{
|
||||
if (!$oAccount || !\RainLoop\Utils::IsOwnCloud() ||
|
||||
!\class_exists('\\OCP\\Contacts') || !\OCP\Contacts::isEnabled() ||
|
||||
!\class_exists('\\OCP\\User') || !\OCP\User::isLoggedIn()
|
||||
if ('' === $sQuery || !$oAccount || !\RainLoop\Utils::IsOwnCloudLoggedIn() ||
|
||||
!\class_exists('\\OCP\\Contacts') || !\OCP\Contacts::isEnabled()
|
||||
)
|
||||
{
|
||||
return $aResult;
|
||||
|
|
@ -33,7 +34,7 @@ class OwnCloudSuggestions implements \RainLoop\Providers\Suggestions\ISuggestion
|
|||
$aSearchResult = \OCP\Contacts::search($sQuery, array('FN', 'EMAIL'));
|
||||
//$this->oLogger->WriteDump($aSearchResult);
|
||||
|
||||
$aPreResult = array();
|
||||
$aHashes = array();
|
||||
if (\is_array($aSearchResult) && 0 < \count($aSearchResult))
|
||||
{
|
||||
foreach ($aSearchResult as $aContact)
|
||||
|
|
@ -54,35 +55,23 @@ class OwnCloudSuggestions implements \RainLoop\Providers\Suggestions\ISuggestion
|
|||
$mEmails = array($mEmails);
|
||||
}
|
||||
|
||||
if (!isset($aPreResult[$sUid]))
|
||||
{
|
||||
$aPreResult[$sUid] = array();
|
||||
}
|
||||
|
||||
foreach ($mEmails as $sEmail)
|
||||
{
|
||||
$sEmail = \trim($sEmail);
|
||||
if (!empty($sEmail))
|
||||
$sHash = '"'.$sFullName.'" <'.$sEmail.'>';
|
||||
if (!isset($aHashes[$sHash]))
|
||||
{
|
||||
$aHashes[$sHash] = true;
|
||||
$aResult[] = array($sEmail, $sFullName);
|
||||
$iLimit--;
|
||||
$aPreResult[$sUid][] = array($sEmail, $sFullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aPreResult = \array_values($aPreResult);
|
||||
// $this->oLogger->WriteDump($aPreResult);
|
||||
foreach ($aPreResult as $aData)
|
||||
{
|
||||
foreach ($aData as $aSubData)
|
||||
{
|
||||
$aResult[] = $aSubData;
|
||||
}
|
||||
}
|
||||
$aResult = \array_slice($aResult, 0, $iInputLimit);
|
||||
}
|
||||
|
||||
unset($aSearchResult, $aPreResult);
|
||||
unset($aSearchResult, $aHashes);
|
||||
}
|
||||
catch (\Exception $oException)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -315,6 +315,13 @@ class Utils
|
|||
return isset($_ENV['RAINLOOP_OWNCLOUD']) && $_ENV['RAINLOOP_OWNCLOUD'] &&
|
||||
\class_exists('\\OC');
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsOwnCloudLoggedIn()
|
||||
{
|
||||
return self::IsOwnCloud() && \class_exists('\\OCP\\User') && \OCP\User::isLoggedIn();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
.thm-border-radius(@login-border-radius);
|
||||
.thm-box-shadow(@login-box-shadow);
|
||||
|
||||
.legend, .checkboxSignMe, .checkboxAdditionalCodeSignMe, .g-ui-link, .social-button, .language-button {
|
||||
.legend, .e-checkbox-icon, .g-ui-link, .social-button, .language-button {
|
||||
color: @login-color !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@
|
|||
color: @languages-color;
|
||||
.flag-name {
|
||||
color: @languages-color;
|
||||
border-bottom: 1px dashed @languages-color;
|
||||
border-color: @languages-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
<div class="b-login-content">
|
||||
<div class="loginFormWrapper">
|
||||
<div class="loginFormWrapper" data-bind="css: {'afterLoginHide': formHidden}">
|
||||
<center>
|
||||
<div class="alert alertError" data-bind="visible: '' !== submitError()">
|
||||
<button type="button" class="close" data-bind="click: function () { submitError('') }">×</button>
|
||||
<span data-bind="text: submitError"></span>
|
||||
</div>
|
||||
<form class="wrapper loginForm thm-login" action="#/" data-bind="submit: submitForm">
|
||||
<div class="controls" data-bind="css: {'error': loginError}">
|
||||
<form class="wrapper loginForm thm-login" action="#/" data-bind="submit: submitForm, css: {'errorAnimated': formError}">
|
||||
<div class="controls" data-bind="css: {'error': loginError, 'animated': loginErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="text" class="input-block-level inputLogin checkAutocomplete" name="RainLoopAdminLogin" id="RainLoopAdminLogin"
|
||||
<input type="text" class="input-block-level inputLogin checkAutocomplete"
|
||||
name="RainLoopAdminLogin" id="RainLoopAdminLogin"
|
||||
style="padding-right: 35px;"
|
||||
autocorrect="off" autocapitalize="off" spellcheck="false" data-i18n="[placeholder]LOGIN/LABEL_LOGIN"
|
||||
data-bind="textInput: login, hasFocus: loginFocus" />
|
||||
<span class="add-on">
|
||||
|
|
@ -16,9 +18,11 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls" data-bind="css: {'error': passwordError}">
|
||||
<div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="password" class="input-block-level inputPassword checkAutocomplete" placeholder="Password" name="RainLoopAdminPassword" id="RainLoopAdminPassword"
|
||||
<input type="password" class="input-block-level inputPassword checkAutocomplete"
|
||||
placeholder="Password" name="RainLoopAdminPassword" id="RainLoopAdminPassword"
|
||||
style="padding-right: 35px;"
|
||||
autocorrect="off" autocapitalize="off" spellcheck="false" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD"
|
||||
data-bind="textInput: password" />
|
||||
<span class="add-on">
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
<div class="tab-pane active" id="branding-welcome-page-section-id">
|
||||
<div class="tab-pane" id="branding-welcome-page-section-id">
|
||||
|
||||
<div class="form-horizontal" data-bind="css: {'disabled-form': !capa()}">
|
||||
<div class="control-group">
|
||||
|
|
|
|||
|
|
@ -26,9 +26,16 @@
|
|||
<div class="control-group">
|
||||
<label class="control-label" data-i18n="TAB_CONTACTS/LABEL_STORAGE_TYPE"></label>
|
||||
<div class="controls">
|
||||
<select data-bind="options: contactsTypesOptions, value: mainContactsType,
|
||||
optionsText: 'name', optionsValue: 'id', optionsAfterRender: defautOptionsAfterRender, saveTrigger: contactsTypeTrigger"></select>
|
||||
<div data-bind="saveTrigger: contactsTypeTrigger"></div>
|
||||
<div data-bind="component: {
|
||||
name: 'Select',
|
||||
params: {
|
||||
options: contactsTypesOptions,
|
||||
value: mainContactsType,
|
||||
trigger: contactsTypeTrigger,
|
||||
optionsText: 'name',
|
||||
optionsValue: 'id'
|
||||
}
|
||||
}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<span class="flag-wrapper">
|
||||
<span data-bind="css: 'flag flag-' + language()" style=""></span>
|
||||
</span>
|
||||
<span class="flag-name" data-bind="text: languageFullName, click: selectLanguage"></span>
|
||||
<span class="flag-name" tabindex="0" data-bind="text: languageFullName, click: selectLanguage, keypress: selectLanguage"></span>
|
||||
|
||||
<div data-bind="component: {
|
||||
name: 'SaveTrigger',
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
<span class="flag-wrapper">
|
||||
<span data-bind="css: 'flag flag-' + languageAdmin()" style=""></span>
|
||||
</span>
|
||||
<span class="flag-name" data-bind="text: languageAdminFullName, click: selectLanguageAdmin"></span>
|
||||
<span class="flag-name" tabindex="0" data-bind="text: languageAdminFullName, click: selectLanguageAdmin, keypress: selectLanguageAdmin"></span>
|
||||
|
||||
<div data-bind="component: {
|
||||
name: 'SaveTrigger',
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@
|
|||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_CHECK_ALL"></td><td>Ctrl + A, Cmd + A</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_ARCHIVE"></td><td>Z</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_DELETE"></td><td>Delete, Shift + Delete, #</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_OPEN_THREAD"></td><td>T</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_MOVE"></td><td>M</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_READ"></td><td>Q</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_UNREAD"></td><td>U</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_IMPORTANT"></td><td>I</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_SEARCH"></td><td>/</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_CANCEL_SEARCH"></td><td>Esc</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_FULLSCREEN_ENTER"></td><td>Enter</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_VIEW_MESSAGE_ENTER"></td><td>Enter</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_SWITCH_TO_MESSAGE"></td><td>→, Tab</td></tr>
|
||||
|
|
@ -60,14 +60,12 @@
|
|||
|
||||
<table class="table table-striped table-bordered">
|
||||
<tbody>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_FULLSCREEN_TOGGLE">Toggle fullscreen mode</td><td>Enter</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_BLOCKQUOTES_TOGGLE">Toggle message blockquotes</td><td>B</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_THREAD_NEXT">Next message in thread</td><td>Ctrl + ←, Cmd + ←</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_THREAD_PREV">Previous message in thread</td><td>Ctrl + →, Cmd + →</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_PRINT">Print</td><td>Ctrl + P, Cmd + P</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_EXIT_FULLSCREEN">Exit fullscreen mode</td><td>Esc</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_CLOSE_MESSAGE">Close message (No preview pane layout)</td><td>Esc</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_SWITCH_TO_LIST">Switch focus back to message list</td><td>Tab, Shift + Tab, Esc</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_FULLSCREEN_TOGGLE"></td><td>Enter</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_BLOCKQUOTES_TOGGLE"></td><td>B</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_PRINT"></td><td>Ctrl + P, Cmd + P</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_EXIT_FULLSCREEN"></td><td>Esc</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_CLOSE_MESSAGE"></td><td>Esc</td></tr>
|
||||
<tr><td class="i18n" data-i18n="SHORTCUTS_HELP/LABEL_SWITCH_TO_LIST"></td><td>Tab, Shift + Tab, Esc</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<label class="e-component e-checkbox inline" data-bind="click: click, css: { 'disabled': disable() || !enable() }">
|
||||
<i data-bind="css: value() ?
|
||||
<span class="e-component e-checkbox inline" tabindex="0" data-bind="click: click, keypress: keypress, css: { 'disabled': disable() || !enable() }">
|
||||
<i role="checkbox" class="e-checkbox-icon" data-bind="css: value() ?
|
||||
(inverted ? 'icon-checkbox-unchecked' : 'icon-checkbox-checked') :
|
||||
(inverted ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked')
|
||||
"></i>
|
||||
<!-- ko if: labeled -->
|
||||
|
||||
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span>
|
||||
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}, css: {'i18n-animation': labelAnimated}"></span>
|
||||
<!-- /ko -->
|
||||
</label>
|
||||
</span>
|
||||
|
|
@ -2,6 +2,6 @@
|
|||
<input type="checkbox" data-bind="checked: value, disable: disable() || !enable()" style="margin: 0 0 1px 0;" />
|
||||
<!-- ko if: labeled -->
|
||||
|
||||
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span>
|
||||
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}, css: {'i18n-animation': labelAnimated}"></span>
|
||||
<!-- /ko -->
|
||||
</label>
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
<label class="e-component e-checkbox material-design inline" data-bind="click: click, css: { 'disabled': disable() || !enable() }">
|
||||
<div class="sub-checkbox-container">
|
||||
<span class="e-component e-checkbox material-design inline" tabindex="0" data-bind="click: click, keypress: keypress, css: { 'disabled': disable() || !enable() }">
|
||||
<div class="sub-checkbox-container" role="checkbox">
|
||||
<div class="sub-checkbox" data-bind="css: {'checked': (value() && !inverted) || (!value() && inverted),
|
||||
'unchecked': (!value() && !inverted) || (value() && inverted),
|
||||
'box': animationBox, 'checkmark': animationCheckmark}"></div>
|
||||
</div>
|
||||
<!-- ko if: labeled -->
|
||||
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span>
|
||||
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}, css: {'i18n-animation': labelAnimated}"></span>
|
||||
<!-- /ko -->
|
||||
</label>
|
||||
</span>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<div class="e-component e-radio" data-bind="foreach: values">
|
||||
<label data-bind="click: $parent.click, css: {'inline': $parent.inline}">
|
||||
<i data-bind="css: $parent.value() === value ? 'icon-radio-checked' : 'icon-radio-unchecked'"></i>
|
||||
<label tabindex="0" data-bind="click: $parent.click, css: {'inline': $parent.inline}">
|
||||
<i role="radio" data-bind="css: $parent.value() === value ? 'icon-radio-checked' : 'icon-radio-unchecked'"></i>
|
||||
|
||||
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span>
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
<!-- ko if: preLabeled -->
|
||||
<span class="i18n" data-bind="attr: {'data-i18n': preLabel}"></span>
|
||||
|
||||
<!-- /ko -->
|
||||
<select data-bind="options: options, value: value, enable: enable, optionsText: optionsText, optionsValue: optionsValue,
|
||||
css: className, optionsAfterRender: defautOptionsAfterRender"></select>
|
||||
<!-- ko if: labeled -->
|
||||
|
||||
<span class="i18n" data-bind="attr: {'data-i18n': label}"></span>
|
||||
|
||||
<!-- /ko -->
|
||||
<!-- ko if: triggered -->
|
||||
|
||||
<div data-bind="component: {
|
||||
name: 'SaveTrigger',
|
||||
params: { value: trigger }
|
||||
}"></div>
|
||||
<!-- /ko -->
|
||||
<div class="e-component e-select inline">
|
||||
<!-- ko if: preLabeled -->
|
||||
<span class="i18n" data-bind="attr: {'data-i18n': preLabel}"></span>
|
||||
|
||||
<!-- /ko -->
|
||||
<select data-bind="options: options, value: value, enable: enable, optionsText: optionsText, optionsValue: optionsValue,
|
||||
css: className, optionsAfterRender: defautOptionsAfterRender"></select>
|
||||
<!-- ko if: labeled -->
|
||||
|
||||
<span class="i18n" data-bind="attr: {'data-i18n': label}"></span>
|
||||
|
||||
<!-- /ko -->
|
||||
<!-- ko if: triggered -->
|
||||
|
||||
<div data-bind="component: {
|
||||
name: 'SaveTrigger',
|
||||
params: { value: trigger }
|
||||
}"></div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<div class="b-login-content">
|
||||
<div class="loginFormWrapper">
|
||||
<div class="loginFormWrapper" data-bind="css: {'afterLoginHide': formHidden}">
|
||||
<center>
|
||||
{{INCLUDE/BeforeLogo/PLACE}}
|
||||
<!-- ko if: logoImg -->
|
||||
|
|
@ -19,12 +19,13 @@
|
|||
<span data-bind="text: submitErrorAddidional"></span>
|
||||
</div>
|
||||
</div>
|
||||
<form class="wrapper loginForm thm-login" action="#/" data-bind="submit: submitForm">
|
||||
<form class="wrapper loginForm thm-login" action="#/" data-bind="submit: submitForm, css: {'errorAnimated': formError}">
|
||||
{{INCLUDE/TopControlGroup/PLACE}}
|
||||
<div class="controls" data-bind="css: {'error': emailError}">
|
||||
<div class="controls" data-bind="css: {'error': emailError, 'animated': emailErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="email" class="i18n input-block-level inputEmail checkAutocomplete"
|
||||
name="RainLoopEmail" id="RainLoopEmail"
|
||||
style="padding-right: 35px;"
|
||||
autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="textInput: email, hasFocus: emailFocus" data-i18n="[placeholder]LOGIN/LABEL_EMAIL" />
|
||||
<span class="add-on">
|
||||
|
|
@ -32,9 +33,11 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls" data-bind="css: {'error': passwordError}">
|
||||
<div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="password" class="i18n input-block-level inputPassword checkAutocomplete" name="RainLoopPassword" id="RainLoopPassword"
|
||||
<input type="password" class="i18n input-block-level inputPassword checkAutocomplete"
|
||||
name="RainLoopPassword" id="RainLoopPassword"
|
||||
style="padding-right: 35px;"
|
||||
autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
data-bind="textInput: password, hasFocus: passwordFocus" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD" />
|
||||
<span class="add-on">
|
||||
|
|
@ -42,9 +45,11 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls" data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error}">
|
||||
<div class="controls" data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error, 'animated': additionalCode.errorAnimation}">
|
||||
<div class="input-append">
|
||||
<input type="text" class="i18n input-block-level inputAdditionalCode" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
<input type="text" class="i18n input-block-level inputAdditionalCode"
|
||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
style="padding-right: 35px;"
|
||||
data-bind="textInput: additionalCode, hasFocus: additionalCode.focused" data-i18n="[placeholder]LOGIN/LABEL_VERIFICATION_CODE" />
|
||||
<span class="add-on">
|
||||
<i class="icon-key"></i>
|
||||
|
|
@ -52,11 +57,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="controls" data-bind="visible: additionalCode.visibility()">
|
||||
<label class="additionalCodeSignMeLabel" data-bind="click: function () { additionalCodeSignMe(!additionalCodeSignMe()); }">
|
||||
<i data-bind="css: additionalCodeSignMe() ? 'checkboxAdditionalCodeSignMe icon-checkbox-checked' : 'checkboxAdditionalCodeSignMe icon-checkbox-unchecked'"></i>
|
||||
|
||||
<span class="i18n" data-i18n="LOGIN/LABEL_DONT_ASK_VERIFICATION_CODE"></span>
|
||||
</label>
|
||||
<div class="additionalCodeSignMeLabel" data-bind="component: {
|
||||
name: 'CheckboxSimple',
|
||||
params: {
|
||||
label: 'LOGIN/LABEL_DONT_ASK_VERIFICATION_CODE',
|
||||
value: additionalCodeSignMe
|
||||
}
|
||||
}"></div>
|
||||
</div>
|
||||
{{INCLUDE/BottomControlGroup/PLACE}}
|
||||
<div class="controls">
|
||||
|
|
@ -80,11 +87,21 @@
|
|||
<i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i>
|
||||
</a>
|
||||
</div>
|
||||
<label class="signMeLabel inline" data-bind="click: function () { signMe(!signMe()); }, visible: signMeVisibility">
|
||||
|
||||
<div class="signMeLabel" data-bind="component: {
|
||||
name: 'CheckboxSimple',
|
||||
params: {
|
||||
label: 'LOGIN/LABEL_SIGN_ME',
|
||||
labelAnimated: true,
|
||||
value: signMe
|
||||
}
|
||||
}"></div>
|
||||
|
||||
<!-- <label class="signMeLabel inline" data-bind="click: function () { signMe(!signMe()); }, visible: signMeVisibility">
|
||||
<i data-bind="css: signMe() ? 'checkboxSignMe icon-checkbox-checked' : 'checkboxSignMe icon-checkbox-unchecked'"></i>
|
||||
|
||||
<span class="i18n i18n-animation" data-i18n="LOGIN/LABEL_SIGN_ME"></span>
|
||||
</label>
|
||||
</label>-->
|
||||
</div>
|
||||
<div class="controls clearfix" data-bind="visible: '' !== forgotPasswordLinkUrl || '' !== registrationLinkUrl">
|
||||
<div class="forgot-link thm-forgot pull-left" data-bind="visible: '' !== forgotPasswordLinkUrl" style="text-align: center">
|
||||
|
|
@ -105,11 +122,12 @@
|
|||
<label class="flag-selector">
|
||||
<i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i>
|
||||
|
||||
<span class="flag-name" data-bind="text: languageFullName, click: selectLanguage"></span>
|
||||
<span class="flag-name" tabindex="0" data-bind="text: languageFullName, click: selectLanguage, keypress: selectLanguage, onTab: selectLanguageOnTab"></span>
|
||||
</label>
|
||||
</div>
|
||||
{{INCLUDE/BottomFooter/PLACE}}
|
||||
</center>
|
||||
</div>
|
||||
<a href="#" onclick="return false;"></a>
|
||||
<div class="loginAfter"></div>
|
||||
</div>
|
||||
|
|
@ -137,19 +137,21 @@
|
|||
<div class="b-content" data-bind="nano: true, initDom: dragOverBodyArea">
|
||||
<div class="content g-scrollbox">
|
||||
<div class="content-wrapper">
|
||||
<div class="listThreadUidDesc" data-bind="visible: '' !== messageListEndThreadUid(), click: cancelThreadUid">
|
||||
<i class="icon-left" data-bind="click: cancelThreadUid"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/BACK_TO_MESSAGE_LIST"></span>
|
||||
</div>
|
||||
<div class="listSearchDesc" data-bind="visible: '' !== messageListSearchDesc()">
|
||||
<a class="close-custom" data-bind="click: cancelSearch">×</a>
|
||||
<!-- <a class="btn btn-small pull-right searchCancelButton" data-bind="click: cancelSearch">
|
||||
<i class="icon-remove"></i>
|
||||
</a>-->
|
||||
<span data-bind="text: messageListSearchDesc"></span>:
|
||||
<span data-bind="text: messageListSearchDesc"></span>
|
||||
</div>
|
||||
<div class="listDragOver" data-bind="css: {'viewAppendArea': dragOver() && '' === messageListError() && !popupVisibility(), 'dragOverEnter': dragOverEnter }, initDom: dragOverArea">
|
||||
<i class="icon-down e-icon"></i>
|
||||
|
||||
<span class="i18n" data-i18n="MESSAGE_LIST/PUT_MESSAGE_HERE"></span>
|
||||
</div>
|
||||
<div class="listClear" data-bind="visible: '' === messageListSearchDesc() && '' === messageListError() && 0 < messageList().length && (isSpamFolder() || isTrashFolder())">
|
||||
<div class="listClear" data-bind="visible: clearListIsVisible()">
|
||||
<span class="g-ui-link i18n" data-i18n="MESSAGE_LIST/BUTTON_EMPTY_FOLDER" data-bind="command: clearCommand"></span>
|
||||
</div>
|
||||
<div class="listError" data-bind="visible: !dragOver() && '' !== messageListError()">
|
||||
|
|
|
|||
|
|
@ -18,15 +18,20 @@
|
|||
<div class="dateParent actionHandle dragHandle">
|
||||
<span class="date" data-moment-format="SHORT" data-moment-format-title="FULL" data-bind="moment: dateTimeStampInUTC"></span>
|
||||
</div>
|
||||
<div class="threadsParent" data-bind="visible: 1 < threadsLen()">
|
||||
<span class="threads-len">
|
||||
<span class="threads-len-data">
|
||||
<span data-bind="text: threadsLen"></span>
|
||||
<i class="icon-right-mini"></i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="checkedParent">
|
||||
<i class="checkboxMessage" data-bind="css: checked() ? 'checkboxMessage icon-checkbox-checked' : 'checkboxMessage icon-checkbox-unchecked'"></i>
|
||||
</div>
|
||||
<div class="senderParent actionHandle dragHandle">
|
||||
<span class="replyFlag"><i class="icon-reply"></i> </span>
|
||||
<span class="forwardFlag"><i class="icon-forward"></i> </span>
|
||||
<span class="threads-len" data-bind="visible: 1 < threadsLen()">
|
||||
<span class="threads-len-data" data-bind="text: threadsLen"></span>
|
||||
</span>
|
||||
<span class="sender" data-bind="text: senderEmailsString, attr: {'title': senderClearEmailsString}"></span>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,14 @@
|
|||
<div class="dateParent actionHandle dragHandle">
|
||||
<span class="date" data-moment-format="SHORT" data-moment-format-title="FULL" data-bind="moment: dateTimeStampInUTC"></span>
|
||||
</div>
|
||||
<div class="threadsParent" data-bind="visible: 1 < threadsLen()">
|
||||
<span class="threads-len">
|
||||
<span class="threads-len-data">
|
||||
<span data-bind="text: threadsLen"></span>
|
||||
<i class="icon-right-mini"></i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="checkedParent">
|
||||
<i class="checkboxMessage" data-bind="css: checked() ? 'checkboxMessage icon-checkbox-checked' : 'checkboxMessage icon-checkbox-unchecked'"></i>
|
||||
</div>
|
||||
|
|
@ -24,9 +32,6 @@
|
|||
<div class="senderParent actionHandle dragHandle">
|
||||
<span class="replyFlag"><i class="icon-reply"></i> </span>
|
||||
<span class="forwardFlag"><i class="icon-forward"></i> </span>
|
||||
<span class="threads-len" data-bind="visible: 1 < threadsLen()">
|
||||
<span class="threads-len-data" data-bind="text: threadsLen"></span>
|
||||
</span>
|
||||
<span class="sender" data-bind="text: senderEmailsString, attr: {'title': senderClearEmailsString}"></span>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -188,64 +188,6 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div class="btn-group thread-controls pull-right g-ui-user-select-none" style="margin-right: 5px"
|
||||
data-bind="visible: viewThreadsControlVisibility">
|
||||
|
||||
<a class="btn last btn-thin pull-right" data-tooltip-join="bottom"
|
||||
data-bind="command: threadBackCommand, tooltip: 'MESSAGE/BUTTON_THREAD_PREV'">
|
||||
<i class="icon-right-middle"></i>
|
||||
</a>
|
||||
<div class="btn-group pull-right" data-bind="registrateBootstrapDropdown: true, openDropdownTrigger: threadsDropdownTrigger">
|
||||
<a class="btn btn-thin btn-dark-disabled-border dropdown-toggle" id="thread-list-view-dropdown-id" data-toggle="dropdown"
|
||||
href="#" tabindex="-1" data-tooltip-join="bottom"
|
||||
style="margin-left: -1px; margin-right: -1px;"
|
||||
data-bind="command: threadListCommand, tooltip: 'MESSAGE/BUTTON_THREAD_LIST'"
|
||||
>
|
||||
<i class="icon-list"
|
||||
data-bind="css: {'icon-list': !messageListOfThreadsLoading(), 'icon-spinner animated': messageListOfThreadsLoading()}"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right g-ui-menu thread-list"
|
||||
role="menu" aria-labelledby="thread-list-view-dropdown-id"
|
||||
style="min-width: 400px; max-width: 400px; width: 400px; padding: 0"
|
||||
data-bind="css: {'hide-more': !viewThreadMessages.showMore() }"
|
||||
>
|
||||
|
||||
<div data-bind="visible: '' !== viewThreadMessages.error()" style="color: red; text-align: center; padding: 10px">
|
||||
<spam data-bind="text: viewThreadMessages.error"></spam>
|
||||
</div>
|
||||
<div data-bind="visible: messageListOfThreadsLoading" style="text-align: center; padding: 10px">
|
||||
<i class="icon-spinner animated" />
|
||||
</div>
|
||||
<div data-bind="foreach: viewThreadMessages, visible: !messageListOfThreadsLoading()">
|
||||
<li class="e-item thread-list-message real-msg" role="presentation" data-bind="css: {'selected': selected, 'more-that': $parent.viewThreadMessages.limit < $index() }">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" onclick="return false;">
|
||||
<span class="thread-date pull-right" data-moment-format="SHORT" data-bind="moment: timestamp"></span>
|
||||
<div style="text-overflow: ellipsis; overflow: hidden;">
|
||||
<span class="thread-from" data-bind="text: fromAsString"></span>
|
||||
|
||||
</div>
|
||||
<div style="text-overflow: ellipsis; overflow: hidden;">
|
||||
<span class="thread-subject" data-bind="text: subject"></span>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
<div data-bind="visible: !viewThreadMessages.showMore() && !messageListOfThreadsLoading()">
|
||||
<li class="e-item thread-list-message" role="presentation">
|
||||
<a class="e-link menuitem more-threads" href="#" tabindex="-1"
|
||||
onclick="return false"
|
||||
data-i18n="MESSAGE/BUTTON_THREAD_MORE"></a>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="btn first btn-thin pull-right" data-tooltip-join="bottom"
|
||||
data-bind="command: threadForwardCommand, tooltip: 'MESSAGE/BUTTON_THREAD_NEXT'">
|
||||
<i class="icon-left-middle"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</nobr>
|
||||
</div>
|
||||
|
||||
|
|
@ -258,9 +200,6 @@
|
|||
<span class="flagParent">
|
||||
<i class="icon-star-empty flagOff" data-bind="css: {'icon-star flagOn': viewIsFlagged, 'icon-star-empty flagOff': !viewIsFlagged()}"></i>
|
||||
</span>
|
||||
<span class="thread-counter" data-bind="visible: viewThreadsControlVisibility"
|
||||
style="font-weight: normal; margin-right: 5px">(<span
|
||||
data-bind="text: viewThreadsControlDesc"></span>)</span>
|
||||
<b style="color: red; margin-right: 5px" data-bind="visible: viewIsImportant">!</b>
|
||||
<span class="subject" data-bind="text: viewSubject, title: viewSubject"></span>
|
||||
<span class="i18n emptySubjectText" data-i18n="MESSAGE/EMPTY_SUBJECT_TEXT"></span>
|
||||
|
|
@ -380,7 +319,8 @@
|
|||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
style="margin-bottom: 0" data-i18n="[placeholder]MESSAGE/PGP_PASSWORD_INPUT_PLACEHOLDER" data-bind="value: viewPgpPassword, onEnter: function() { decryptPgpEncryptedMessage(message()); }" />
|
||||
</div>
|
||||
<div class="attachmentsPlace" data-bind="visible: message() && message().hasVisibleAttachments(), css: {'selection-mode' : showAttachmnetControls}">
|
||||
<div class="attachmentsPlace" data-bind="visible: message() && message().hasVisibleAttachments(),
|
||||
css: {'selection-mode' : showAttachmnetControls, 'unselectedAttachmentsError': highlightUnselectedAttachments}">
|
||||
<ul class="attachmentList" data-bind="foreach: message() ? message().attachments() : []">
|
||||
<li class="attachmentItem clearfix" draggable="true" data-tooltip-join="top"
|
||||
data-bind="visible: !isLinked, event: { 'dragstart': eventDragStart }, attr: { 'title': fileName }, css: {'checked': checked}">
|
||||
|
|
@ -392,7 +332,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="showPreview">
|
||||
<a data-bind="css: {'attachmentImagePreview': isImage()}, attr: { 'title': fileName, 'href': linkPreviewMain(), 'data-index': $index }" target="_blank">
|
||||
<a data-bind="css: {'attachmentImagePreview': isImage(), 'visible': !isLinked}, attr: { 'title': fileName, 'href': linkPreviewMain() }" target="_blank">
|
||||
<div class="iconMain">
|
||||
<i class="attachmentIcon attachmentMainIcon" data-bind="css: iconClass()"></i>
|
||||
<i class="attachmentIconText attachmentMainIconText" data-bind="text: iconText()"></i>
|
||||
|
|
@ -430,14 +370,43 @@
|
|||
<i class="icon-cog controls-handle" data-bind="visible: allowAttachmnetControls() && !showAttachmnetControls(), click: function () { showAttachmnetControls(true) }"></i>
|
||||
</div>
|
||||
<div class="attachmentsControls"
|
||||
data-bind="visible: showAttachmnetControls() && message() && message().hasVisibleAttachments()">
|
||||
<i class="icon-file-zip"></i>
|
||||
|
||||
<span class="g-ui-link">Download as zip</span>
|
||||
|
||||
<i class="icon-cloud-up"></i>
|
||||
|
||||
<span class="g-ui-link">Save to ownCloud</span>
|
||||
data-bind="visible: showAttachmnetControls() && message() && message().hasVisibleAttachments()">
|
||||
|
||||
<span data-bind="visible: downloadAsZipAllowed">
|
||||
<i class="icon-remove iconcolor-red" data-bind="visible: downloadAsZipError"></i>
|
||||
<i class="icon-file-zip" data-bind="visible: !downloadAsZipError(),
|
||||
css: {'icon-file-zip': !downloadAsZipLoading(), 'icon-spinner animated': downloadAsZipLoading()}"></i>
|
||||
|
||||
<span class="g-ui-link" data-bind="click: downloadAsZip">
|
||||
<span class="i18n" data-i18n="MESSAGE/LINK_DOWNLOAD_AS_ZIP"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span data-bind="visible: saveToOwnCloudAllowed">
|
||||
|
||||
<i class="icon-remove iconcolor-red" data-bind="visible: saveToOwnCloudError"></i>
|
||||
<i class="icon-ok iconcolor-green" data-bind="visible: saveToOwnCloudSuccess"></i>
|
||||
<i class="icon-cloud-up" data-bind="visible: !saveToOwnCloudSuccess() && !saveToOwnCloudError(),
|
||||
css: {'icon-cloud-up': !saveToOwnCloudLoading(), 'icon-spinner animated': saveToOwnCloudLoading()}"></i>
|
||||
|
||||
<span class="g-ui-link" data-bind="click: saveToOwnCloud">
|
||||
<span class="i18n" data-i18n="MESSAGE/LINK_SAVE_TO_OWNCLOUD"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span data-bind="visible: saveToDropboxAllowed">
|
||||
|
||||
<i class="icon-remove iconcolor-red" data-bind="visible: saveToDropboxError"></i>
|
||||
<i class="icon-ok iconcolor-green" data-bind="visible: saveToDropboxSuccess"></i>
|
||||
<i class="icon-dropbox" data-bind="visible: !saveToDropboxSuccess() && !saveToDropboxError(),
|
||||
css: {'icon-dropbox': !saveToDropboxLoading(), 'icon-spinner animated': saveToDropboxLoading()}"></i>
|
||||
|
||||
<span class="g-ui-link" data-bind="click: saveToDropbox">
|
||||
<span class="i18n" data-i18n="MESSAGE/LINK_SAVE_TO_DROPBOX"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- // --->
|
||||
<button type="button" class="close" style="margin-right: 5px;"
|
||||
data-bind="click: function () { showAttachmnetControls(false); }">×</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -54,15 +54,15 @@
|
|||
<span class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE"></span>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select data-bind="value: selectedDateValue">
|
||||
<option value="-1" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_ALL"></option>
|
||||
<option value="3" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_3_DAYS"></option>
|
||||
<option value="7" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_7_DAYS"></option>
|
||||
<option value="30" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_MONTH"></option>
|
||||
<option value="90" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_3_MONTHS"></option>
|
||||
<option value="180" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_6_MONTHS"></option>
|
||||
<option value="365" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_YEAR"></option>
|
||||
</select>
|
||||
<div data-bind="component: {
|
||||
name: 'Select',
|
||||
params: {
|
||||
options: selectedDates,
|
||||
value: selectedDateValue,
|
||||
optionsText: 'name',
|
||||
optionsValue: 'id'
|
||||
}
|
||||
}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@
|
|||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/DELETING_ASK"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<td class="delete-folder-parent">
|
||||
<span class="delete-folder" data-bind="visible: canBeDeleted() && !deleteAccess(), click: function (oFolder) { $root.folderForDeletion(oFolder); }">
|
||||
<i class="icon-trash"></i>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<td class="subscribe-folder-parent">
|
||||
<span class="unsubscribe-folder" data-bind="visible: canBeSubScribed() && !subScribed(), click: function(oFolder) { $root.subscribeFolder(oFolder); }">
|
||||
<i class="icon-eye"></i>
|
||||
</span>
|
||||
|
|
@ -25,5 +25,13 @@
|
|||
<i class="icon-eye"></i>
|
||||
</span>
|
||||
</td>
|
||||
<td class="check-folder-parent">
|
||||
<span class="uncheck-folder" data-bind="visible: canBeChecked() && subScribed() && !checkable(), click: function(oFolder) { $root.checkableTrueFolder(oFolder); }">
|
||||
<i class="icon-check-mark-circle-two"></i>
|
||||
</span>
|
||||
<span class="check-folder" data-bind="visible: canBeChecked() && subScribed() && checkable(), click: function(oFolder) { $root.checkableFalseFolder(oFolder); }">
|
||||
<i class="icon-check-mark-circle-two"></i>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- ko template: { name: 'SettingsFolderItem', foreach: subFolders } --><!-- /ko -->
|
||||
|
|
@ -1,37 +1,45 @@
|
|||
<div class="b-settings-folders g-ui-user-select-none" data-bind="css: { 'ignore-folder-subscribe': !useImapSubscribe }">
|
||||
<div class="form-horizontal">
|
||||
<div class="legend">
|
||||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/LEGEND_FOLDERS"></span>
|
||||
|
||||
<i class="icon-spinner animated" style="margin-top: 5px" data-bind="visible: loading"></i>
|
||||
<div class="b-settings-folders g-ui-user-select-none row" data-bind="css: { 'ignore-folder-subscribe': !useImapSubscribe }">
|
||||
<div class="span8">
|
||||
<div class="form-horizontal">
|
||||
<div class="legend">
|
||||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/LEGEND_FOLDERS"></span>
|
||||
|
||||
<i class="icon-spinner animated" style="margin-top: 5px" data-bind="visible: loading"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn" data-bind="click: createFolder">
|
||||
<i class="icon-folder-add"></i>
|
||||
|
||||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/BUTTON_CREATE"></span>
|
||||
</a>
|
||||
|
||||
<a class="btn" data-bind="click: systemFolder">
|
||||
<i class="icon-wrench"></i>
|
||||
|
||||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/BUTTON_SYSTEM"></span>
|
||||
</a>
|
||||
<div class="alert folders-list-error" data-bind="visible: folderList.optimized">
|
||||
<b class="i18n" data-i18n="SETTINGS_FOLDERS/TO_MANY_FOLDERS_DESC_1"></b>
|
||||
<br />
|
||||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/TO_MANY_FOLDERS_DESC_2"></span>
|
||||
</div>
|
||||
<div class="alert folders-list-error" data-bind="visible: '' !== folderList.error()">
|
||||
<button type="button" class="close" data-bind="click: function () { folderList.error(''); }">×</button>
|
||||
<span data-bind="text: folderList.error"></span>
|
||||
</div>
|
||||
<table class="table table-hover list-table" data-bind="i18nUpdate: folderList" style="margin-top: 40px">
|
||||
<colgroup>
|
||||
<col />
|
||||
<col style="width: 1%" />
|
||||
<col style="width: 1%" />
|
||||
<col style="width: 1%" />
|
||||
</colgroup>
|
||||
<tbody data-bind="template: { name: 'SettingsFolderItem', foreach: folderList }"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="span4" style="position: relative" data-bind="visible: '' !== folderListHelp()">
|
||||
<div class="alert alert-info" style="position: fixed">
|
||||
<span data-bind="text: folderListHelp"></span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn" data-bind="click: createFolder">
|
||||
<i class="icon-folder-add"></i>
|
||||
|
||||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/BUTTON_CREATE"></span>
|
||||
</a>
|
||||
|
||||
<a class="btn" data-bind="click: systemFolder">
|
||||
<i class="icon-wrench"></i>
|
||||
|
||||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/BUTTON_SYSTEM"></span>
|
||||
</a>
|
||||
<div class="alert folders-list-error" data-bind="visible: folderList.optimized">
|
||||
<b class="i18n" data-i18n="SETTINGS_FOLDERS/TO_MANY_FOLDERS_DESC_1"></b>
|
||||
<br />
|
||||
<span class="i18n" data-i18n="SETTINGS_FOLDERS/TO_MANY_FOLDERS_DESC_2"></span>
|
||||
</div>
|
||||
<div class="alert folders-list-error" data-bind="visible: '' !== folderList.error()">
|
||||
<button type="button" class="close" data-bind="click: function () { folderList.error(''); }">×</button>
|
||||
<span data-bind="text: folderList.error"></span>
|
||||
</div>
|
||||
<table class="table table-hover list-table" data-bind="i18nUpdate: folderList" style="margin-top: 40px">
|
||||
<colgroup>
|
||||
<col />
|
||||
<col style="width: 1%" />
|
||||
<col style="width: 1%" />
|
||||
</colgroup>
|
||||
<tbody data-bind="template: { name: 'SettingsFolderItem', foreach: folderList }"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue