mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Contacts tags support (unstable - step 1).
+ Small fixes
This commit is contained in:
parent
74fa12c000
commit
0ca00dec40
34 changed files with 1790 additions and 801 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -974,6 +974,17 @@ RainLoopApp.prototype.getAutocomplete = function (sQuery, 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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -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');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -134,9 +134,9 @@ Knoin.prototype.buildViewModel = function (ViewModelClass, oScreen)
|
|||
|
||||
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]);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ function ContactModel()
|
|||
this.idContact = 0;
|
||||
this.display = '';
|
||||
this.properties = [];
|
||||
this.tags = '';
|
||||
this.readOnly = false;
|
||||
|
||||
this.focused = ko.observable(false);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
45
dev/Models/ContactTagModel.js
Normal file
45
dev/Models/ContactTagModel.js
Normal 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();
|
||||
};
|
||||
|
|
@ -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
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ 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});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
.inputosaurus-container {
|
||||
|
||||
width: 99%;
|
||||
line-height: 20px;
|
||||
padding: 2px;
|
||||
|
|
@ -9,6 +10,12 @@
|
|||
.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;
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ function MailBoxMessageViewViewModel()
|
|||
}, this.messageVisibility);
|
||||
|
||||
// viewer
|
||||
this.viewHash = '';
|
||||
this.viewSubject = ko.observable('');
|
||||
this.viewFromShort = ko.observable('');
|
||||
this.viewToShort = ko.observable('');
|
||||
|
|
@ -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,6 +186,11 @@ function MailBoxMessageViewViewModel()
|
|||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.viewHash = '';
|
||||
this.scrollMessageToTop();
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
|
|
@ -202,10 +214,6 @@ function MailBoxMessageViewViewModel()
|
|||
}
|
||||
});
|
||||
|
||||
this.messageActiveDom.subscribe(function () {
|
||||
this.scrollMessageToTop();
|
||||
}, this);
|
||||
|
||||
this.goUpCommand = Utils.createCommand(this, function () {
|
||||
RL.pub('mailbox.message-list.selector.go-up');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ 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);
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
@ -291,7 +307,7 @@ function PopupsContactsViewModel()
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
}, sRequestUid, this.viewID(), aProperties);
|
||||
}, sRequestUid, this.viewID(), this.viewTags(), aProperties);
|
||||
|
||||
}, function () {
|
||||
var
|
||||
|
|
@ -324,7 +340,7 @@ function PopupsContactsViewModel()
|
|||
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');
|
||||
|
|
@ -562,6 +584,7 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
|
|||
|
||||
this.emptySelection(false);
|
||||
this.viewReadOnly(false);
|
||||
this.viewTags('');
|
||||
|
||||
if (oContact)
|
||||
{
|
||||
|
|
@ -587,9 +610,14 @@ 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)));
|
||||
|
||||
|
|
@ -626,7 +654,8 @@ 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)
|
||||
|
|
@ -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());
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4973,11 +4973,17 @@ class Actions
|
|||
$iLimit = 0 > $iLimit ? 20 : $iLimit;
|
||||
|
||||
$iResultCount = 0;
|
||||
if ($this->AddressBookProvider($oAccount)->IsActive())
|
||||
$mResult = array();
|
||||
$aTags = array();
|
||||
|
||||
$oAbp = $this->AddressBookProvider($oAccount);
|
||||
if ($oAbp->IsActive())
|
||||
{
|
||||
$iResultCount = 0;
|
||||
$mResult = $this->AddressBookProvider($oAccount)->GetContacts($oAccount->ParentEmailHelper(),
|
||||
$mResult = $oAbp->GetContacts($oAccount->ParentEmailHelper(),
|
||||
$iOffset, $iLimit, $sSearch, $iResultCount);
|
||||
|
||||
$aTags = $oAbp->GetContactTags($oAccount->ParentEmailHelper());
|
||||
}
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__, array(
|
||||
|
|
@ -4985,6 +4991,26 @@ class Actions
|
|||
'Limit' => $iLimit,
|
||||
'Count' => $iResultCount,
|
||||
'Search' => $sSearch,
|
||||
'List' => $mResult,
|
||||
'Tags' => $aTags
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function DoContactTags()
|
||||
{
|
||||
$oAccount = $this->getAccountFromToken();
|
||||
|
||||
$mResult = false;
|
||||
if ($this->AddressBookProvider($oAccount)->IsActive())
|
||||
{
|
||||
$mResult = $this->AddressBookProvider($oAccount)->GetContactTags($oAccount->ParentEmailHelper());
|
||||
}
|
||||
|
||||
return $this->DefaultResponse(__FUNCTION__, array(
|
||||
'List' => $mResult
|
||||
));
|
||||
}
|
||||
|
|
@ -5025,6 +5051,11 @@ class Actions
|
|||
if ($oAddressBookProvider && $oAddressBookProvider->IsActive() && 0 < \strlen($sRequestUid))
|
||||
{
|
||||
$sUid = \trim($this->GetActionParam('Uid', ''));
|
||||
$sTags = \trim($this->GetActionParam('Tags', ''));
|
||||
$aTags = \explode(';', $sTags);
|
||||
$aTags = \array_map('trim', $aTags);
|
||||
$aTags = \array_unique($aTags);
|
||||
|
||||
$oContact = null;
|
||||
if (0 < \strlen($sUid))
|
||||
{
|
||||
|
|
@ -5040,6 +5071,7 @@ class Actions
|
|||
}
|
||||
}
|
||||
|
||||
$oContact->Tags = $aTags;
|
||||
$oContact->Properties = array();
|
||||
$aProperties = $this->GetActionParam('Properties', array());
|
||||
if (\is_array($aProperties))
|
||||
|
|
@ -6933,9 +6965,19 @@ class Actions
|
|||
'Display' => \MailSo\Base\Utils::Utf8Clear($mResponse->Display),
|
||||
'ReadOnly' => $mResponse->ReadOnly,
|
||||
'IdPropertyFromSearch' => $mResponse->IdPropertyFromSearch,
|
||||
'Tags' => $this->responseObject($mResponse->Tags, $sParent, $aParameters),
|
||||
'Properties' => $this->responseObject($mResponse->Properties, $sParent, $aParameters)
|
||||
));
|
||||
}
|
||||
else if ('RainLoop\Providers\AddressBook\Classes\Tag' === $sClassName)
|
||||
{
|
||||
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array(
|
||||
/* @var $mResponse \RainLoop\Providers\AddressBook\Classes\Tag */
|
||||
'IdContactTag' => $mResponse->IdContactTag,
|
||||
'Name' => \MailSo\Base\Utils::Utf8Clear($mResponse->Name),
|
||||
'ReadOnly' => $mResponse->ReadOnly
|
||||
));
|
||||
}
|
||||
else if ('RainLoop\Providers\AddressBook\Classes\Property' === $sClassName)
|
||||
{
|
||||
// Simple hack
|
||||
|
|
|
|||
|
|
@ -224,13 +224,13 @@ abstract class PdoAbstract
|
|||
}
|
||||
|
||||
/**
|
||||
* @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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ class Contact
|
|||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $Tags;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $ReadOnly;
|
||||
|
||||
/**
|
||||
|
|
@ -58,10 +63,10 @@ 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 = '';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -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
|
||||
|
|
@ -704,6 +811,7 @@ class PdoAddressBook
|
|||
|
||||
$iResultCount = $iCount;
|
||||
|
||||
$aResult = array();
|
||||
if (0 < $iCount)
|
||||
{
|
||||
$sSql = 'SELECT * FROM rainloop_ab_contacts WHERE deleted = 0 AND id_user = :id_user';
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1506,7 +1737,7 @@ SQLITEINITIAL;
|
|||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $mCache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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 />
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
2
rainloop/v/0.0.0/static/css/app.min.css
vendored
2
rainloop/v/0.0.0/static/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -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),
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -4057,14 +4140,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;
|
||||
|
|
@ -4281,9 +4371,9 @@ Knoin.prototype.buildViewModel = function (ViewModelClass, oScreen)
|
|||
|
||||
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]);
|
||||
|
|
@ -4911,6 +5001,50 @@ 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
|
||||
|
|
|
|||
8
rainloop/v/0.0.0/static/js/admin.min.js
vendored
8
rainloop/v/0.0.0/static/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1806,7 +1806,7 @@ Utils.setExpandedFolder = function (sFullNameHash, bExpanded)
|
|||
Utils.initLayoutResizer = function (sLeft, sRight, sClientSideKeyName)
|
||||
{
|
||||
var
|
||||
iDisabledWidth = 65,
|
||||
iDisabledWidth = 60,
|
||||
iMinWidth = 155,
|
||||
oLeft = $(sLeft),
|
||||
oRight = $(sRight),
|
||||
|
|
@ -3173,11 +3173,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) {
|
||||
|
|
@ -3223,8 +3231,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');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -4990,14 +5073,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;
|
||||
|
|
@ -5214,9 +5304,9 @@ Knoin.prototype.buildViewModel = function (ViewModelClass, oScreen)
|
|||
|
||||
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]);
|
||||
|
|
@ -5852,6 +5942,7 @@ function ContactModel()
|
|||
this.idContact = 0;
|
||||
this.display = '';
|
||||
this.properties = [];
|
||||
this.tags = '';
|
||||
this.readOnly = false;
|
||||
|
||||
this.focused = ko.observable(false);
|
||||
|
|
@ -5902,6 +5993,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']))
|
||||
{
|
||||
|
|
@ -5913,6 +6005,11 @@ ContactModel.prototype.parse = function (oItem)
|
|||
}, this);
|
||||
}
|
||||
|
||||
if (Utils.isNonEmptyArray(oItem['Tags']))
|
||||
{
|
||||
this.tags = oItem['Tags'].join(';');
|
||||
}
|
||||
|
||||
bResult = true;
|
||||
}
|
||||
|
||||
|
|
@ -5990,6 +6087,50 @@ function ContactPropertyModel(iType, sTypeStr, sValue, bFocused, sPlaceholder)
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
|
@ -9732,6 +9873,7 @@ 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);
|
||||
|
||||
|
|
@ -9752,6 +9894,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) {
|
||||
|
|
@ -9999,7 +10156,7 @@ function PopupsContactsViewModel()
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
}, sRequestUid, this.viewID(), aProperties);
|
||||
}, sRequestUid, this.viewID(), this.viewTags(), aProperties);
|
||||
|
||||
}, function () {
|
||||
var
|
||||
|
|
@ -10032,7 +10189,7 @@ function PopupsContactsViewModel()
|
|||
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('');
|
||||
});
|
||||
|
|
@ -10093,6 +10250,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');
|
||||
|
|
@ -10270,6 +10433,7 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
|
|||
|
||||
this.emptySelection(false);
|
||||
this.viewReadOnly(false);
|
||||
this.viewTags('');
|
||||
|
||||
if (oContact)
|
||||
{
|
||||
|
|
@ -10295,9 +10459,14 @@ 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)));
|
||||
|
||||
|
|
@ -10334,7 +10503,8 @@ 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)
|
||||
|
|
@ -10351,14 +10521,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());
|
||||
};
|
||||
|
||||
|
|
@ -13187,6 +13369,7 @@ function MailBoxMessageViewViewModel()
|
|||
}, this.messageVisibility);
|
||||
|
||||
// viewer
|
||||
this.viewHash = '';
|
||||
this.viewSubject = ko.observable('');
|
||||
this.viewFromShort = ko.observable('');
|
||||
this.viewToShort = ko.observable('');
|
||||
|
|
@ -13219,6 +13402,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));
|
||||
|
|
@ -13246,6 +13435,11 @@ function MailBoxMessageViewViewModel()
|
|||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.viewHash = '';
|
||||
this.scrollMessageToTop();
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
|
|
@ -13269,10 +13463,6 @@ function MailBoxMessageViewViewModel()
|
|||
}
|
||||
});
|
||||
|
||||
this.messageActiveDom.subscribe(function () {
|
||||
this.scrollMessageToTop();
|
||||
}, this);
|
||||
|
||||
this.goUpCommand = Utils.createCommand(this, function () {
|
||||
RL.pub('mailbox.message-list.selector.go-up');
|
||||
});
|
||||
|
|
@ -15260,6 +15450,7 @@ 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});
|
||||
|
|
@ -17390,11 +17581,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
|
||||
});
|
||||
};
|
||||
|
|
@ -19612,6 +19804,17 @@ RainLoopApp.prototype.getAutocomplete = function (sQuery, 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) {
|
||||
|
|
|
|||
18
rainloop/v/0.0.0/static/js/app.min.js
vendored
18
rainloop/v/0.0.0/static/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
80
vendors/inputosaurus/inputosaurus.js
vendored
80
vendors/inputosaurus/inputosaurus.js
vendored
|
|
@ -43,6 +43,10 @@
|
|||
|
||||
allowDuplicates : false,
|
||||
|
||||
allowDragAndDrop : true,
|
||||
|
||||
focusCallback : null,
|
||||
|
||||
parseOnBlur : false,
|
||||
|
||||
// optional wrapper for widget
|
||||
|
|
@ -74,27 +78,37 @@
|
|||
|
||||
// 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 (this.options.allowDragAndDrop)
|
||||
{
|
||||
els.ul.droppable({
|
||||
'drop': function(event, ui) {
|
||||
|
||||
if (ui.draggable.__widget)
|
||||
{
|
||||
ui.draggable.__widget._removeDraggedTag(ui.draggable);
|
||||
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) {
|
||||
o.placeholder = placeholder;
|
||||
|
|
@ -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;
|
||||
|
|
@ -496,13 +524,17 @@
|
|||
;
|
||||
|
||||
$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);
|
||||
|
||||
|
|
|
|||
2
vendors/inputosaurus/inputosaurus.min.js
vendored
2
vendors/inputosaurus/inputosaurus.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue