Contacts tags support (unstable - step 1).

+ Small fixes
This commit is contained in:
RainLoop Team 2014-05-13 12:29:36 +04:00
parent 74fa12c000
commit 0ca00dec40
34 changed files with 1790 additions and 801 deletions

View file

@ -184,6 +184,7 @@ module.exports = function (grunt) {
"dev/Knoin/Knoin.js",
"dev/Models/EmailModel.js",
"dev/Models/ContactTagModel.js",
"dev/ViewModels/PopupsDomainViewModel.js",
"dev/ViewModels/PopupsPluginViewModel.js",
@ -264,6 +265,7 @@ module.exports = function (grunt) {
"dev/Models/EmailModel.js",
"dev/Models/ContactModel.js",
"dev/Models/ContactPropertyModel.js",
"dev/Models/ContactTagModel.js",
"dev/Models/AttachmentModel.js",
"dev/Models/ComposeAttachmentModel.js",
"dev/Models/MessageModel.js",
@ -459,10 +461,10 @@ module.exports = function (grunt) {
require('wrench').rmdirSyncRecursive(releasesSrcPath);
}
});
// uglify
grunt.registerTask('rlmin', ['uglify:min_app', 'uglify:min_admin']);
// uglify (optional)
grunt.registerTask('rl', ['uglify:rl']);
grunt.registerTask('nano', ['uglify:nano']);

View file

@ -12,7 +12,7 @@ function RainLoopApp()
this.oRemote = null;
this.oCache = null;
this.oMoveCache = {};
this.quotaDebounce = _.debounce(this.quota, 1000 * 30);
this.moveOrDeleteResponseHelper = _.bind(this.moveOrDeleteResponseHelper, this);
@ -189,11 +189,11 @@ RainLoopApp.prototype.contactsSync = function (fResultFunc)
{
return false;
}
oContacts.syncing(true);
RL.remote().contactsSync(function (sResult, oData) {
oContacts.syncing(false);
if (fResultFunc)
@ -211,14 +211,14 @@ RainLoopApp.prototype.messagesMoveTrigger = function ()
self = this,
sSpamFolder = RL.data().spamFolder()
;
_.each(this.oMoveCache, function (oItem) {
var
bSpam = sSpamFolder === oItem['To'],
bHam = !bSpam && sSpamFolder === oItem['From'] && 'INBOX' === oItem['To']
;
RL.remote().messagesMove(self.moveOrDeleteResponseHelper, oItem['From'], oItem['To'], oItem['Uid'],
bSpam ? 'SPAM' : (bHam ? 'HAM' : ''));
});
@ -272,7 +272,7 @@ RainLoopApp.prototype.moveOrDeleteResponseHelper = function (sResult, oData)
else
{
RL.cache().setFolderHash(RL.data().currentFolderFullNameRaw(), '');
if (oData && -1 < Utils.inArray(oData.ErrorCode,
[Enums.Notification.CantMoveMessage, Enums.Notification.CantCopyMessage]))
{
@ -345,14 +345,14 @@ RainLoopApp.prototype.deleteMessagesFromFolder = function (iDeleteType, sFromFol
{
kn.showScreenPopup(PopupsFolderSystemViewModel, [nSetSystemFoldersNotification]);
}
else if (!bUseFolder || (Enums.FolderType.Trash === iDeleteType &&
else if (!bUseFolder || (Enums.FolderType.Trash === iDeleteType &&
(sFromFolderFullNameRaw === oData.spamFolder() || sFromFolderFullNameRaw === oData.trashFolder())))
{
kn.showScreenPopup(PopupsAskViewModel, [Utils.i18n('POPUPS_ASK/DESC_WANT_DELETE_MESSAGES'), function () {
self.messagesDeleteHelper(sFromFolderFullNameRaw, aUidForRemove);
oData.removeMessagesFromList(sFromFolderFullNameRaw, aUidForRemove);
}]);
}
else if (oMoveFolder)
@ -469,7 +469,7 @@ RainLoopApp.prototype.reloadOpenPgpKeys = function ()
RainLoopApp.prototype.accountsAndIdentities = function ()
{
var oRainLoopData = RL.data();
oRainLoopData.accountsLoading(true);
oRainLoopData.identitiesLoading(true);
@ -477,14 +477,14 @@ RainLoopApp.prototype.accountsAndIdentities = function ()
oRainLoopData.accountsLoading(false);
oRainLoopData.identitiesLoading(false);
if (Enums.StorageResultType.Success === sResult && oData.Result)
{
var
var
sParentEmail = RL.settingsGet('ParentEmail'),
sAccountEmail = oRainLoopData.accountEmail()
;
sParentEmail = '' === sParentEmail ? sAccountEmail : sParentEmail;
if (Utils.isArray(oData.Result['Accounts']))
@ -493,21 +493,21 @@ RainLoopApp.prototype.accountsAndIdentities = function ()
return new AccountModel(sValue, sValue !== sParentEmail);
}));
}
if (Utils.isArray(oData.Result['Identities']))
{
oRainLoopData.identities(_.map(oData.Result['Identities'], function (oIdentityData) {
var
var
sId = Utils.pString(oIdentityData['Id']),
sEmail = Utils.pString(oIdentityData['Email']),
oIdentity = new IdentityModel(sId, sEmail, sId !== sAccountEmail)
;
oIdentity.name(Utils.pString(oIdentityData['Name']));
oIdentity.replyTo(Utils.pString(oIdentityData['ReplyTo']));
oIdentity.bcc(Utils.pString(oIdentityData['Bcc']));
return oIdentity;
}));
}
@ -518,7 +518,7 @@ RainLoopApp.prototype.accountsAndIdentities = function ()
RainLoopApp.prototype.quota = function ()
{
this.remote().quota(function (sResult, oData) {
if (Enums.StorageResultType.Success === sResult && oData && oData.Result &&
if (Enums.StorageResultType.Success === sResult && oData && oData.Result &&
Utils.isArray(oData.Result) && 1 < oData.Result.length &&
Utils.isPosNumeric(oData.Result[0], true) && Utils.isPosNumeric(oData.Result[1], true))
{
@ -555,7 +555,7 @@ RainLoopApp.prototype.folderInformation = function (sFolder, aList)
if (oFolder)
{
oFolder.interval = iUtc;
if (oData.Result.Hash)
{
RL.cache().setFolderHash(oData.Result.Folder, oData.Result.Hash);
@ -652,7 +652,7 @@ RainLoopApp.prototype.folderInformationMultiply = function (bBoot)
if (oData && oData.Result && oData.Result.List && Utils.isNonEmptyArray(oData.Result.List))
{
_.each(oData.Result.List, function (oItem) {
var
aList = [],
sHash = RL.cache().getFolderHash(oItem.Folder),
@ -936,7 +936,7 @@ RainLoopApp.prototype.folderListOptionsBuilder = function (aSystem, aList, aDisa
}
}
}
if (oItem.subScribed() && 0 < oItem.subFolders().length)
{
aResult = aResult.concat(RL.folderListOptionsBuilder([], oItem.subFolders(), aDisabled, [],
@ -970,10 +970,21 @@ RainLoopApp.prototype.getAutocomplete = function (sQuery, fCallback)
{
fCallback([]);
}
}, sQuery);
};
/**
* @param {string} sQuery
* @param {Function} fCallback
*/
RainLoopApp.prototype.getContactsTagsAutocomplete = function (sQuery, fCallback)
{
fCallback(_.filter(RL.data().contactTags(), function (oContactTag) {
return oContactTag && oContactTag.filterHelper(sQuery);
}));
};
RainLoopApp.prototype.emailsPicsHashes = function ()
{
RL.remote().emailsPicsHashes(function (sResult, oData) {
@ -1021,7 +1032,7 @@ RainLoopApp.prototype.bootstart = function ()
bFacebook = RL.settingsGet('AllowFacebookSocial'),
bTwitter = RL.settingsGet('AllowTwitterSocial')
;
if (!RL.settingsGet('ChangePasswordIsAllowed'))
{
Utils.removeSettingsViewModel(SettingsChangePasswordScreen);
@ -1036,7 +1047,7 @@ RainLoopApp.prototype.bootstart = function ()
{
Utils.removeSettingsViewModel(SettingsAccounts);
}
if (RL.settingsGet('AllowIdentities'))
{
Utils.removeSettingsViewModel(SettingsIdentity);
@ -1045,7 +1056,7 @@ RainLoopApp.prototype.bootstart = function ()
{
Utils.removeSettingsViewModel(SettingsIdentities);
}
if (!RL.settingsGet('OpenPGP'))
{
Utils.removeSettingsViewModel(SettingsOpenPGP);
@ -1060,12 +1071,12 @@ RainLoopApp.prototype.bootstart = function ()
{
Utils.removeSettingsViewModel(SettingsSocialScreen);
}
if (!RL.settingsGet('AllowThemes'))
{
Utils.removeSettingsViewModel(SettingsThemes);
}
Utils.initOnStartOrLangChange(function () {
$.extend(true, $.magnificPopup.defaults, {
@ -1113,9 +1124,9 @@ RainLoopApp.prototype.bootstart = function ()
{
RL.data().openpgpKeyring = new window.openpgp.Keyring();
RL.data().allowOpenPGP(true);
RL.pub('openpgp.init');
RL.reloadOpenPgpKeys();
}
}
@ -1127,7 +1138,7 @@ RainLoopApp.prototype.bootstart = function ()
}
kn.startScreens([MailBoxScreen, SettingsScreen]);
if (bGoogle || bFacebook || bTwitter)
{
RL.socialUsers(true);
@ -1163,7 +1174,7 @@ RainLoopApp.prototype.bootstart = function ()
window.setInterval(function () {
RL.contactsSync();
}, iContactsSyncInterval * 60000 + 5000);
_.delay(function () {
RL.contactsSync();
}, 5000);
@ -1223,7 +1234,7 @@ RainLoopApp.prototype.bootstart = function ()
Utils.initLayoutResizer('#rl-left', '#rl-right', Enums.ClientSideKeyName.FolderListSize);
});
}
}, this));
}
else

View file

@ -74,7 +74,7 @@ ko.bindingHandlers.tooltip3 = {
'init': function (oElement) {
var $oEl = $(oElement);
$oEl.tooltip({
'container': 'body',
'trigger': 'hover manual',
@ -93,7 +93,7 @@ ko.bindingHandlers.tooltip3 = {
$document.click(function () {
$oEl.tooltip('hide');
});
},
'update': function (oElement, fValueAccessor) {
var sValue = ko.utils.unwrapObservable(fValueAccessor());
@ -124,7 +124,7 @@ ko.bindingHandlers.openDropdownTrigger = {
$el.find('.dropdown-toggle').dropdown('toggle');
Utils.detectDropdownVisibility();
}
fValueAccessor()(false);
}
}
@ -221,7 +221,7 @@ ko.bindingHandlers.clickOnTrue = {
ko.bindingHandlers.modal = {
'init': function (oElement, fValueAccessor) {
$(oElement).toggleClass('fade', !Globals.bMobileDevice).modal({
'keyboard': false,
'show': ko.utils.unwrapObservable(fValueAccessor())
@ -536,11 +536,19 @@ ko.bindingHandlers.emailsTags = {
'init': function(oElement, fValueAccessor) {
var
$oEl = $(oElement),
fValue = fValueAccessor()
fValue = fValueAccessor(),
fFocusCallback = function (bValue) {
if (fValue && fValue.focusTrigger)
{
fValue.focusTrigger(bValue);
}
}
;
$oEl.inputosaurus({
'parseOnBlur': true,
'allowDragAndDrop': true,
'focusCallback': fFocusCallback,
'inputDelimiters': [',', ';'],
'autoCompleteSource': function (oData, fResponse) {
RL.getAutocomplete(oData.term, function (aData) {
@ -551,12 +559,12 @@ ko.bindingHandlers.emailsTags = {
},
'parseHook': function (aInput) {
return _.map(aInput, function (sInputValue) {
var
sValue = Utils.trim(sInputValue),
oEmail = null
;
if ('' !== sValue)
{
oEmail = new EmailModel();
@ -586,8 +594,83 @@ ko.bindingHandlers.emailsTags = {
if (fValue.focusTrigger)
{
fValue.focusTrigger.subscribe(function () {
$oEl.inputosaurus('focus');
fValue.focusTrigger.subscribe(function (bValue) {
if (bValue)
{
$oEl.inputosaurus('focus');
}
});
}
}
};
ko.bindingHandlers.contactTags = {
'init': function(oElement, fValueAccessor) {
var
$oEl = $(oElement),
fValue = fValueAccessor(),
fFocusCallback = function (bValue) {
if (fValue && fValue.focusTrigger)
{
fValue.focusTrigger(bValue);
}
}
;
$oEl.inputosaurus({
'parseOnBlur': true,
'allowDragAndDrop': false,
'focusCallback': fFocusCallback,
'inputDelimiters': [';'],
'outputDelimiter': ';',
'autoCompleteSource': function (oData, fResponse) {
RL.getContactsTagsAutocomplete(oData.term, function (aData) {
fResponse(_.map(aData, function (oTagItem) {
return oTagItem.toLine(false);
}));
});
},
'parseHook': function (aInput) {
return _.map(aInput, function (sInputValue) {
var
sValue = Utils.trim(sInputValue),
oTag = null
;
if ('' !== sValue)
{
oTag = new ContactTagModel();
oTag.name(sValue);
return [oTag.toLine(false), oTag];
}
return [sValue, null];
});
},
'change': _.bind(function (oEvent) {
$oEl.data('ContactsTagsValue', oEvent.target.value);
fValue(oEvent.target.value);
}, this)
});
fValue.subscribe(function (sValue) {
if ($oEl.data('ContactsTagsValue') !== sValue)
{
$oEl.val(sValue);
$oEl.data('ContactsTagsValue', sValue);
$oEl.inputosaurus('refresh');
}
});
if (fValue.focusTrigger)
{
fValue.focusTrigger.subscribe(function (bValue) {
if (bValue)
{
$oEl.inputosaurus('focus');
}
});
}
}
@ -737,7 +820,7 @@ ko.observable.fn.validateFunc = function (fFunc)
this.subscribe(function (sValue) {
this.hasFuncError(!fFunc(sValue));
}, this);
this.valueHasMutated();
}

View file

@ -325,13 +325,13 @@ Utils.i18nToNode = function (oElement)
{
jqThis.html(Utils.i18n(sKey));
}
sKey = jqThis.data('i18n-placeholder');
if (sKey)
{
jqThis.attr('placeholder', Utils.i18n(sKey));
}
sKey = jqThis.data('i18n-title');
if (sKey)
{
@ -469,7 +469,7 @@ Utils.fixLongSubject = function (sSubject)
;
sSubject = Utils.trim(sSubject.replace(/[\s]+/, ' '));
do
{
oMatch = /^Re(\[([\d]+)\]|):[\s]{0,3}Re(\[([\d]+)\]|):/ig.exec(sSubject);
@ -598,9 +598,9 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.CantSaveSettings] = Utils.i18n('NOTIFICATIONS/CANT_SAVE_SETTINGS');
NotificationI18N[Enums.Notification.CantSavePluginSettings] = Utils.i18n('NOTIFICATIONS/CANT_SAVE_PLUGIN_SETTINGS');
NotificationI18N[Enums.Notification.DomainAlreadyExists] = Utils.i18n('NOTIFICATIONS/DOMAIN_ALREADY_EXISTS');
NotificationI18N[Enums.Notification.CantInstallPackage] = Utils.i18n('NOTIFICATIONS/CANT_INSTALL_PACKAGE');
NotificationI18N[Enums.Notification.CantDeletePackage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_PACKAGE');
NotificationI18N[Enums.Notification.InvalidPluginPackage] = Utils.i18n('NOTIFICATIONS/INVALID_PLUGIN_PACKAGE');
@ -613,7 +613,7 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.DemoSendMessageError] = Utils.i18n('NOTIFICATIONS/DEMO_SEND_MESSAGE_ERROR');
NotificationI18N[Enums.Notification.AccountAlreadyExists] = Utils.i18n('NOTIFICATIONS/ACCOUNT_ALREADY_EXISTS');
NotificationI18N[Enums.Notification.MailServerError] = Utils.i18n('NOTIFICATIONS/MAIL_SERVER_ERROR');
NotificationI18N[Enums.Notification.UnknownNotification] = Utils.i18n('NOTIFICATIONS/UNKNOWN_ERROR');
NotificationI18N[Enums.Notification.UnknownError] = Utils.i18n('NOTIFICATIONS/UNKNOWN_ERROR');
@ -765,7 +765,7 @@ Utils.initDataConstructorBySettings = function (oData)
oData.contactsAutosave = ko.observable(false);
Globals.sAnimationType = Enums.InterfaceAnimation.Full;
oData.allowThemes = ko.observable(true);
oData.allowCustomLogin = ko.observable(false);
oData.allowLanguagesOnSettings = ko.observable(true);
@ -775,7 +775,7 @@ Utils.initDataConstructorBySettings = function (oData)
oData.useThreads = ko.observable(true);
oData.replySameFolder = ko.observable(true);
oData.useCheckboxesInList = ko.observable(true);
oData.layout = ko.observable(Enums.Layout.SidePreview);
oData.usePreviewPane = ko.computed(function () {
return Enums.Layout.NoPreview !== oData.layout();
@ -1058,7 +1058,7 @@ Utils.setExpandedFolder = function (sFullNameHash, bExpanded)
{
aExpandedList = [];
}
if (bExpanded)
{
aExpandedList.push(sFullNameHash);
@ -1075,7 +1075,7 @@ Utils.setExpandedFolder = function (sFullNameHash, bExpanded)
Utils.initLayoutResizer = function (sLeft, sRight, sClientSideKeyName)
{
var
iDisabledWidth = 65,
iDisabledWidth = 60,
iMinWidth = 155,
oLeft = $(sLeft),
oRight = $(sRight),
@ -1153,7 +1153,7 @@ Utils.initBlockquoteSwitcher = function (oMessageTextBody)
var $oList = $('blockquote:not(.rl-bq-switcher)', oMessageTextBody).filter(function () {
return 0 === $(this).parent().closest('blockquote', oMessageTextBody).length;
});
if ($oList && 0 < $oList.length)
{
$oList.each(function () {
@ -1563,7 +1563,7 @@ Utils.resizeAndCrop = function (sUrl, iValue, fCallback)
oCtx.fillStyle = '#fff';
oCtx.fillRect(0, 0, iValue, iValue);
oCtx.drawImage(this, aDiff[0] / 2, aDiff[1] / 2, this.width - aDiff[0], this.height - aDiff[1], 0, 0, iValue, iValue);
fCallback(oCanvas.toDataURL('image/jpeg'));
};

View file

@ -17,7 +17,7 @@ function KnoinAbstractViewModel(sPosition, sTemplate)
this.viewModelName = '';
this.viewModelVisibility = ko.observable(false);
this.modalVisibility = ko.observable(false).extend({'rateLimit': 0});
this.viewModelDom = null;
}
@ -72,14 +72,21 @@ KnoinAbstractViewModel.prototype.restoreKeyScope = function ()
RL.data().keyScope(this.sCurrentKeyScope);
};
KnoinAbstractViewModel.prototype.registerPopupEscapeKey = function ()
KnoinAbstractViewModel.prototype.registerPopupKeyDown = function ()
{
var self = this;
$window.on('keydown', function (oEvent) {
if (oEvent && Enums.EventKeyCode.Esc === oEvent.keyCode && self.modalVisibility && self.modalVisibility())
if (oEvent && self.modalVisibility && self.modalVisibility())
{
Utils.delegateRun(self, 'cancelCommand');
return false;
if (!this.bDisabeCloseOnEsc && Enums.EventKeyCode.Esc === oEvent.keyCode)
{
Utils.delegateRun(self, 'cancelCommand');
return false;
}
else if (Enums.EventKeyCode.Backspace === oEvent.keyCode && !Utils.inFocus())
{
return false;
}
}
return true;

View file

@ -83,7 +83,7 @@ Knoin.prototype.buildViewModel = function (ViewModelClass, oScreen)
ViewModelClass.__builded = true;
ViewModelClass.__vm = oViewModel;
oViewModel.data = RL.data();
oViewModel.viewModelName = ViewModelClass.__name;
if (oViewModelPlace && 1 === oViewModelPlace.length)
@ -126,19 +126,19 @@ Knoin.prototype.buildViewModel = function (ViewModelClass, oScreen)
self.viewModelDom.hide();
}, 300);
}
}, oViewModel);
}
Plugins.runHook('view-model-pre-build', [ViewModelClass.__name, oViewModel, oViewModelDom]);
ko.applyBindings(oViewModel, oViewModelDom[0]);
Utils.delegateRun(oViewModel, 'onBuild', [oViewModelDom]);
if (oViewModel && 'Popups' === sPosition && !oViewModel.bDisabeCloseOnEsc)
if (oViewModel && 'Popups' === sPosition)
{
oViewModel.registerPopupEscapeKey();
oViewModel.registerPopupKeyDown();
}
Plugins.runHook('view-model-post-build', [ViewModelClass.__name, oViewModel, oViewModelDom]);
}
else
@ -240,7 +240,7 @@ Knoin.prototype.screenOnRoute = function (sScreenName, sSubPart)
}
_.defer(function () {
// hide screen
if (self.oCurrentScreen)
{
@ -336,7 +336,7 @@ Knoin.prototype.startScreens = function (aScreensClasses)
{
oScreen.__started = true;
oScreen.__start();
Plugins.runHook('screen-pre-start', [oScreen.screenName(), oScreen]);
Utils.delegateRun(oScreen, 'onStart');
Plugins.runHook('screen-post-start', [oScreen.screenName(), oScreen]);

View file

@ -8,6 +8,7 @@ function ContactModel()
this.idContact = 0;
this.display = '';
this.properties = [];
this.tags = '';
this.readOnly = false;
this.focused = ko.observable(false);
@ -21,11 +22,11 @@ function ContactModel()
*/
ContactModel.prototype.getNameAndEmailHelper = function ()
{
var
var
sName = '',
sEmail = ''
;
if (Utils.isNonEmptyArray(this.properties))
{
_.each(this.properties, function (aProperty) {
@ -58,6 +59,7 @@ ContactModel.prototype.parse = function (oItem)
this.idContact = Utils.pInt(oItem['IdContact']);
this.display = Utils.pString(oItem['Display']);
this.readOnly = !!oItem['ReadOnly'];
this.tags = '';
if (Utils.isNonEmptyArray(oItem['Properties']))
{
@ -69,6 +71,11 @@ ContactModel.prototype.parse = function (oItem)
}, this);
}
if (Utils.isNonEmptyArray(oItem['Tags']))
{
this.tags = oItem['Tags'].join(';');
}
bResult = true;
}

View file

@ -0,0 +1,45 @@
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
/**
* @constructor
*/
function ContactTagModel()
{
this.idContactTag = 0;
this.name = ko.observable('');
this.readOnly = false;
}
ContactTagModel.prototype.parse = function (oItem)
{
var bResult = false;
if (oItem && 'Object/Tag' === oItem['@Object'])
{
this.idContact = Utils.pInt(oItem['IdContactTag']);
this.name(Utils.pString(oItem['Name']));
this.readOnly = !!oItem['ReadOnly'];
bResult = true;
}
return bResult;
};
/**
* @param {string} sSearch
* @return {boolean}
*/
ContactTagModel.prototype.filterHelper = function (sSearch)
{
return this.name().toLowerCase().indexOf(sSearch.toLowerCase()) !== -1;
};
/**
* @param {boolean=} bEncodeHtml = false
* @return {string}
*/
ContactTagModel.prototype.toLine = function (bEncodeHtml)
{
return (Utils.isUnd(bEncodeHtml) ? false : !!bEncodeHtml) ?
Utils.encodeHtml(this.name()) : this.name();
};

View file

@ -7,7 +7,7 @@
function WebMailAjaxRemoteStorage()
{
AbstractAjaxRemoteStorage.call(this);
this.oRequests = {};
}
@ -686,11 +686,12 @@ WebMailAjaxRemoteStorage.prototype.contacts = function (fCallback, iOffset, iLim
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.contactSave = function (fCallback, sRequestUid, sUid, aProperties)
WebMailAjaxRemoteStorage.prototype.contactSave = function (fCallback, sRequestUid, sUid, sTags, aProperties)
{
this.defaultRequest(fCallback, 'ContactSave', {
'RequestUid': sRequestUid,
'Uid': Utils.trim(sUid),
'Tags': Utils.trim(sTags),
'Properties': aProperties
});
};

View file

@ -83,13 +83,14 @@ function WebMailDataStorage()
this.identitiesLoading = ko.observable(false).extend({'throttle': 100});
// contacts
this.contactTags = ko.observableArray([]);
this.contacts = ko.observableArray([]);
this.contacts.loading = ko.observable(false).extend({'throttle': 200});
this.contacts.importing = ko.observable(false).extend({'throttle': 200});
this.contacts.syncing = ko.observable(false).extend({'throttle': 200});
this.contacts.exportingVcf = ko.observable(false).extend({'throttle': 200});
this.contacts.exportingCsv = ko.observable(false).extend({'throttle': 200});
this.allowContactsSync = ko.observable(false);
this.enableContactsSync = ko.observable(false);
this.contactsSyncUrl = ko.observable('');
@ -229,11 +230,11 @@ function WebMailDataStorage()
this.messageListEndFolder = ko.observable('');
this.messageListEndSearch = ko.observable('');
this.messageListEndPage = ko.observable(1);
this.messageListEndHash = ko.computed(function () {
return this.messageListEndFolder() + '|' + this.messageListEndSearch() + '|' + this.messageListEndPage();
}, this);
this.messageListPageCount = ko.computed(function () {
var iPage = Math.ceil(this.messageListCount() / this.messagesPerPage());
return 0 >= iPage ? 1 : iPage;
@ -266,7 +267,7 @@ function WebMailDataStorage()
this.messageListCompleteLoading.subscribe(function (bValue) {
this.messageListCompleteLoadingThrottle(bValue);
}, this);
this.messageList.subscribe(_.debounce(function (aList) {
_.each(aList, function (oItem) {
if (oItem.newForAnimation())
@ -283,7 +284,7 @@ function WebMailDataStorage()
this.messageLoadingThrottle = ko.observable(false).extend({'throttle': 50});
this.message.focused = ko.observable(false);
this.message.subscribe(function (oMessage) {
if (!oMessage)
{
@ -313,7 +314,7 @@ function WebMailDataStorage()
RL.data().keyScope(Enums.KeyState.MessageList);
}
});
this.folderList.focused.subscribe(function (bValue) {
if (bValue)
{
@ -324,11 +325,11 @@ function WebMailDataStorage()
RL.data().keyScope(Enums.KeyState.MessageList);
}
});
this.messageLoading.subscribe(function (bValue) {
this.messageLoadingThrottle(bValue);
}, this);
this.messageFullScreenMode = ko.observable(false);
this.messageError = ko.observable('');
@ -349,13 +350,13 @@ function WebMailDataStorage()
}, this);
this.currentMessage = ko.observable(null);
this.messageListChecked = ko.computed(function () {
return _.filter(this.messageList(), function (oItem) {
return oItem.checked();
});
}, this).extend({'rateLimit': 0});
this.hasCheckedMessages = ko.computed(function () {
return 0 < this.messageListChecked().length;
}, this).extend({'rateLimit': 0});
@ -442,7 +443,7 @@ WebMailDataStorage.prototype.purgeMessageBodyCache = function()
oMessagesBodiesDom = null,
iEnd = Globals.iMessageBodyCacheCount - Consts.Values.MessageBodyCacheLimit
;
if (0 < iEnd)
{
oMessagesBodiesDom = this.messagesBodiesDom();
@ -475,7 +476,7 @@ WebMailDataStorage.prototype.populateDataOnStart = function()
this.accountIncLogin(RL.settingsGet('IncLogin'));
this.accountOutLogin(RL.settingsGet('OutLogin'));
this.projectHash(RL.settingsGet('ProjectHash'));
this.displayName(RL.settingsGet('DisplayName'));
this.replyTo(RL.settingsGet('ReplyTo'));
this.signature(RL.settingsGet('Signature'));
@ -517,7 +518,7 @@ WebMailDataStorage.prototype.initUidNextAndNewMessages = function (sFolder, sUid
{
oNotification.show();
}
window.setTimeout((function (oLocalNotifications) {
return function () {
if (oLocalNotifications.cancel)
@ -720,7 +721,7 @@ WebMailDataStorage.prototype.hideMessageBodies = function ()
WebMailDataStorage.prototype.getNextFolderNames = function (bBoot)
{
bBoot = Utils.isUnd(bBoot) ? false : !!bBoot;
var
aResult = [],
iLimit = 10,
@ -756,10 +757,10 @@ WebMailDataStorage.prototype.getNextFolderNames = function (bBoot)
{
return 1;
}
return 0;
});
_.find(aTimeouts, function (aItem) {
var oFolder = RL.cache().getFolderFromCacheList(aItem[1]);
if (oFolder)
@ -767,7 +768,7 @@ WebMailDataStorage.prototype.getNextFolderNames = function (bBoot)
oFolder.interval = iUtc;
aResult.push(aItem[1]);
}
return iLimit <= aResult.length;
});
@ -785,7 +786,7 @@ WebMailDataStorage.prototype.removeMessagesFromList = function (
{
sToFolderFullNameRaw = Utils.isNormal(sToFolderFullNameRaw) ? sToFolderFullNameRaw : '';
bCopy = Utils.isUnd(bCopy) ? false : !!bCopy;
aUidForRemove = _.map(aUidForRemove, function (mValue) {
return Utils.pInt(mValue);
});
@ -845,7 +846,7 @@ WebMailDataStorage.prototype.removeMessagesFromList = function (
else
{
oData.messageListIsNotCompleted(true);
_.each(aMessages, function (oMessage) {
if (oCurrentMessage && oCurrentMessage.hash === oMessage.hash)
{
@ -916,7 +917,7 @@ WebMailDataStorage.prototype.setMessage = function (oData, bCached)
oBody = $('<div id="' + sId + '" />').hide().addClass('rl-cache-class');
oBody.data('rl-cache-count', ++Globals.iMessageBodyCacheCount);
if (Utils.isNormal(oData.Result.Html) && '' !== oData.Result.Html)
{
bIsHtml = true;
@ -926,7 +927,7 @@ WebMailDataStorage.prototype.setMessage = function (oData, bCached)
{
bIsHtml = false;
sPlain = oData.Result.Plain.toString();
if ((oMessage.isPgpSigned() || oMessage.isPgpEncrypted()) &&
RL.data().allowOpenPGP() &&
Utils.isNormal(oData.Result.PlainRaw))
@ -939,7 +940,7 @@ WebMailDataStorage.prototype.setMessage = function (oData, bCached)
bPgpSigned = /-----BEGIN PGP SIGNED MESSAGE-----/.test(oMessage.plainRaw) &&
/-----BEGIN PGP SIGNATURE-----/.test(oMessage.plainRaw);
}
$proxyDiv.empty();
if (bPgpSigned && oMessage.isPgpSigned())
{
@ -951,7 +952,7 @@ WebMailDataStorage.prototype.setMessage = function (oData, bCached)
}
else if (bPgpEncrypted && oMessage.isPgpEncrypted())
{
sPlain =
sPlain =
$proxyDiv.append(
$('<pre class="b-plain-openpgp encrypted"></pre>').text(oMessage.plainRaw)
).html()
@ -1028,7 +1029,7 @@ WebMailDataStorage.prototype.setMessage = function (oData, bCached)
{
RL.setMessageSeen(oMessage);
}
Utils.windowResize();
}
};
@ -1081,7 +1082,7 @@ WebMailDataStorage.prototype.setMessageList = function (oData, bCached)
if (oFolder && !bCached)
{
oFolder.interval = iUtc;
RL.cache().setFolderHash(oData.Result.Folder, oData.Result.FolderHash);
if (Utils.isNormal(oData.Result.MessageCount))

View file

@ -175,7 +175,7 @@
float: left;
padding: 0 8px 0 6px;
}
.shareParent {
display: none;
float: right;
@ -224,7 +224,7 @@
}
&.selected {
background-color: #fff;
z-index: 102;
@ -238,7 +238,7 @@
}
}
}
.b-view-content-toolbar {
background-color: #f5f5f5;
padding: 7px;
@ -279,6 +279,27 @@
-webkit-overflow-scrolling: touch;
}
.tags-property-container {
font-size: 18px;
width: 400px;
.inputosaurus-container {
border-width: 1px;
border-color: transparent;
.box-shadow(none);
&:hover {
.box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.075));
border-color: #ccc;
}
&.inputosaurus-focused {
.box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.075));
border-color: #999;
}
}
}
.contactValueStatic, .contactValueLargeStatic, .contactValueTextAreaStatic {
height: 20px;
line-height: 20px;
@ -288,7 +309,7 @@
color: #555;
display: none;
}
&.read-only {
.contactValueStatic, .contactValueLargeStatic, .contactValueTextAreaStatic {
display: inline-block;
@ -361,7 +382,7 @@
color: #ddd;
}
}
.contactValueTextArea {
width: 300px;
}

View file

@ -175,9 +175,9 @@ html.ssm-state-tablet {
}
html.rl-left-panel-disabled {
#rl-left {
width: 65px !important;
width: 60px !important;
.show-on-panel-disabled {
display: block;
@ -201,7 +201,7 @@ html.rl-left-panel-disabled {
}
#rl-right {
left: 65px !important;
left: 60px !important;
}
}

View file

@ -1,14 +1,21 @@
.inputosaurus-container {
width: 99%;
line-height: 20px;
padding: 2px;
border: 1px solid #cccccc;
border-radius: 3px;
.box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.075));
.transition(~"border linear .2s, box-shadow linear .2s");
&.inputosaurus-focused {
background-color: #fff;
border: @rlInputBorderSize solid darken(@inputBorder, 20%);
.box-shadow(none);
}
li {
max-width: 500px;
background-color: #eee;

View file

@ -67,9 +67,9 @@ function MailBoxMessageViewViewModel()
this.forwardCommand = createCommandHelper(Enums.ComposeType.Forward);
this.forwardAsAttachmentCommand = createCommandHelper(Enums.ComposeType.ForwardAsAttachment);
this.editAsNewCommand = createCommandHelper(Enums.ComposeType.EditAsNew);
this.messageVisibilityCommand = Utils.createCommand(this, Utils.emptyFunction, this.messageVisibility);
this.messageEditCommand = Utils.createCommand(this, function () {
this.editMessage();
}, this.messageVisibility);
@ -91,7 +91,7 @@ function MailBoxMessageViewViewModel()
[this.message().uid], false);
}
}, this.messageVisibility);
this.archiveCommand = Utils.createCommand(this, function () {
if (this.message())
{
@ -100,7 +100,7 @@ function MailBoxMessageViewViewModel()
[this.message().uid], true);
}
}, this.messageVisibility);
this.spamCommand = Utils.createCommand(this, function () {
if (this.message())
{
@ -120,6 +120,7 @@ function MailBoxMessageViewViewModel()
}, this.messageVisibility);
// viewer
this.viewHash = '';
this.viewSubject = ko.observable('');
this.viewFromShort = ko.observable('');
this.viewToShort = ko.observable('');
@ -134,7 +135,7 @@ function MailBoxMessageViewViewModel()
this.viewDownloadLink = ko.observable('');
this.viewUserPic = ko.observable(Consts.DataImages.UserDotPic);
this.viewUserPicVisible = ko.observable(false);
this.viewPgpPassword = ko.observable('');
this.viewPgpSignedVerifyStatus = ko.computed(function () {
return this.message() ? this.message().pgpSignedVerifyStatus() : Enums.SignedVerifyStatus.None;
@ -143,7 +144,7 @@ function MailBoxMessageViewViewModel()
this.viewPgpSignedVerifyUser = ko.computed(function () {
return this.message() ? this.message().pgpSignedVerifyUser() : '';
}, this);
this.message.subscribe(function (oMessage) {
this.messageActiveDom(null);
@ -152,6 +153,12 @@ function MailBoxMessageViewViewModel()
if (oMessage)
{
if (this.viewHash !== oMessage.hash)
{
this.scrollMessageToTop();
}
this.viewHash = oMessage.hash;
this.viewSubject(oMessage.subject());
this.viewFromShort(oMessage.fromToLine(true, true));
this.viewToShort(oMessage.toToLine(true, true));
@ -179,7 +186,12 @@ function MailBoxMessageViewViewModel()
}
});
}
else
{
this.viewHash = '';
this.scrollMessageToTop();
}
}, this);
this.fullScreenMode.subscribe(function (bValue) {
@ -202,14 +214,10 @@ function MailBoxMessageViewViewModel()
}
});
this.messageActiveDom.subscribe(function () {
this.scrollMessageToTop();
}, this);
this.goUpCommand = Utils.createCommand(this, function () {
RL.pub('mailbox.message-list.selector.go-up');
});
this.goDownCommand = Utils.createCommand(this, function () {
RL.pub('mailbox.message-list.selector.go-down');
});
@ -274,7 +282,7 @@ MailBoxMessageViewViewModel.prototype.scrollToTop = function ()
// $('.messageItem', this.viewModelDom).animate({'scrollTop': 0}, 300);
$('.messageItem', this.viewModelDom).scrollTop(0);
}
Utils.windowResize();
};
@ -308,7 +316,7 @@ MailBoxMessageViewViewModel.prototype.replyOrforward = function (sType)
MailBoxMessageViewViewModel.prototype.onBuild = function (oDom)
{
var
var
self = this,
oData = RL.data()
;
@ -319,7 +327,7 @@ MailBoxMessageViewViewModel.prototype.onBuild = function (oDom)
self.message.focused(true);
}
}, this);
$('.attachmentsPlace', oDom).magnificPopup({
'delegate': '.magnificPopupImage:visible',
'type': 'image',
@ -513,7 +521,7 @@ MailBoxMessageViewViewModel.prototype.initShortcuts = function ()
{
self.message().printMessage();
}
return false;
});
@ -529,7 +537,7 @@ MailBoxMessageViewViewModel.prototype.initShortcuts = function ()
{
self.deleteCommand();
}
return false;
}
});
@ -673,7 +681,7 @@ MailBoxMessageViewViewModel.prototype.readReceipt = function (oMessage)
if (oMessage && '' !== oMessage.readReceipt())
{
RL.remote().sendReadReceiptMessage(Utils.emptyFunction, oMessage.folderFullNameRaw, oMessage.uid,
oMessage.readReceipt(),
oMessage.readReceipt(),
Utils.i18n('READ_RECEIPT/SUBJECT', {'SUBJECT': oMessage.subject()}),
Utils.i18n('READ_RECEIPT/BODY', {'READ-RECEIPT': RL.data().accountEmail()}));

View file

@ -24,7 +24,8 @@ function PopupsContactsViewModel()
this.search = ko.observable('');
this.contactsCount = ko.observable(0);
this.contacts = RL.data().contacts;
this.contactTags = RL.data().contactTags;
this.currentContact = ko.observable(null);
this.importUploaderButton = ko.observable(null);
@ -44,6 +45,21 @@ function PopupsContactsViewModel()
this.viewReadOnly = ko.observable(false);
this.viewProperties = ko.observableArray([]);
this.viewTags = ko.observable('');
this.viewTags.visibility = ko.observable(false);
this.viewTags.focusTrigger = ko.observable(false);
this.viewTags.focusTrigger.subscribe(function (bValue) {
if (!bValue && '' === this.viewTags())
{
this.viewTags.visibility(false);
}
else if (bValue)
{
this.viewTags.visibility(true);
}
}, this);
this.viewSaveTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.viewPropertiesNames = this.viewProperties.filter(function(oProperty) {
@ -57,7 +73,7 @@ function PopupsContactsViewModel()
Enums.ContactPropertyType.Note
]);
});
this.viewPropertiesOther = ko.computed(function () {
var aList = _.filter(this.viewProperties(), function (oProperty) {
@ -71,7 +87,7 @@ function PopupsContactsViewModel()
});
}, this);
this.viewPropertiesEmails = this.viewProperties.filter(function(oProperty) {
return Enums.ContactPropertyType.Email === oProperty.type();
});
@ -81,7 +97,7 @@ function PopupsContactsViewModel()
});
this.viewHasNonEmptyRequaredProperties = ko.computed(function() {
var
aNames = this.viewPropertiesNames(),
aEmail = this.viewPropertiesEmails(),
@ -89,7 +105,7 @@ function PopupsContactsViewModel()
return '' !== Utils.trim(oProperty.value());
}
;
return !!(_.find(aNames, fHelper) || _.find(aEmail, fHelper));
}, this);
@ -198,7 +214,7 @@ function PopupsContactsViewModel()
this.populateViewContact(null);
this.currentContact(null);
});
this.deleteCommand = Utils.createCommand(this, function () {
this.deleteSelectedContacts();
this.emptySelection(true);
@ -213,7 +229,7 @@ function PopupsContactsViewModel()
aE = _.map(aC, function (oItem) {
if (oItem)
{
var
var
aData = oItem.getNameAndEmailHelper(),
oEmail = aData ? new EmailModel(aData[0], aData[1]) : null
;
@ -235,7 +251,7 @@ function PopupsContactsViewModel()
kn.hideScreenPopup(PopupsContactsViewModel);
kn.showScreenPopup(PopupsComposeViewModel, [Enums.ComposeType.Empty, null, aE]);
}
}, function () {
return 0 < this.contactsCheckedOrSelected().length;
});
@ -245,11 +261,11 @@ function PopupsContactsViewModel()
});
this.saveCommand = Utils.createCommand(this, function () {
this.viewSaving(true);
this.viewSaveTrigger(Enums.SaveSettingsStep.Animate);
var
var
sRequestUid = Utils.fakeMd5(),
aProperties = []
;
@ -265,7 +281,7 @@ function PopupsContactsViewModel()
var bRes = false;
self.viewSaving(false);
if (Enums.StorageResultType.Success === sResult && oData && oData.Result &&
oData.Result.RequestUid === sRequestUid && 0 < Utils.pInt(oData.Result.ResultID))
{
@ -290,11 +306,11 @@ function PopupsContactsViewModel()
self.viewSaveTrigger(Enums.SaveSettingsStep.Idle);
}, 1000);
}
}, sRequestUid, this.viewID(), aProperties);
}, sRequestUid, this.viewID(), this.viewTags(), aProperties);
}, function () {
var
var
bV = this.viewHasNonEmptyRequaredProperties(),
bReadOnly = this.viewReadOnly()
;
@ -313,7 +329,7 @@ function PopupsContactsViewModel()
self.reloadContactList(true);
});
}, function () {
return !this.contacts.syncing() && !this.contacts.importing();
});
@ -322,9 +338,9 @@ function PopupsContactsViewModel()
this.watchDirty = ko.observable(false);
this.watchHash = ko.observable(false);
this.viewHash = ko.computed(function () {
return '' + _.map(self.viewProperties(), function (oItem) {
return '' + self.viewTags() + '|' + _.map(self.viewProperties(), function (oItem) {
return oItem.value();
}).join('');
});
@ -385,6 +401,12 @@ PopupsContactsViewModel.prototype.addNewOrFocusProperty = function (sType, sType
}
};
PopupsContactsViewModel.prototype.addNewTag = function ()
{
this.viewTags.visibility(true);
this.viewTags.focusTrigger(true);
};
PopupsContactsViewModel.prototype.addNewEmail = function ()
{
this.addNewProperty(Enums.ContactPropertyType.Email, 'Home');
@ -454,7 +476,7 @@ PopupsContactsViewModel.prototype.initUploader = function ()
this.contacts.importing(true);
}, this))
.on('onComplete', _.bind(function (sId, bResult, oData) {
this.contacts.importing(false);
this.reloadContactList();
@ -478,7 +500,7 @@ PopupsContactsViewModel.prototype.removeCheckedOrSelectedContactsFromList = func
iCount = this.contacts().length,
aContacts = this.contactsCheckedOrSelected()
;
if (0 < aContacts.length)
{
_.each(aContacts, function (oContact) {
@ -499,11 +521,11 @@ PopupsContactsViewModel.prototype.removeCheckedOrSelectedContactsFromList = func
}
_.delay(function () {
_.each(aContacts, function (oContact) {
oKoContacts.remove(oContact);
});
}, 500);
}
};
@ -562,7 +584,8 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
this.emptySelection(false);
this.viewReadOnly(false);
this.viewTags('');
if (oContact)
{
sId = oContact.idContact;
@ -587,15 +610,20 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
});
}
this.viewTags(oContact.tags);
this.viewReadOnly(!!oContact.readOnly);
}
this.viewTags.focusTrigger.valueHasMutated();
this.viewTags.visibility('' !== this.viewTags());
aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.LastName, '', sLastName, false,
this.getPropertyPlceholder(Enums.ContactPropertyType.LastName)));
aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.FirstName, '', sFirstName, !oContact,
this.getPropertyPlceholder(Enums.ContactPropertyType.FirstName)));
this.viewID(sId);
this.viewProperties([]);
this.viewProperties(aList);
@ -613,7 +641,7 @@ PopupsContactsViewModel.prototype.reloadContactList = function (bDropPagePositio
self = this,
iOffset = (this.contactsPage() - 1) * Consts.Defaults.ContactsPerPage
;
this.bDropPageAfterDelete = false;
if (Utils.isUnd(bDropPagePosition) ? false : !!bDropPagePosition)
@ -626,9 +654,10 @@ PopupsContactsViewModel.prototype.reloadContactList = function (bDropPagePositio
RL.remote().contacts(function (sResult, oData) {
var
iCount = 0,
aList = []
aList = [],
aTagsList = []
;
if (Enums.StorageResultType.Success === sResult && oData && oData.Result && oData.Result.List)
{
if (Utils.isNonEmptyArray(oData.Result.List))
@ -643,14 +672,26 @@ PopupsContactsViewModel.prototype.reloadContactList = function (bDropPagePositio
iCount = Utils.pInt(oData.Result.Count);
iCount = 0 < iCount ? iCount : 0;
}
if (Utils.isNonEmptyArray(oData.Result.Tags))
{
aTagsList = _.map(oData.Result.Tags, function (oItem) {
var oContactTag = new ContactTagModel();
return oContactTag.parse(oItem) ? oContactTag : null;
});
aTagsList = _.compact(aTagsList);
}
}
self.contactsCount(iCount);
self.contacts(aList);
self.viewClearSearch('' !== self.search());
self.contactTags(aTagsList);
self.contacts.loading(false);
self.viewClearSearch('' !== self.search());
}, iOffset, Consts.Defaults.ContactsPerPage, this.search());
};

File diff suppressed because it is too large Load diff

View file

@ -23,7 +23,7 @@ abstract class PdoAbstract
* @var string
*/
protected $sDbType;
/**
* @return bool
*/
@ -67,7 +67,7 @@ abstract class PdoAbstract
{
return $this->oPDO;
}
if (!\class_exists('PDO'))
{
throw new \Exception('Class PDO does not exist');
@ -100,7 +100,7 @@ abstract class PdoAbstract
{
throw $oException;
}
if ($oPdo)
{
$this->oPDO = $oPdo;
@ -127,7 +127,7 @@ abstract class PdoAbstract
{
$mName = \strtolower($sTabelName.'_'.$sColumnName.'_seq');
}
return null === $mName ? $this->getPDO()->lastInsertId() : $this->getPDO()->lastInsertId($mName);
}
@ -222,15 +222,15 @@ abstract class PdoAbstract
$mResult->closeCursor();
}
}
/**
* @param string $sSql
* @param mixed $mData
*/
protected function writeLog($sSql)
protected function writeLog($mData)
{
if ($this->oLogger)
{
$this->oLogger->WriteMixed($sSql, \MailSo\Log\Enumerations\Type::INFO, 'SQL');
$this->oLogger->WriteMixed($mData, \MailSo\Log\Enumerations\Type::INFO, 'SQL');
}
}
@ -324,7 +324,7 @@ abstract class PdoAbstract
}
$this->writeLog($sQuery);
$oStmt = $oPdo->prepare($sQuery);
if ($oStmt->execute(array($sName)))
{
@ -355,7 +355,7 @@ abstract class PdoAbstract
/**
* @param string $sName
* @param int $iVersion
*
*
* @return bool
*/
protected function setVersion($sName, $iVersion)
@ -371,7 +371,7 @@ abstract class PdoAbstract
$sQuery = 'DELETE FROM rainloop_system WHERE sys_name = ? AND value_int <= ?;';
$this->writeLog($sQuery);
$oStmt = $oPdo->prepare($sQuery);
$bResult = !!$oStmt->execute(array($sName.'_version', $iVersion));
if ($bResult)
@ -507,7 +507,7 @@ rl_email text NOT NULL DEFAULT \'\'
{
$oPdo->rollBack();
}
throw $oException;
}
}
@ -550,7 +550,7 @@ rl_email text NOT NULL DEFAULT \'\'
{
$oPdo = false;
$bResult = false;
foreach ($aData as $iVersion => $aQuery)
{
if (0 === \count($aQuery))
@ -580,7 +580,7 @@ rl_email text NOT NULL DEFAULT \'\'
if (false === $bExec)
{
$this->writeLog('Result: false');
$bResult = false;
break;
}
@ -605,7 +605,7 @@ rl_email text NOT NULL DEFAULT \'\'
{
$oPdo->rollBack();
}
throw $oException;
}

View file

@ -85,7 +85,7 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
{
return $this->IsActive() ? $this->oDriver->Export($sEmail, $sType) : false;
}
/**
* @param string $sEmail
* @param \RainLoop\Providers\AddressBook\Classes\Contact $oContact
@ -123,6 +123,16 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
$iOffset, $iLimit, $sSearch, $iResultCount) : array();
}
/**
* @param string $sEmail
*
* @return array
*/
public function GetContactTags($sEmail)
{
return $this->IsActive() ? $this->oDriver->GetContactTags($sEmail) : array();
}
/**
* @param string $sEmail
* @param string $mID
@ -238,7 +248,7 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
$aMap = \array_change_key_case($aMap, CASE_LOWER);
}
$sCsvNameLower = \MailSo\Base\Utils::IsAscii($sCsvName) ? \preg_replace('/[\s\-]+/', '', \strtolower($sCsvName)) : '';
return !empty($sCsvNameLower) && isset($aMap[$sCsvNameLower]) ? $aMap[$sCsvNameLower] : PropertyType::UNKNOWN;
}
@ -258,7 +268,7 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
foreach ($aCsvData as $aItem)
{
\MailSo\Base\Utils::ResetTimeLimit();
foreach ($aItem as $sItemName => $sItemValue)
{
$sItemName = \trim($sItemName);
@ -294,10 +304,10 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
unset($oContact);
}
return $iCount;
}
/**
* @param string $sEmail
* @param string $sVcfData
@ -336,14 +346,14 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
if ($oVCard instanceof \Sabre\VObject\Component\VCard)
{
\MailSo\Base\Utils::ResetTimeLimit();
if (empty($oVCard->UID))
{
$oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID();
}
$oContact->PopulateByVCard($oVCard->serialize());
if (0 < \count($oContact->Properties))
{
if ($this->ContactSave($sEmail, $oContact))

View file

@ -37,6 +37,11 @@ class Contact
/**
* @var array
*/
public $Tags;
/**
* @var bool
*/
public $ReadOnly;
/**
@ -58,15 +63,15 @@ class Contact
{
$this->IdContact = '';
$this->IdContactStr = '';
$this->IdUser = 0;
$this->Display = '';
$this->Changed = \time();
$this->Properties = array();
$this->Tags = array();
$this->ReadOnly = false;
$this->IdPropertyFromSearch = 0;
$this->Etag = '';
}
public function UpdateDependentValues()
{
$sLastName = '';
@ -75,7 +80,7 @@ class Contact
$sOther = '';
$oFullNameProperty = null;
foreach ($this->Properties as /* @var $oProperty \RainLoop\Providers\AddressBook\Classes\Property */ &$oProperty)
{
if ($oProperty)
@ -133,7 +138,7 @@ class Contact
}
$this->Display = \trim($sDisplay);
if ($oFullNameProperty)
{
$oFullNameProperty->Value = $this->Display;
@ -200,7 +205,7 @@ class Contact
{
$oVCard = new \Sabre\VObject\Component\VCard();
}
$oVCard->VERSION = '3.0';
$oVCard->PRODID = '-//RainLoop//'.APP_VERSION.'//EN';
@ -482,7 +487,7 @@ class Contact
public function PopulateByVCard($sVCard, $sEtag = '')
{
$this->Properties = array();
if (!empty($sEtag))
{
$this->Etag = $sEtag;

View file

@ -0,0 +1,33 @@
<?php
namespace RainLoop\Providers\AddressBook\Classes;
class Tag
{
/**
* @var string
*/
public $IdContactTag;
/**
* @var string
*/
public $Name;
/**
* @var bool
*/
public $ReadOnly;
public function __construct()
{
$this->Clear();
}
public function Clear()
{
$this->IdContactTag = '';
$this->Name = '';
$this->ReadOnly = false;
}
}

View file

@ -17,7 +17,7 @@ class PdoAddressBook
* @var string
*/
private $sDsnType;
/**
* @var string
*/
@ -73,7 +73,7 @@ class PdoAddressBook
)
);
}
private function prepearDatabaseSyncData($iUserID)
{
$aResult = array();
@ -122,7 +122,7 @@ class PdoAddressBook
{
$this->oLogger->WriteException($oException);
}
if (\is_array($aResponse))
{
$mResult = array();
@ -159,7 +159,7 @@ class PdoAddressBook
private function davClientRequest($oClient, $sCmd, $sUrl, $mData = null)
{
\MailSo\Base\Utils::ResetTimeLimit();
$this->oLogger->Write($sCmd.' '.$sUrl.('PUT' === $sCmd && null !== $mData ? ' ('.\strlen($mData).')' : ''),
\MailSo\Log\Enumerations\Type::INFO, 'DAV');
@ -183,7 +183,7 @@ class PdoAddressBook
{
$this->oLogger->WriteException($oException);
}
return $oResponse;
}
@ -199,13 +199,13 @@ class PdoAddressBook
public function Sync($sEmail, $sUrl, $sUser, $sPassword, $sProxy = '')
{
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
if (0 >= $iUserID)
{
return false;
}
$aUrl = \parse_url($sUrl);
if (!\is_array($aUrl))
{
@ -233,6 +233,9 @@ class PdoAddressBook
$oClient = new \Sabre\DAV\Client($aSettings);
$oClient->setVerifyPeer(false);
$this->oLogger->AddSecret($sPassword);
$this->oLogger->Write('User: '.$aSettings['userName'].', Url: '.$sUrl, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
$aRemoteSyncData = $this->prepearRemoteSyncData($oClient, $sPath);
if (false === $aRemoteSyncData)
{
@ -276,7 +279,7 @@ class PdoAddressBook
//+++new or newer (from db)
foreach ($aDatabaseSyncData as $sKey => $aData)
{
if (!$aData['deleted'] &&
if (!$aData['deleted'] &&
(empty($aData['etag']) && !isset($aRemoteSyncData[$sKey])) // new
||
(!empty($aData['etag']) && isset($aRemoteSyncData[$sKey]) && // newer
@ -302,7 +305,7 @@ class PdoAddressBook
$oResponse = $this->davClientRequest($oClient, 'PUT',
$sPath.$oContact->CardDavNameUri(), $oContact->ToVCard($sExsistensBody));
if ($oResponse && isset($oResponse['headers'], $oResponse['headers']['etag']))
{
$sEtag = \trim(\trim($oResponse['headers']['etag']), '"\'');
@ -314,7 +317,7 @@ class PdoAddressBook
}
}
}
unset($oContact);
}
}
@ -348,7 +351,7 @@ class PdoAddressBook
{
$oContact = new \RainLoop\Providers\AddressBook\Classes\Contact();
}
$oContact->PopulateByVCard($sBody,
!empty($oResponse['headers']['etag']) ? \trim(\trim($oResponse['headers']['etag']), '"\'') : '');
@ -421,16 +424,16 @@ class PdoAddressBook
{
$this->SyncDatabase();
}
$iUserID = $this->getUserId($sEmail);
$iIdContact = 0 < \strlen($oContact->IdContact) && \is_numeric($oContact->IdContact) ? (int) $oContact->IdContact : 0;
$bUpdate = 0 < $iIdContact;
$oContact->UpdateDependentValues();
$oContact->Changed = \time();
try
{
if ($this->isTransactionSupported())
@ -465,6 +468,14 @@ class PdoAddressBook
':id_contact' => array($iIdContact, \PDO::PARAM_INT)
)
);
// clear previos tags
$this->prepareAndExecute(
'DELETE FROM rainloop_ab_tags_contacts WHERE id_contact = :id_contact',
array(
':id_contact' => array($iIdContact, \PDO::PARAM_INT)
)
);
}
else
{
@ -517,6 +528,44 @@ class PdoAddressBook
'(:id_contact, :id_user, :prop_type, :prop_type_str, :prop_value, :prop_value_custom, :prop_frec)';
$this->prepareAndExecute($sSql, $aParams, true);
$aTags = array();
$aContactTags = $this->GetContactTags($sEmail);
$fFindFunc = function ($sName) use ($aContactTags) {
$iResult = 0;
foreach ($aContactTags as $oItem)
{
if ($oItem && $oItem->Name === $sName)
{
$iResult = (int) $oItem->IdContactTag;
break;
}
}
return $iResult;
};
foreach ($oContact->Tags as $sTagName)
{
$iTagID = $fFindFunc($sTagName);
if (0 >= $iTagID)
{
$iTagID = $this->CreateTag($sEmail, $sTagName, false);
}
if (\is_int($iTagID) && 0 < $iTagID)
{
$aTags[] = array(
':id_tag' => array($iTagID, \PDO::PARAM_INT),
':id_contact' => array($iIdContact, \PDO::PARAM_INT)
);
}
}
$sSql = 'INSERT INTO rainloop_ab_tags_contacts (id_tag, id_contact) VALUES (:id_tag, :id_contact)';
$this->prepareAndExecute($sSql, $aTags, true);
}
}
catch (\Exception $oException)
@ -537,6 +586,64 @@ class PdoAddressBook
return 0 < $iIdContact;
}
/**
* @param string $sEmail
* @param string $sName
* @param bool $bSyncDb = true
*
* @return bool|int
*/
public function CreateTag($sEmail, $sName, $bSyncDb = true)
{
if ($bSyncDb)
{
$this->SyncDatabase();
}
$iUserID = $this->getUserId($sEmail);
$mResult = false;
try
{
if ($this->isTransactionSupported())
{
$this->beginTransaction();
}
$sSql = 'INSERT INTO rainloop_ab_tags '.
'(id_user, tag_name) VALUES (:id_user, :tag_name)';
$this->prepareAndExecute($sSql,
array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':tag_name' => array($sName, \PDO::PARAM_STR)
)
);
$sLast = $this->lastInsertId('rainloop_ab_contacts', 'id_contact');
if (\is_numeric($sLast) && 0 < (int) $sLast)
{
$mResult = (int) $sLast;
}
}
catch (\Exception $oException)
{
if ($this->isTransactionSupported())
{
$this->rollBack();
}
throw $oException;
}
if ($this->isTransactionSupported())
{
$this->commit();
}
return $mResult;
}
/**
* @param string $sEmail
* @param array $aContactIds
@ -615,7 +722,7 @@ class PdoAddressBook
* @param int $iLimit = 20
* @param string $sSearch = ''
* @param int $iResultCount = 0
*
*
* @return array
*/
public function GetContacts($sEmail, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0)
@ -631,7 +738,7 @@ class PdoAddressBook
$iCount = 0;
$aSearchIds = array();
$aPropertyFromSearchIds = array();
if (0 < \strlen($sSearch))
{
$sCustomSearch = $this->specialConvertSearchValueCustomPhone($sSearch);
@ -645,7 +752,7 @@ class PdoAddressBook
PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME, PropertyType::NICK_NAME,
PropertyType::PHONE, PropertyType::WEB_PAGE
));
$sSql = 'SELECT id_user, id_prop, id_contact FROM rainloop_ab_properties '.
'WHERE (id_user = :id_user) AND prop_type IN ('.$sSearchTypes.') AND (prop_value LIKE :search ESCAPE \'=\''.
(0 < \strlen($sCustomSearch) ? ' OR (prop_type = '.PropertyType::PHONE.' AND prop_value_custom <> \'\' AND prop_value_custom LIKE :search_custom_phone)' : '').
@ -686,7 +793,7 @@ class PdoAddressBook
{
$sSql = 'SELECT COUNT(DISTINCT id_contact) as contact_count FROM rainloop_ab_properties '.
'WHERE id_user = :id_user';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
);
@ -704,10 +811,11 @@ class PdoAddressBook
$iResultCount = $iCount;
$aResult = array();
if (0 < $iCount)
{
$sSql = 'SELECT * FROM rainloop_ab_contacts WHERE deleted = 0 AND id_user = :id_user';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
);
@ -794,13 +902,136 @@ class PdoAddressBook
$oItem->UpdateDependentValues();
}
return \array_values($aContacts);
$aResult = \array_values($aContacts);
}
}
}
}
return array();
if (\is_array($aResult) && 0 < \count($aResult))
{
$aIdContacts = array();
$aIdTagsContacts = array();
$aTags = $this->GetContactTags($sEmail);
if (\is_array($aTags) && 0 < \count($aTags))
{
foreach ($aResult as $oItem)
{
$aIdContacts[$oItem->IdContact] = true;
}
if (0 < \count($aIdContacts))
{
$sSql = 'SELECT id_tag, id_contact FROM rainloop_ab_tags_contacts WHERE id_contact IN ('.\implode(',', \array_keys($aIdContacts)).')';
$oStmt = $this->prepareAndExecute($sSql);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
foreach ($aFetch as $aItem)
{
if ($aItem && isset($aItem['id_tag'], $aItem['id_contact']))
{
$sID = (string) $aItem['id_contact'];
if (!isset($aIdTagsContacts[$sID]))
{
$aIdTagsContacts[$sID] = array();
}
$aIdTagsContacts[$sID][] = (string) $aItem['id_tag'];
}
}
}
unset($aFetch);
}
if (0 < \count($aIdTagsContacts))
{
$fFindFunc = function ($sID) use ($aTags) {
foreach ($aTags as $oItem)
{
if ($oItem && (string) $oItem->IdContactTag === $sID)
{
return\trim($oItem->Name);
}
}
return '';
};
foreach ($aResult as &$oItem)
{
$sID = $oItem ? (string) $oItem->IdContact : '';
if (0 < \strlen($sID) && isset($aIdTagsContacts[$sID]) && \is_array($aIdTagsContacts[$sID]))
{
$aNames = array();
foreach ($aIdTagsContacts[$sID] as $sSubID)
{
$sName = $fFindFunc($sSubID);
if (0 < \strlen($sName))
{
$aNames[] = $sName;
}
}
$oItem->Tags = $aNames;
}
}
}
}
}
}
return $aResult;
}
/**
* @param string $sEmail
*
* @return array
*/
public function GetContactTags($sEmail)
{
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
$sSql = 'SELECT id_tag, id_user, tag_name FROM rainloop_ab_tags WHERE id_user = :id_user ORDER BY tag_name ASC';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
);
$aResult = array();
$oStmt = $this->prepareAndExecute($sSql, $aParams);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
foreach ($aFetch as $aItem)
{
$iIdContactTag = $aItem && isset($aItem['id_tag']) ? (int) $aItem['id_tag'] : 0;
if (0 < $iIdContactTag)
{
$oContactTag = new \RainLoop\Providers\AddressBook\Classes\Tag();
$oContactTag->IdContactTag = (string) $iIdContactTag;
$oContactTag->Name = isset($aItem['tag_name']) ? (string) $aItem['tag_name'] : '';
$oContactTag->ReadOnly = $iUserID !== (isset($aItem['id_user']) ? (int) $aItem['id_user'] : 0);
$aResult[] = $oContactTag;
}
}
}
}
return $aResult;
}
/**
@ -924,7 +1155,7 @@ class PdoAddressBook
}
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
$sTypes = implode(',', array(
@ -933,7 +1164,7 @@ class PdoAddressBook
$sSql = 'SELECT id_contact, id_prop, prop_type, prop_value FROM rainloop_ab_properties '.
'WHERE (id_user = :id_user) AND prop_type IN ('.$sTypes.') AND prop_value LIKE :search ESCAPE \'=\'';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':limit' => array($iLimit, \PDO::PARAM_INT),
@ -944,7 +1175,7 @@ class PdoAddressBook
$sSql .= ' LIMIT :limit';
$aResult = array();
$oStmt = $this->prepareAndExecute($sSql, $aParams);
if ($oStmt)
{
@ -960,7 +1191,7 @@ class PdoAddressBook
$iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0;
$iIdProp = $aItem && isset($aItem['id_prop']) ? (int) $aItem['id_prop'] : 0;
$iType = $aItem && isset($aItem['prop_type']) ? (int) $aItem['prop_type'] : 0;
if (0 < $iIdContact && 0 < $iIdProp)
{
$aIdContacts[$iIdContact] = $iIdContact;
@ -985,7 +1216,7 @@ class PdoAddressBook
if (0 < count($aIdContacts))
{
$oStmt->closeCursor();
$sTypes = \implode(',', array(
PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME, PropertyType::NICK_NAME
));
@ -1042,10 +1273,10 @@ class PdoAddressBook
{
$bName = \in_array(PropertyType::FIRST_NAME, $aContactAllAccess[$iId]) || \in_array(PropertyType::LAST_NAME, $aContactAllAccess[$iId]);
$bNick = \in_array(PropertyType::NICK_NAME, $aContactAllAccess[$iId]);
$aNameItem = isset($aNames[$iId]) && \is_array($aNames[$iId]) ? $aNames[$iId] : array('', '');
$sNameItem = \trim($aNameItem[0].' '.$aNameItem[1]);
$sNickItem = isset($aNicks[$iId]) ? $aNicks[$iId] : '';
foreach ($aItems as $sEmail)
@ -1054,7 +1285,7 @@ class PdoAddressBook
{
$aResult[] = array($sEmail, $sNameItem);
}
if ($bNick)
{
$aResult[] = array($sEmail, $sNickItem);
@ -1089,7 +1320,7 @@ class PdoAddressBook
{
$aResult = \array_slice($aResult, 0, $iLimit);
}
return $aResult;
}
}
@ -1121,7 +1352,7 @@ class PdoAddressBook
$aEmailsObjects = \array_filter($aEmailsObjects, function ($oItem) {
return !!$oItem;
});
if (0 === \count($aEmailsObjects))
{
throw new \InvalidArgumentException('Empty Emails argument');
@ -1156,7 +1387,7 @@ class PdoAddressBook
}
}
}
$aEmailsToCreate = \array_filter($aEmailsObjects, function ($oItem) use ($aExists, &$aEmailsToUpdate) {
if ($oItem)
{
@ -1185,7 +1416,7 @@ class PdoAddressBook
}
}
}
unset($aEmails, $aEmailsObjects);
if (0 < \count($aEmailsToCreate))
@ -1225,7 +1456,7 @@ class PdoAddressBook
$oContact->Properties[] = $oPropName;
}
if (0 < \strlen($sLast))
{
$oPropName = new \RainLoop\Providers\AddressBook\Classes\Property();
@ -1240,17 +1471,17 @@ class PdoAddressBook
{
$this->ContactSave($sEmail, $oContact);
}
$oContact->Clear();
}
}
$sSql = 'UPDATE rainloop_ab_properties SET prop_frec = prop_frec + 1 WHERE id_user = :id_user AND prop_type = :prop_type';
$aEmailsQuoted = \array_map(function ($mItem) use ($self) {
return $self->quoteValue($mItem);
}, $aEmailsToUpdate);
if (1 === \count($aEmailsQuoted))
{
$sSql .= ' AND prop_value = '.$aEmailsQuoted[0];
@ -1259,7 +1490,7 @@ class PdoAddressBook
{
$sSql .= ' AND prop_value IN ('.\implode(',', $aEmailsQuoted).')';
}
return !!$this->prepareAndExecute($sSql, array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':prop_type' => array(PropertyType::EMAIl, \PDO::PARAM_INT)
@ -1506,7 +1737,7 @@ SQLITEINITIAL;
break;
}
return false;
return $mCache;
}
/**

View file

@ -315,7 +315,7 @@ class Client {
// Return headers as part of the response
CURLOPT_HEADER => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_USERAGENT => 'RainLoop DAV Client',
CURLOPT_USERAGENT => 'RainLoop DAV Client', // TODO rainloop
// Automatically follow redirects
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
@ -323,9 +323,13 @@ class Client {
if($this->verifyPeer !== null) {
$curlSettings[CURLOPT_SSL_VERIFYPEER] = $this->verifyPeer;
// TODO rainloop
if (!$this->verifyPeer) {
$curlSettings[CURLOPT_SSL_VERIFYHOST] = 0;
} // ---
}
if($this->trustedCertificates) {
if($this->trustedCertificates) {
$curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates;
}
@ -459,6 +463,7 @@ class Client {
// @codeCoverageIgnoreStart
protected function curlRequest($url, $settings) {
// TODO rainloop
$curl = curl_init($url);
$sSafeMode = strtolower(trim(@ini_get('safe_mode')));
$bSafeMode = 'on' === $sSafeMode || '1' === $sSafeMode;

View file

@ -1,11 +1,11 @@
<div class="popups">
<div class="modal hide b-contacts-content" data-bind="modal: modalVisibility">
<div class="modal-header b-header-toolbar g-ui-user-select-none">
<button type="button" class="close" data-bind="command: cancelCommand">&times;</button>
<div class="btn-toolbar">
<div class="btn-group">
<a class="btn button-create-contact" data-bind="command: newCommand">
<i class="icon-plus"></i>
@ -126,25 +126,41 @@
<ul class="dropdown-menu g-ui-menu" style="text-align: left" tabindex="-1" role="menu" aria-labelledby="button-add-prop-dropdown-id">
<li class="e-item" role="presentation">
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: addNewEmail">
<i class="icon-none"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="CONTACTS/ADD_MENU_EMAIL"></span>
</a>
</li>
<li class="e-item" role="presentation">
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: addNewPhone">
<i class="icon-none"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="CONTACTS/ADD_MENU_PHONE"></span>
</a>
</li>
<li class="e-item" role="presentation">
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: addNewWeb">
<i class="icon-none"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="CONTACTS/ADD_MENU_URL"></span>
</a>
</li>
<li class="divider" role="presentation"></li>
<li class="e-item" role="presentation">
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: addNewNickname">
<i class="icon-none"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="CONTACTS/ADD_MENU_NICKNAME"></span>
</a>
</li>
<li class="divider" role="presentation"></li>
<li class="e-item" role="presentation">
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: addNewTag">
<i class="icon-tags"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="CONTACTS/ADD_MENU_TAGS"></span>
</a>
</li>
<!-- <li class="e-item" role="presentation">
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: addNewAddress">
<span class="i18n" data-i18n-text="CONTACTS/ADD_MENU_ADDRESS"></span>
@ -247,6 +263,18 @@
</div>
</div>
</div>
<div class="control-group" data-bind="visible: !viewReadOnly() && viewTags.visibility()">
<label class="control-label remove-padding-top fix-width">
<i class="icon-tags iconsize24" data-tooltip-placement="left" data-bind="tooltip: 'CONTACTS/LABEL_TAGS'"></i>
</label>
<div class="controls fix-width">
<div class="property-line">
<div class="tags-property-container">
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" data-bind="contactTags: viewTags" />
</div>
</div>
</div>
</div>
<div class="control-group">
<div class="controls fix-width">
<br />
@ -254,12 +282,12 @@
</div>
</div>
</div>
<!-- <div class="e-read-only-sign">
<i class="icon-lock iconsize24" data-tooltip-placement="left" data-bind="tooltip: 'CONTACTS/LABEL_READ_ONLY'"></i>
</div>-->
</div>
</div>
</div>

View file

@ -39,8 +39,8 @@
<br />
<select class="span2" data-bind="value: imapSecure">
<option value="0">None</option>
<option value="1">SSL</option>
<option value="2">TLS</option>
<option value="1">SSL/TLS</option>
<option value="2">STARTTLS</option>
</select>
<br />
<br />
@ -72,8 +72,8 @@
<br />
<select class="span2" data-bind="value: smtpSecure">
<option value="0">None</option>
<option value="1">SSL</option>
<option value="2">TLS</option>
<option value="1">SSL/TLS</option>
<option value="2">STARTTLS</option>
</select>
<br />
<br />

View file

@ -167,6 +167,7 @@ LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LABEL_TAGS = "Tags"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
LINK_BIRTHDAY = "Birthday"
@ -184,6 +185,7 @@ ADD_MENU_PHONE = "Phone"
ADD_MENU_URL = "URL"
ADD_MENU_ADDRESS = "Address"
ADD_MENU_BIRTHDAY = "Birthday"
ADD_MENU_TAGS = "Tags"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"

View file

@ -637,7 +637,7 @@
border-radius: 8px;
}
/*! normalize.css 2012-03-11T12:53 UTC - http://github.com/necolas/normalize.css */
/* =============================================================================
@ -1142,7 +1142,7 @@ table {
border-collapse: collapse;
border-spacing: 0;
}
@charset "UTF-8";
@font-face {
@ -1510,7 +1510,7 @@ table {
.icon-help:before {
content: "\e06c";
}
/** initial setup **/
.nano {
/*
@ -1627,7 +1627,7 @@ table {
.nano > .pane2:hover > .slider2, .nano > .pane2.active > .slider2 {
background-color: rgba(0, 0, 0, 0.4);
}
/* Magnific Popup CSS */
.mfp-bg {
top: 0;
@ -1992,7 +1992,7 @@ img.mfp-img {
right: 0;
padding-top: 0; }
/* overlay at start */
.mfp-fade.mfp-bg {
@ -2038,7 +2038,7 @@ img.mfp-img {
-moz-transform: translateX(50px);
transform: translateX(50px);
}
.simple-pace {
-webkit-pointer-events: none;
pointer-events: none;
@ -2109,7 +2109,7 @@ img.mfp-img {
@keyframes simple-pace-stripe-animation {
0% { transform: none; transform: none; }
100% { transform: translate(-32px, 0); transform: translate(-32px, 0); }
}
}
.inputosaurus-container {
background-color:#fff;
border:1px solid #bcbec0;
@ -2177,7 +2177,7 @@ img.mfp-img {
box-shadow:none;
}
.inputosaurus-input-hidden { display:none; }
.flag-wrapper {
width: 24px;
height: 16px;
@ -2221,7 +2221,7 @@ img.mfp-img {
.flag.flag-pt-br {background-position: -192px -11px}
.flag.flag-cn, .flag.flag-zh-tw, .flag.flag-zh-cn, .flag.flag-zh-hk {background-position: -208px -22px}
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
.clearfix {
*zoom: 1;
@ -6522,6 +6522,13 @@ html.no-rgba .modal {
-o-transition: border linear .2s, box-shadow linear .2s;
transition: border linear .2s, box-shadow linear .2s;
}
.inputosaurus-container.inputosaurus-focused {
background-color: #fff;
border: 1px solid #999999;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
.inputosaurus-container li {
max-width: 500px;
background-color: #eee;
@ -7101,7 +7108,7 @@ html.ssm-state-tablet .b-contacts-content.modal .contactValueInput {
display: none;
}
html.rl-left-panel-disabled #rl-left {
width: 65px !important;
width: 60px !important;
}
html.rl-left-panel-disabled #rl-left .show-on-panel-disabled {
display: block;
@ -7121,7 +7128,7 @@ html.rl-left-panel-disabled #rl-left.ui-state-disabled {
filter: alpha(opacity=100);
}
html.rl-left-panel-disabled #rl-right {
left: 65px !important;
left: 60px !important;
}
.ui-resizable-helper {
border-right: 5px solid #777;
@ -8637,6 +8644,29 @@ html.rl-message-fullscreen .messageView .b-content .buttonFull {
.b-contacts-content.modal .b-view-content .content {
-webkit-overflow-scrolling: touch;
}
.b-contacts-content.modal .b-view-content .tags-property-container {
font-size: 18px;
width: 400px;
}
.b-contacts-content.modal .b-view-content .tags-property-container .inputosaurus-container {
border-width: 1px;
border-color: transparent;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
.b-contacts-content.modal .b-view-content .tags-property-container .inputosaurus-container:hover {
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
border-color: #ccc;
}
.b-contacts-content.modal .b-view-content .tags-property-container .inputosaurus-container.inputosaurus-focused {
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
border-color: #999;
}
.b-contacts-content.modal .b-view-content .contactValueStatic,
.b-contacts-content.modal .b-view-content .contactValueLargeStatic,
.b-contacts-content.modal .b-view-content .contactValueTextAreaStatic {

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
/*! RainLoop Webmail Admin Module (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
(function (window, $, ko, crossroads, hasher, _) {
/*! RainLoop Webmail Admin Module (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
(function (window, $, ko, crossroads, hasher, _) {
'use strict';
@ -77,14 +77,14 @@ var
$document = $(window.document),
NotificationClass = window.Notification && window.Notification.requestPermission ? window.Notification : null
;
;
/*jshint onevar: false*/
/**
* @type {?AdminApp}
*/
var RL = null;
/*jshint onevar: true*/
/**
* @type {?}
*/
@ -233,7 +233,7 @@ if (Globals.bAllowPdfPreview && navigator && navigator.mimeTypes)
return oType && 'application/pdf' === oType.type;
});
}
Consts.Defaults = {};
Consts.Values = {};
Consts.DataImages = {};
@ -351,7 +351,7 @@ Consts.DataImages.UserDotPic = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA
* @type {string}
*/
Consts.DataImages.TranspPic = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=';
/**
* @enum {string}
*/
@ -726,7 +726,7 @@ Enums.Notification = {
'UnknownNotification': 999,
'UnknownError': 999
};
Utils.trim = $.trim;
Utils.inArray = $.inArray;
Utils.isArray = _.isArray;
@ -1052,13 +1052,13 @@ Utils.i18nToNode = function (oElement)
{
jqThis.html(Utils.i18n(sKey));
}
sKey = jqThis.data('i18n-placeholder');
if (sKey)
{
jqThis.attr('placeholder', Utils.i18n(sKey));
}
sKey = jqThis.data('i18n-title');
if (sKey)
{
@ -1196,7 +1196,7 @@ Utils.fixLongSubject = function (sSubject)
;
sSubject = Utils.trim(sSubject.replace(/[\s]+/, ' '));
do
{
oMatch = /^Re(\[([\d]+)\]|):[\s]{0,3}Re(\[([\d]+)\]|):/ig.exec(sSubject);
@ -1325,9 +1325,9 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.CantSaveSettings] = Utils.i18n('NOTIFICATIONS/CANT_SAVE_SETTINGS');
NotificationI18N[Enums.Notification.CantSavePluginSettings] = Utils.i18n('NOTIFICATIONS/CANT_SAVE_PLUGIN_SETTINGS');
NotificationI18N[Enums.Notification.DomainAlreadyExists] = Utils.i18n('NOTIFICATIONS/DOMAIN_ALREADY_EXISTS');
NotificationI18N[Enums.Notification.CantInstallPackage] = Utils.i18n('NOTIFICATIONS/CANT_INSTALL_PACKAGE');
NotificationI18N[Enums.Notification.CantDeletePackage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_PACKAGE');
NotificationI18N[Enums.Notification.InvalidPluginPackage] = Utils.i18n('NOTIFICATIONS/INVALID_PLUGIN_PACKAGE');
@ -1340,7 +1340,7 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.DemoSendMessageError] = Utils.i18n('NOTIFICATIONS/DEMO_SEND_MESSAGE_ERROR');
NotificationI18N[Enums.Notification.AccountAlreadyExists] = Utils.i18n('NOTIFICATIONS/ACCOUNT_ALREADY_EXISTS');
NotificationI18N[Enums.Notification.MailServerError] = Utils.i18n('NOTIFICATIONS/MAIL_SERVER_ERROR');
NotificationI18N[Enums.Notification.UnknownNotification] = Utils.i18n('NOTIFICATIONS/UNKNOWN_ERROR');
NotificationI18N[Enums.Notification.UnknownError] = Utils.i18n('NOTIFICATIONS/UNKNOWN_ERROR');
@ -1492,7 +1492,7 @@ Utils.initDataConstructorBySettings = function (oData)
oData.contactsAutosave = ko.observable(false);
Globals.sAnimationType = Enums.InterfaceAnimation.Full;
oData.allowThemes = ko.observable(true);
oData.allowCustomLogin = ko.observable(false);
oData.allowLanguagesOnSettings = ko.observable(true);
@ -1502,7 +1502,7 @@ Utils.initDataConstructorBySettings = function (oData)
oData.useThreads = ko.observable(true);
oData.replySameFolder = ko.observable(true);
oData.useCheckboxesInList = ko.observable(true);
oData.layout = ko.observable(Enums.Layout.SidePreview);
oData.usePreviewPane = ko.computed(function () {
return Enums.Layout.NoPreview !== oData.layout();
@ -1785,7 +1785,7 @@ Utils.setExpandedFolder = function (sFullNameHash, bExpanded)
{
aExpandedList = [];
}
if (bExpanded)
{
aExpandedList.push(sFullNameHash);
@ -1802,7 +1802,7 @@ Utils.setExpandedFolder = function (sFullNameHash, bExpanded)
Utils.initLayoutResizer = function (sLeft, sRight, sClientSideKeyName)
{
var
iDisabledWidth = 65,
iDisabledWidth = 60,
iMinWidth = 155,
oLeft = $(sLeft),
oRight = $(sRight),
@ -1880,7 +1880,7 @@ Utils.initBlockquoteSwitcher = function (oMessageTextBody)
var $oList = $('blockquote:not(.rl-bq-switcher)', oMessageTextBody).filter(function () {
return 0 === $(this).parent().closest('blockquote', oMessageTextBody).length;
});
if ($oList && 0 < $oList.length)
{
$oList.each(function () {
@ -2290,7 +2290,7 @@ Utils.resizeAndCrop = function (sUrl, iValue, fCallback)
oCtx.fillStyle = '#fff';
oCtx.fillRect(0, 0, iValue, iValue);
oCtx.drawImage(this, aDiff[0] / 2, aDiff[1] / 2, this.width - aDiff[0], this.height - aDiff[1], 0, 0, iValue, iValue);
fCallback(oCanvas.toDataURL('image/jpeg'));
};
@ -2469,7 +2469,7 @@ Utils.detectDropdownVisibility = _.debounce(function () {
Globals.dropdownVisibility(!!_.find(BootstrapDropdowns, function (oItem) {
return oItem.hasClass('open');
}));
}, 50);
}, 50);
// Base64 encode / decode
// http://www.webtoolkit.info/
@ -2632,7 +2632,7 @@ Base64 = {
}
};
/*jslint bitwise: false*/
/*jslint bitwise: false*/
ko.bindingHandlers.tooltip = {
'init': function (oElement, fValueAccessor) {
if (!Globals.bMobileDevice)
@ -2707,7 +2707,7 @@ ko.bindingHandlers.tooltip3 = {
'init': function (oElement) {
var $oEl = $(oElement);
$oEl.tooltip({
'container': 'body',
'trigger': 'hover manual',
@ -2726,7 +2726,7 @@ ko.bindingHandlers.tooltip3 = {
$document.click(function () {
$oEl.tooltip('hide');
});
},
'update': function (oElement, fValueAccessor) {
var sValue = ko.utils.unwrapObservable(fValueAccessor());
@ -2757,7 +2757,7 @@ ko.bindingHandlers.openDropdownTrigger = {
$el.find('.dropdown-toggle').dropdown('toggle');
Utils.detectDropdownVisibility();
}
fValueAccessor()(false);
}
}
@ -2854,7 +2854,7 @@ ko.bindingHandlers.clickOnTrue = {
ko.bindingHandlers.modal = {
'init': function (oElement, fValueAccessor) {
$(oElement).toggleClass('fade', !Globals.bMobileDevice).modal({
'keyboard': false,
'show': ko.utils.unwrapObservable(fValueAccessor())
@ -3169,11 +3169,19 @@ ko.bindingHandlers.emailsTags = {
'init': function(oElement, fValueAccessor) {
var
$oEl = $(oElement),
fValue = fValueAccessor()
fValue = fValueAccessor(),
fFocusCallback = function (bValue) {
if (fValue && fValue.focusTrigger)
{
fValue.focusTrigger(bValue);
}
}
;
$oEl.inputosaurus({
'parseOnBlur': true,
'allowDragAndDrop': true,
'focusCallback': fFocusCallback,
'inputDelimiters': [',', ';'],
'autoCompleteSource': function (oData, fResponse) {
RL.getAutocomplete(oData.term, function (aData) {
@ -3184,12 +3192,12 @@ ko.bindingHandlers.emailsTags = {
},
'parseHook': function (aInput) {
return _.map(aInput, function (sInputValue) {
var
sValue = Utils.trim(sInputValue),
oEmail = null
;
if ('' !== sValue)
{
oEmail = new EmailModel();
@ -3219,8 +3227,83 @@ ko.bindingHandlers.emailsTags = {
if (fValue.focusTrigger)
{
fValue.focusTrigger.subscribe(function () {
$oEl.inputosaurus('focus');
fValue.focusTrigger.subscribe(function (bValue) {
if (bValue)
{
$oEl.inputosaurus('focus');
}
});
}
}
};
ko.bindingHandlers.contactTags = {
'init': function(oElement, fValueAccessor) {
var
$oEl = $(oElement),
fValue = fValueAccessor(),
fFocusCallback = function (bValue) {
if (fValue && fValue.focusTrigger)
{
fValue.focusTrigger(bValue);
}
}
;
$oEl.inputosaurus({
'parseOnBlur': true,
'allowDragAndDrop': false,
'focusCallback': fFocusCallback,
'inputDelimiters': [';'],
'outputDelimiter': ';',
'autoCompleteSource': function (oData, fResponse) {
RL.getContactsTagsAutocomplete(oData.term, function (aData) {
fResponse(_.map(aData, function (oTagItem) {
return oTagItem.toLine(false);
}));
});
},
'parseHook': function (aInput) {
return _.map(aInput, function (sInputValue) {
var
sValue = Utils.trim(sInputValue),
oTag = null
;
if ('' !== sValue)
{
oTag = new ContactTagModel();
oTag.name(sValue);
return [oTag.toLine(false), oTag];
}
return [sValue, null];
});
},
'change': _.bind(function (oEvent) {
$oEl.data('ContactsTagsValue', oEvent.target.value);
fValue(oEvent.target.value);
}, this)
});
fValue.subscribe(function (sValue) {
if ($oEl.data('ContactsTagsValue') !== sValue)
{
$oEl.val(sValue);
$oEl.data('ContactsTagsValue', sValue);
$oEl.inputosaurus('refresh');
}
});
if (fValue.focusTrigger)
{
fValue.focusTrigger.subscribe(function (bValue) {
if (bValue)
{
$oEl.inputosaurus('focus');
}
});
}
}
@ -3370,14 +3453,14 @@ ko.observable.fn.validateFunc = function (fFunc)
this.subscribe(function (sValue) {
this.hasFuncError(!fFunc(sValue));
}, this);
this.valueHasMutated();
}
return this;
};
/**
* @constructor
*/
@ -3689,7 +3772,7 @@ LinkBuilder.prototype.socialFacebook = function ()
{
return this.sServer + 'SocialFacebook' + ('' !== this.sSpecSuffix ? '/' + this.sSpecSuffix + '/' : '');
};
/**
* @type {Object}
*/
@ -3783,7 +3866,7 @@ Plugins.settingsGet = function (sPluginSection, sName)
};
/**
* @constructor
*/
@ -3857,7 +3940,7 @@ CookieDriver.prototype.get = function (sKey)
return mResult;
};
/**
* @constructor
*/
@ -3928,7 +4011,7 @@ LocalStorageDriver.prototype.get = function (sKey)
return mResult;
};
/**
* @constructor
*/
@ -3971,7 +4054,7 @@ LocalStorage.prototype.get = function (iKey)
{
return this.oDriver ? this.oDriver.get('p' + iKey) : null;
};
/**
* @constructor
*/
@ -3984,7 +4067,7 @@ KnoinAbstractBoot.prototype.bootstart = function ()
{
};
/**
* @param {string=} sPosition = ''
* @param {string=} sTemplate = ''
@ -4002,7 +4085,7 @@ function KnoinAbstractViewModel(sPosition, sTemplate)
this.viewModelName = '';
this.viewModelVisibility = ko.observable(false);
this.modalVisibility = ko.observable(false).extend({'rateLimit': 0});
this.viewModelDom = null;
}
@ -4057,20 +4140,27 @@ KnoinAbstractViewModel.prototype.restoreKeyScope = function ()
RL.data().keyScope(this.sCurrentKeyScope);
};
KnoinAbstractViewModel.prototype.registerPopupEscapeKey = function ()
KnoinAbstractViewModel.prototype.registerPopupKeyDown = function ()
{
var self = this;
$window.on('keydown', function (oEvent) {
if (oEvent && Enums.EventKeyCode.Esc === oEvent.keyCode && self.modalVisibility && self.modalVisibility())
if (oEvent && self.modalVisibility && self.modalVisibility())
{
Utils.delegateRun(self, 'cancelCommand');
return false;
if (!this.bDisabeCloseOnEsc && Enums.EventKeyCode.Esc === oEvent.keyCode)
{
Utils.delegateRun(self, 'cancelCommand');
return false;
}
else if (Enums.EventKeyCode.Backspace === oEvent.keyCode && !Utils.inFocus())
{
return false;
}
}
return true;
});
};
/**
* @param {string} sScreenName
* @param {?=} aViewModels = []
@ -4146,7 +4236,7 @@ KnoinAbstractScreen.prototype.__start = function ()
this.oCross = oRoute;
}
};
/**
* @constructor
*/
@ -4230,7 +4320,7 @@ Knoin.prototype.buildViewModel = function (ViewModelClass, oScreen)
ViewModelClass.__builded = true;
ViewModelClass.__vm = oViewModel;
oViewModel.data = RL.data();
oViewModel.viewModelName = ViewModelClass.__name;
if (oViewModelPlace && 1 === oViewModelPlace.length)
@ -4273,19 +4363,19 @@ Knoin.prototype.buildViewModel = function (ViewModelClass, oScreen)
self.viewModelDom.hide();
}, 300);
}
}, oViewModel);
}
Plugins.runHook('view-model-pre-build', [ViewModelClass.__name, oViewModel, oViewModelDom]);
ko.applyBindings(oViewModel, oViewModelDom[0]);
Utils.delegateRun(oViewModel, 'onBuild', [oViewModelDom]);
if (oViewModel && 'Popups' === sPosition && !oViewModel.bDisabeCloseOnEsc)
if (oViewModel && 'Popups' === sPosition)
{
oViewModel.registerPopupEscapeKey();
oViewModel.registerPopupKeyDown();
}
Plugins.runHook('view-model-post-build', [ViewModelClass.__name, oViewModel, oViewModelDom]);
}
else
@ -4387,7 +4477,7 @@ Knoin.prototype.screenOnRoute = function (sScreenName, sSubPart)
}
_.defer(function () {
// hide screen
if (self.oCurrentScreen)
{
@ -4483,7 +4573,7 @@ Knoin.prototype.startScreens = function (aScreensClasses)
{
oScreen.__started = true;
oScreen.__start();
Plugins.runHook('screen-pre-start', [oScreen.screenName(), oScreen]);
Utils.delegateRun(oScreen, 'onStart');
Plugins.runHook('screen-post-start', [oScreen.screenName(), oScreen]);
@ -4546,7 +4636,7 @@ Knoin.prototype.bootstart = function ()
};
kn = new Knoin();
/**
* @param {string=} sEmail
* @param {string=} sName
@ -4910,7 +5000,51 @@ EmailModel.prototype.inputoTagLine = function ()
{
return 0 < this.name.length ? this.name + ' (' + this.email + ')' : this.email;
};
/**
* @constructor
*/
function ContactTagModel()
{
this.idContactTag = 0;
this.name = ko.observable('');
this.readOnly = false;
}
ContactTagModel.prototype.parse = function (oItem)
{
var bResult = false;
if (oItem && 'Object/Tag' === oItem['@Object'])
{
this.idContact = Utils.pInt(oItem['IdContactTag']);
this.name(Utils.pString(oItem['Name']));
this.readOnly = !!oItem['ReadOnly'];
bResult = true;
}
return bResult;
};
/**
* @param {string} sSearch
* @return {boolean}
*/
ContactTagModel.prototype.filterHelper = function (sSearch)
{
return this.name().toLowerCase().indexOf(sSearch.toLowerCase()) !== -1;
};
/**
* @param {boolean=} bEncodeHtml = false
* @return {string}
*/
ContactTagModel.prototype.toLine = function (bEncodeHtml)
{
return (Utils.isUnd(bEncodeHtml) ? false : !!bEncodeHtml) ?
Utils.encodeHtml(this.name()) : this.name();
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5205,7 +5339,7 @@ PopupsDomainViewModel.prototype.clearForm = function ()
this.smtpAuth(true);
this.whiteList('');
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5342,7 +5476,7 @@ PopupsPluginViewModel.prototype.onBuild = function ()
}
}, this));
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5458,7 +5592,7 @@ PopupsActivateViewModel.prototype.validateSubscriptionKey = function ()
{
var sValue = this.key();
return '' === sValue || !!/^RL[\d]+-[A-Z0-9\-]+Z$/.test(Utils.trim(sValue));
};
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5518,7 +5652,7 @@ PopupsLanguagesViewModel.prototype.changeLanguage = function (sLang)
RL.data().mainLanguage(sLang);
this.cancelCommand();
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5624,7 +5758,7 @@ PopupsAskViewModel.prototype.onBuild = function ()
}, this));
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5711,7 +5845,7 @@ AdminLoginViewModel.prototype.onHide = function ()
{
this.loginFocus(false);
};
/**
* @param {?} oScreen
*
@ -5735,7 +5869,7 @@ AdminMenuViewModel.prototype.link = function (sRoute)
{
return '#/' + sRoute;
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5759,7 +5893,7 @@ AdminPaneViewModel.prototype.logoutClick = function ()
RL.remote().adminLogout(function () {
RL.loginAndLogoutReload();
});
};
};
/**
* @constructor
*/
@ -5859,7 +5993,7 @@ AdminGeneral.prototype.selectLanguage = function ()
{
kn.showScreenPopup(PopupsLanguagesViewModel);
};
/**
* @constructor
*/
@ -5911,7 +6045,7 @@ AdminLogin.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
@ -5980,7 +6114,7 @@ AdminBranding.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
@ -6200,7 +6334,7 @@ AdminContacts.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
@ -6289,7 +6423,7 @@ AdminDomains.prototype.onDomainListChangeRequest = function ()
{
RL.reloadDomainList();
};
/**
* @constructor
*/
@ -6401,7 +6535,7 @@ AdminSecurity.prototype.phpInfoLink = function ()
{
return RL.link().phpInfo();
};
/**
* @constructor
*/
@ -6517,7 +6651,7 @@ AdminSocial.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
@ -6614,7 +6748,7 @@ AdminPlugins.prototype.onPluginDisableRequest = function (sResult, oData)
RL.reloadPluginList();
};
/**
* @constructor
*/
@ -6712,7 +6846,7 @@ AdminPackages.prototype.installPackage = function (oPackage)
RL.remote().packageInstall(this.requestHelper(oPackage, true), oPackage);
}
};
/**
* @constructor
*/
@ -6763,7 +6897,7 @@ AdminLicensing.prototype.licenseExpiredMomentValue = function ()
{
var oDate = moment.unix(this.licenseExpired());
return oDate.format('LL') + ' (' + oDate.from(moment()) + ')';
};
};
/**
* @constructor
*/
@ -6893,7 +7027,7 @@ AbstractData.prototype.populateDataOnStart = function()
this.contactsIsAllowed(!!RL.settingsGet('ContactsIsAllowed'));
};
/**
* @constructor
* @extends AbstractData
@ -6935,7 +7069,7 @@ _.extend(AdminDataStorage.prototype, AbstractData.prototype);
AdminDataStorage.prototype.populateDataOnStart = function()
{
AbstractData.prototype.populateDataOnStart.call(this);
};
};
/**
* @constructor
*/
@ -7209,7 +7343,7 @@ AbstractAjaxRemoteStorage.prototype.jsVersion = function (fCallback, sVersion)
'Version': sVersion
});
};
/**
* @constructor
* @extends AbstractAjaxRemoteStorage
@ -7454,7 +7588,7 @@ AdminAjaxRemoteStorage.prototype.adminPing = function (fCallback)
{
this.defaultRequest(fCallback, 'AdminPing');
};
/**
* @constructor
*/
@ -7537,7 +7671,7 @@ AbstractCacheStorage.prototype.setEmailsPicsHashesData = function (oData)
{
this.oEmailsPicsHashes = oData;
};
/**
* @constructor
* @extends AbstractCacheStorage
@ -7548,7 +7682,7 @@ function AdminCacheStorage()
}
_.extend(AdminCacheStorage.prototype, AbstractCacheStorage.prototype);
/**
* @param {Array} aViewModels
* @constructor
@ -7726,7 +7860,7 @@ AbstractSettings.prototype.routes = function ()
['', oRules]
];
};
/**
* @constructor
* @extends KnoinAbstractScreen
@ -7741,7 +7875,7 @@ _.extend(AdminLoginScreen.prototype, KnoinAbstractScreen.prototype);
AdminLoginScreen.prototype.onShow = function ()
{
RL.setTitle('');
};
};
/**
* @constructor
* @extends AbstractSettings
@ -7761,7 +7895,7 @@ AdminSettingsScreen.prototype.onShow = function ()
// AbstractSettings.prototype.onShow.call(this);
RL.setTitle('');
};
};
/**
* @constructor
* @extends KnoinAbstractBoot
@ -8109,7 +8243,7 @@ AbstractApp.prototype.bootstart = function ()
ssm.ready();
};
/**
* @constructor
* @extends AbstractApp
@ -8348,7 +8482,7 @@ AdminApp.prototype.bootstart = function ()
* @type {AdminApp}
*/
RL = new AdminApp();
$html.addClass(Globals.bMobileDevice ? 'mobile' : 'no-mobile');
$window.keydown(Utils.killCtrlAandS).keyup(Utils.killCtrlAandS);
@ -8399,9 +8533,9 @@ window['__RLBOOT'] = function (fCall) {
window['__RLBOOT'] = null;
});
};
if (window.SimplePace) {
window.SimplePace.add(10);
}
}
}(window, jQuery, ko, crossroads, hasher, _));

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
/**
* Inputosaurus Text
* Inputosaurus Text
*
* Must be instantiated on an <input> element
* Allows multiple input items. Each item is represented with a removable tag that appears to be inside the input area.
@ -33,7 +33,7 @@
//
// 'change' - triggered whenever a tag is added or removed (should be similar to binding the the change event of the instantiated input
// 'keyup' - keyup event on the newly created input
// while typing, the user can separate values using these delimiters
// the value tags are created on the fly when an inputDelimiter is detected
inputDelimiters : [',', ';'],
@ -43,6 +43,10 @@
allowDuplicates : false,
allowDragAndDrop : true,
focusCallback : null,
parseOnBlur : false,
// optional wrapper for widget
@ -59,7 +63,7 @@
// manipulate and return the input value after parseInput() parsing
// the array of tag names is passed and expected to be returned as an array after manipulation
parseHook : null,
// define a placeholder to display when the input is empty
placeholder: null
},
@ -69,36 +73,46 @@
els = {},
o = widget.options,
placeholder = o.placeholder || this.element.attr('placeholder') || null;
this._chosenValues = [];
// Create the elements
els.ul = $('<ul class="inputosaurus-container"></ul>');
els.ul.droppable({
'drop': function(event, ui) {
ui.draggable.addClass('inputosaurus-dropped');
els.input.val(ui.draggable.data('inputosaurus-value'));
if (ui.draggable.__widget)
{
ui.draggable.__widget._removeDraggedTag(ui.draggable);
if (this.options.allowDragAndDrop)
{
els.ul.droppable({
'drop': function(event, ui) {
ui.draggable.addClass('inputosaurus-dropped');
els.input.val(ui.draggable.data('inputosaurus-value'));
if (ui.draggable.__widget)
{
ui.draggable.__widget._removeDraggedTag(ui.draggable);
}
widget.parseInput();
}
});
}
widget.parseInput();
}
});
els.input = $('<input type="text" />');
// els.input = $('<input type="email" />');
els.input = $('<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />');
// els.input = $('<input type="email" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />');
els.inputCont = $('<li class="inputosaurus-input inputosaurus-required"></li>');
els.origInputCont = $('<li class="inputosaurus-input-hidden inputosaurus-required"></li>');
els.lastEdit = '';
els.input.on('focus', function () {
widget._focusTrigger(true);
}).on('blur', function () {
widget._focusTrigger(false);
});
// define starting placeholder
if (placeholder) {
if (placeholder) {
o.placeholder = placeholder;
els.input.attr('placeholder', o.placeholder);
els.input.attr('placeholder', o.placeholder);
if (o.width) {
els.input.css('min-width', o.width - 50);
}
@ -107,11 +121,11 @@
o.wrapperElement && o.wrapperElement.append(els.ul);
this.element.replaceWith(o.wrapperElement || els.ul);
els.origInputCont.append(this.element).hide();
els.inputCont.append(els.input);
els.ul.append(els.inputCont);
els.ul.append(els.origInputCont);
o.width && els.ul.css('width', o.width);
this.elements = els;
@ -127,6 +141,20 @@
this._instAutocomplete();
},
_focusTriggerTimer : 0,
_focusTrigger : function (bValue) {
var widget = this;
window.clearTimeout(this._focusTriggerTimer);
this._focusTriggerTimer = window.setTimeout(function () {
widget.elements.ul[!bValue ? 'removeClass' : 'addClass']('inputosaurus-focused');
if (widget.options.focusCallback)
{
widget.options.focusCallback(bValue);
}
}, 10);
},
_instAutocomplete : function() {
if(this.options.autoCompleteSource){
var widget = this;
@ -264,7 +292,7 @@
widget.elements.input.width(txtWidth);
// }, 1);
},
// resets placeholder on representative input
_resetPlaceholder: function () {
var placeholder = this.options.placeholder,
@ -289,7 +317,7 @@
ev.preventDefault();
lastTag.find('a').focus();
}
},
_editTag : function(ev) {
@ -304,7 +332,7 @@
ev.preventDefault();
var
var
oPrev = null,
next = false
;
@ -325,7 +353,7 @@
{
widget.elements.lastEdit = oPrev.value;
}
$li.after(widget.elements.inputCont);
widget.elements.input.val(tagName);
@ -488,21 +516,25 @@
// @className optional className for <li>
_createTag : function(name, key, obj) {
if (name !== undefined && obj) {
var
var
widget = this,
$li = $('<li data-inputosaurus="' + key + '" title="' + obj.toLine(false, false, true) +
'"><a href="javascript:void(0);" class="ficon">&#x2716;</a><span>' +
obj.toLine(true, false, true) + '</span></li>')
;
$li.data('inputosaurus-value', obj.toLine(false, false, false));
$li.draggable({
'revert': 'invalid',
'revertDuration': 200,
'start': function(event, ui) {
ui.helper.__widget = widget;
}
});
if (this.options.allowDragAndDrop)
{
$li.draggable({
'revert': 'invalid',
'revertDuration': 200,
'start': function(event, ui) {
ui.helper.__widget = widget;
}
});
}
return $li;
}
@ -594,7 +626,7 @@
values.push(val);
delim && (values = val.split(delim));
if(values.length){
if (values.length) {
this._chosenValues = [];
$.isFunction(this.options.parseHook) && (values = this.options.parseHook(values));
@ -607,12 +639,14 @@
},
_attachEvents : function() {
var widget = this;
this.elements.input.on('keyup.inputosaurus', {widget : widget}, this._inputKeypress);
this.elements.input.on('keydown.inputosaurus', {widget : widget}, this._inputKeypress);
this.elements.input.on('change.inputosaurus', {widget : widget}, this._inputKeypress);
this.elements.input.on('focus.inputosaurus', {widget : widget}, this._inputFocus);
this.options.parseOnBlur && this.elements.input.on('blur.inputosaurus', {widget : widget}, this.parseInput);
this.elements.ul.on('click.inputosaurus', {widget : widget}, this._focus);
@ -626,14 +660,12 @@
_destroy: function() {
this.elements.input.unbind('.inputosaurus');
this.elements.ul.replaceWith(this.element);
}
};
$('body').append(inputosaurustext.fakeSpan);
$.widget("ui.inputosaurus", inputosaurustext);
})(jQuery);

File diff suppressed because one or more lines are too long