Synchronizing with a remote CardDAV server (#42, #138, #139)

+ Updated contacts sql shema (breaking changes)
+ Fixes
- Removed SabreDAV Server
- Removed Contacts Sharing (awhile, code refactoring)
This commit is contained in:
RainLoop Team 2014-04-26 01:50:17 +04:00
parent 54a4c2657a
commit d29f20789f
78 changed files with 2024 additions and 2317 deletions

View file

@ -190,7 +190,7 @@ AdminContacts.prototype.onBuild = function ()
'ContactsSync': bValue ? '1' : '0'
});
});
self.contactsType.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f5, {
'ContactsPdoType': sValue

View file

@ -14,7 +14,7 @@ Consts.Defaults.MessagesPerPage = 20;
* @const
* @type {number}
*/
Consts.Defaults.ContactsPerPage = 20;
Consts.Defaults.ContactsPerPage = 50;
/**
* @const
@ -46,6 +46,12 @@ Consts.Defaults.SendMessageAjaxTimeout = 300000;
*/
Consts.Defaults.SaveMessageAjaxTimeout = 200000;
/**
* @const
* @type {number}
*/
Consts.Defaults.ContactsSyncAjaxTimeout = 200000;
/**
* @const
* @type {string}

View file

@ -263,14 +263,6 @@ Enums.Layout = {
'BottomPreview': 2
};
/**
* @enum {number}
*/
Enums.ContactScopeType = {
'Default': 0,
'ShareAll': 2
};
/**
* @enum {number}
*/
@ -300,17 +292,9 @@ Enums.ContactPropertyType = {
'NamePrefix': 20,
'NameSuffix': 21,
'EmailPersonal': 30,
'EmailBussines': 31,
'PhonePersonal': 50,
'PhoneBussines': 51,
'MobilePersonal': 60,
'MobileBussines': 61,
'FaxPesonal': 70,
'FaxBussines': 71,
'Email': 30,
'Phone': 31,
'Web': 32,
'Facebook': 90,
'Skype': 91,
@ -344,6 +328,8 @@ Enums.Notification = {
'NewPasswordShort': 132,
'NewPasswordWeak': 133,
'NewPasswordForbidden': 134,
'ContactsSyncError': 140,
'CantGetMessageList': 201,
'CantGetMessage': 202,

View file

@ -577,6 +577,8 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.NewPasswordWeak] = Utils.i18n('NOTIFICATIONS/NEW_PASSWORD_WEAK');
NotificationI18N[Enums.Notification.NewPasswordForbidden] = Utils.i18n('NOTIFICATIONS/NEW_PASSWORD_FORBIDDENT');
NotificationI18N[Enums.Notification.ContactsSyncError] = Utils.i18n('NOTIFICATIONS/CONTACTS_SYNC_ERROR');
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
@ -1362,6 +1364,15 @@ Utils.fakeMd5 = function(iLen)
return sResult;
};
/* jshint ignore:start */
/**
* @param {string} s
* @return {string}
*/
Utils.md5 = function(s){function L(k,d){return(k<<d)|(k>>>(32-d))}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H)}if(I|d){if(x&1073741824){return(x^3221225472^F^H)}else{return(x^1073741824^F^H)}}else{return(x^F^H)}}function r(d,F,k){return(d&F)|((~d)&k)}function q(d,F,k){return(d&k)|(F&(~k))}function p(d,F,k){return(d^F^k)}function n(d,F,k){return(F^(d|(~k)))}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F)}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F)}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F)}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F)}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H<F){Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=(aa[Z]|(G.charCodeAt(H)<<d));H++}Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=aa[Z]|(128<<d);aa[I-2]=F<<3;aa[I-1]=F>>>29;return aa}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2)}return k}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F<k.length;F++){var x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x)}else{if((x>127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128)}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128)}}}return d}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g)}var i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase()};
/* jshint ignore:end */
Utils.convertPlainTextToHtml = function (sPlain)
{
return sPlain.toString()
@ -1376,11 +1387,12 @@ Utils.draggeblePlace = function ()
Utils.defautOptionsAfterRender = function (oOption, oItem)
{
if (oItem && !Utils.isUnd(oItem.disabled))
if (oItem && !Utils.isUnd(oItem.disabled) && oOption)
{
ko.applyBindingsToNode(oOption, {
'disabled': oItem.disabled
}, oItem);
$(oOption)
.toggleClass('disabled', oItem.disabled)
.prop('disabled', oItem.disabled)
;
}
};

View file

@ -6,17 +6,14 @@
function ContactModel()
{
this.idContact = 0;
this.idContactStr = '';
this.display = '';
this.properties = [];
this.readOnly = false;
this.scopeType = Enums.ContactScopeType.Default;
this.focused = ko.observable(false);
this.selected = ko.observable(false);
this.checked = ko.observable(false);
this.deleted = ko.observable(false);
this.shared = ko.observable(false);
}
/**
@ -34,15 +31,15 @@ ContactModel.prototype.getNameAndEmailHelper = function ()
_.each(this.properties, function (aProperty) {
if (aProperty)
{
if ('' === sName && Enums.ContactPropertyType.FullName === aProperty[0])
if (Enums.ContactPropertyType.FirstName === aProperty[0])
{
sName = aProperty[1];
sName = Utils.trim(aProperty[1] + ' ' + sName);
}
else if ('' === sEmail && -1 < Utils.inArray(aProperty[0], [
Enums.ContactPropertyType.EmailPersonal,
Enums.ContactPropertyType.EmailBussines,
Enums.ContactPropertyType.EmailOther
]))
else if (Enums.ContactPropertyType.LastName === aProperty[0])
{
sName = Utils.trim(sName + ' ' + aProperty[1]);
}
else if ('' === sEmail && Enums.ContactPropertyType.Email === aProperty[0])
{
sEmail = aProperty[1];
}
@ -59,22 +56,19 @@ ContactModel.prototype.parse = function (oItem)
if (oItem && 'Object/Contact' === oItem['@Object'])
{
this.idContact = Utils.pInt(oItem['IdContact']);
this.idContactStr = Utils.pString(oItem['IdContactStr']);
this.display = Utils.pString(oItem['Display']);
this.readOnly = !!oItem['ReadOnly'];
this.scopeType = Utils.pInt(oItem['ScopeType']);
if (Utils.isNonEmptyArray(oItem['Properties']))
{
_.each(oItem['Properties'], function (oProperty) {
if (oProperty && oProperty['Type'] && Utils.isNormal(oProperty['Value']))
if (oProperty && oProperty['Type'] && Utils.isNormal(oProperty['Value']) && Utils.isNormal(oProperty['TypeStr']))
{
this.properties.push([Utils.pInt(oProperty['Type']), Utils.pString(oProperty['Value'])]);
this.properties.push([Utils.pInt(oProperty['Type']), Utils.pString(oProperty['Value']), Utils.pString(oProperty['TypeStr'])]);
}
}, this);
}
this.shared(Enums.ContactScopeType.ShareAll === this.scopeType);
bResult = true;
}
@ -115,10 +109,6 @@ ContactModel.prototype.lineAsCcc = function ()
{
aResult.push('checked');
}
if (this.shared())
{
aResult.push('shared');
}
if (this.focused())
{
aResult.push('focused');

View file

@ -2,15 +2,17 @@
/**
* @param {number=} iType = Enums.ContactPropertyType.Unknown
* @param {string=} sTypeStr = ''
* @param {string=} sValue = ''
* @param {boolean=} bFocused = false
* @param {string=} sPlaceholder = ''
*
* @constructor
*/
function ContactPropertyModel(iType, sValue, bFocused, sPlaceholder)
function ContactPropertyModel(iType, sTypeStr, sValue, bFocused, sPlaceholder)
{
this.type = ko.observable(Utils.isUnd(iType) ? Enums.ContactPropertyType.Unknown : iType);
this.typeStr = ko.observable(Utils.isUnd(sTypeStr) ? '' : sTypeStr);
this.focused = ko.observable(Utils.isUnd(bFocused) ? false : !!bFocused);
this.value = ko.observable(Utils.pString(sValue));

View file

@ -8,22 +8,34 @@ function SettingsContacts()
var oData = RL.data();
this.contactsAutosave = oData.contactsAutosave;
this.showPassword = ko.observable(false);
this.allowContactsSync = !!RL.settingsGet('ContactsSyncIsAllowed');
this.contactsSyncServer = RL.settingsGet('ContactsSyncServer');
this.contactsSyncUser = RL.settingsGet('ContactsSyncUser');
this.contactsSyncPass = RL.settingsGet('ContactsSyncPassword');
this.contactsSyncPabUrl = RL.settingsGet('ContactsSyncPabUrl');
this.allowContactsSync = oData.allowContactsSync;
this.enableContactsSync = oData.enableContactsSync;
this.contactsSyncUrl = oData.contactsSyncUrl;
this.contactsSyncUser = oData.contactsSyncUser;
this.contactsSyncPass = oData.contactsSyncPass;
this.saveTrigger = ko.computed(function () {
return [
this.enableContactsSync() ? '1' : '0',
this.contactsSyncUrl(),
this.contactsSyncUser(),
this.contactsSyncPass()
].join('|');
}, this).extend({'throttle': 500});
this.saveTrigger.subscribe(function () {
RL.remote().saveContactsSyncData(null,
this.enableContactsSync(),
this.contactsSyncUrl(),
this.contactsSyncUser(),
this.contactsSyncPass()
);
}, this);
}
Utils.addSettingsViewModel(SettingsContacts, 'SettingsContacts', 'SETTINGS_LABELS/LABEL_CONTACTS_NAME', 'contacts');
SettingsContacts.prototype.toggleShowPassword = function ()
{
this.showPassword(!this.showPassword());
};
SettingsContacts.prototype.onBuild = function ()
{
RL.data().contactsAutosave.subscribe(function (bValue) {
@ -33,7 +45,7 @@ SettingsContacts.prototype.onBuild = function ()
});
};
SettingsContacts.prototype.onShow = function ()
{
this.showPassword(false);
};
//SettingsContacts.prototype.onShow = function ()
//{
//
//};

View file

@ -8,6 +8,7 @@ function AbstractCacheStorage()
this.oEmailsPicsHashes = {};
this.oServices = {};
}
/**
* @type {Object}
*/
@ -28,26 +29,36 @@ AbstractCacheStorage.prototype.clear = function ()
* @param {string} sEmail
* @return {string}
*/
AbstractCacheStorage.prototype.getUserPic = function (sEmail)
AbstractCacheStorage.prototype.getUserPic = function (sEmail, fCallback)
{
sEmail = Utils.trim(sEmail);
var
sUrl = '',
sService = '',
sEmailLower = sEmail.toLowerCase(),
sPicHash = Utils.isUnd(this.oEmailsPicsHashes[sEmail]) ? '' : this.oEmailsPicsHashes[sEmail]
sPicHash = Utils.isUnd(this.oEmailsPicsHashes[sEmailLower]) ? '' : this.oEmailsPicsHashes[sEmailLower]
;
if ('' === sPicHash)
if ('' !== sPicHash)
{
sUrl = RL.link().getUserPicUrlFromHash(sPicHash);
}
else
{
sService = sEmailLower.substr(sEmail.indexOf('@') + 1);
sUrl = '' !== sService && this.oServices[sService] ? this.oServices[sService] : '';
}
else
{
sUrl = RL.link().getUserPicUrlFromHash(sPicHash);
}
return sUrl;
// if ('' === sUrl) // Gravatar // TODO
// {
// fCallback('//secure.gravatar.com/avatar/' + Utils.md5(sEmailLower) + '.jpg?s=80&d=mm', sEmail);
// }
// else
// {
fCallback(sUrl, sEmail);
// }
};
/**

View file

@ -112,6 +112,31 @@ WebMailAjaxRemoteStorage.prototype.clearTwoFactorInfo = function (fCallback)
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.contactsSync = function (fCallback)
{
this.defaultRequest(fCallback, 'ContactsSync', null, Consts.Defaults.ContactsSyncAjaxTimeout);
};
/**
* @param {?Function} fCallback
* @param {boolean} bEnable
* @param {string} sUrl
* @param {string} sUser
* @param {string} sPassword
*/
WebMailAjaxRemoteStorage.prototype.saveContactsSyncData = function (fCallback, bEnable, sUrl, sUser, sPassword)
{
this.defaultRequest(fCallback, 'SaveContactsSyncData', {
'Enable': bEnable ? '1' : '0',
'Url': sUrl,
'User': sUser,
'Password': sPassword
});
};
/**
* @param {?Function} fCallback
* @param {string} sEmail
@ -659,13 +684,11 @@ WebMailAjaxRemoteStorage.prototype.contacts = function (fCallback, iOffset, iLim
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.contactSave = function (fCallback, sRequestUid, sUid, sUidStr, iScopeType, aProperties)
WebMailAjaxRemoteStorage.prototype.contactSave = function (fCallback, sRequestUid, sUid, aProperties)
{
this.defaultRequest(fCallback, 'ContactSave', {
'RequestUid': sRequestUid,
'Uid': Utils.trim(sUid),
'UidStr': Utils.trim(sUidStr),
'ScopeType': iScopeType,
'Properties': aProperties
});
};

View file

@ -82,6 +82,18 @@ function WebMailDataStorage()
this.identities = ko.observableArray([]);
this.identitiesLoading = ko.observable(false).extend({'throttle': 100});
this.allowContactsSync = ko.observable(false);
this.enableContactsSync = ko.observable(false);
this.contactsSyncUrl = ko.observable('');
this.contactsSyncUser = ko.observable('');
this.contactsSyncPass = ko.observable('');
this.allowContactsSync = ko.observable(!!RL.settingsGet('ContactsSyncIsAllowed'));
this.enableContactsSync = ko.observable(!!RL.settingsGet('EnableContactsSync'));
this.contactsSyncUrl = ko.observable(RL.settingsGet('ContactsSyncUrl'));
this.contactsSyncUser = ko.observable(RL.settingsGet('ContactsSyncUser'));
this.contactsSyncPass = ko.observable(RL.settingsGet('ContactsSyncPassword'));
// folders
this.namespace = '';
this.folderList = ko.observableArray([]);

View file

@ -9,9 +9,9 @@ function MailBoxMessageViewViewModel()
KnoinAbstractViewModel.call(this, 'Right', 'MailMessageView');
var
sPic = '',
oData = RL.data(),
self = this,
sLastEmail = '',
oData = RL.data(),
createCommandHelper = function (sType) {
return Utils.createCommand(self, function () {
this.replyOrforward(sType);
@ -155,17 +155,19 @@ function MailBoxMessageViewViewModel()
this.viewViewLink(oMessage.viewLink());
this.viewDownloadLink(oMessage.downloadLink());
sPic = RL.cache().getUserPic(oMessage.fromAsSingleEmail());
if (sPic !== this.viewUserPic())
{
this.viewUserPicVisible(false);
this.viewUserPic(Consts.DataImages.UserDotPic);
if ('' !== sPic)
sLastEmail = oMessage.fromAsSingleEmail();
RL.cache().getUserPic(sLastEmail, function (sPic, $sEmail) {
if (sPic !== self.viewUserPic() && sLastEmail === $sEmail)
{
this.viewUserPicVisible(true);
this.viewUserPic(sPic);
self.viewUserPicVisible(false);
self.viewUserPic(Consts.DataImages.UserDotPic);
if ('' !== sPic)
{
self.viewUserPicVisible(true);
self.viewUserPic(sPic);
}
}
}
});
}
}, this);

View file

@ -16,7 +16,7 @@ function PopupsAdvancedSearchViewModel()
this.text = ko.observable('');
this.selectedDateValue = ko.observable(-1);
this.hasAttachments = ko.observable(false);
this.hasAttachment = ko.observable(false);
this.starred = ko.observable(false);
this.unseen = ko.observable(false);
@ -54,6 +54,7 @@ PopupsAdvancedSearchViewModel.prototype.buildSearchString = function ()
sTo = Utils.trim(this.to()),
sSubject = Utils.trim(this.subject()),
sText = Utils.trim(this.text()),
aIs = [],
aHas = []
;
@ -72,19 +73,19 @@ PopupsAdvancedSearchViewModel.prototype.buildSearchString = function ()
aResult.push('subject:' + this.buildSearchStringValue(sSubject));
}
if (this.hasAttachments())
if (this.hasAttachment())
{
aHas.push('attachments');
aHas.push('attachment');
}
if (this.unseen())
{
aHas.push('unseen');
aIs.push('unseen');
}
if (this.starred())
{
aHas.push('flag');
aIs.push('flagged');
}
if (0 < aHas.length)
@ -92,6 +93,11 @@ PopupsAdvancedSearchViewModel.prototype.buildSearchString = function ()
aResult.push('has:' + aHas.join(','));
}
if (0 < aIs.length)
{
aResult.push('is:' + aIs.join(','));
}
if (-1 < this.selectedDateValue())
{
aResult.push('date:' + moment().subtract('days', this.selectedDateValue()).format('YYYY.MM.DD') + '/');
@ -113,7 +119,7 @@ PopupsAdvancedSearchViewModel.prototype.clearPopup = function ()
this.text('');
this.selectedDateValue(-1);
this.hasAttachments(false);
this.hasAttachment(false);
this.starred(false);
this.unseen(false);

View file

@ -70,7 +70,7 @@ function PopupsComposeOpenPgpViewModel()
var aKeys = oData.findPublicKeysByEmail(sEmail);
if (0 === aKeys.length && bResult)
{
this.notification(Utils.i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND_FOR', {
self.notification(Utils.i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND_FOR', {
'EMAIL': sEmail
}));

View file

@ -10,17 +10,6 @@ function PopupsContactsViewModel()
var
self = this,
oT = Enums.ContactPropertyType,
aNameTypes = [oT.FirstName, oT.LastName],
aEmailTypes = [oT.EmailPersonal, oT.EmailBussines, oT.EmailOther],
aPhonesTypes = [
oT.PhonePersonal, oT.PhoneBussines, oT.PhoneOther,
oT.MobilePersonal, oT.MobileBussines, oT.MobileOther,
oT.FaxPesonal, oT.FaxBussines, oT.FaxOther
],
aOtherTypes = [
oT.Facebook, oT.Skype, oT.GitHub
],
fFastClearEmptyListHelper = function (aList) {
if (aList && 0 < aList.length) {
self.viewProperties.removeAll(aList);
@ -28,18 +17,19 @@ function PopupsContactsViewModel()
}
;
this.enableContactsSync = RL.data().enableContactsSync;
this.search = ko.observable('');
this.contactsCount = ko.observable(0);
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.currentContact = ko.observable(null);
this.importUploaderButton = ko.observable(null);
this.contactsSharingIsAllowed = !!RL.settingsGet('ContactsSharingIsAllowed');
this.contactsPage = ko.observable(1);
this.contactsPageCount = ko.computed(function () {
var iPage = Math.ceil(this.contactsCount() / Consts.Defaults.ContactsPerPage);
@ -52,33 +42,19 @@ function PopupsContactsViewModel()
this.viewClearSearch = ko.observable(false);
this.viewID = ko.observable('');
this.viewIDStr = ko.observable('');
this.viewReadOnly = ko.observable(false);
this.viewScopeType = ko.observable(Enums.ContactScopeType.Default);
this.viewProperties = ko.observableArray([]);
this.viewSaveTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.viewPropertiesNames = this.viewProperties.filter(function(oProperty) {
return -1 < Utils.inArray(oProperty.type(), aNameTypes);
return -1 < Utils.inArray(oProperty.type(), [Enums.ContactPropertyType.FirstName, Enums.ContactPropertyType.LastName]);
});
this.viewPropertiesEmails = this.viewProperties.filter(function(oProperty) {
return -1 < Utils.inArray(oProperty.type(), aEmailTypes);
return Enums.ContactPropertyType.Email === oProperty.type();
});
this.shareIcon = ko.computed(function() {
return Enums.ContactScopeType.ShareAll === this.viewScopeType() ? 'icon-earth' : 'icon-share';
}, this);
this.shareToNone = ko.computed(function() {
return Enums.ContactScopeType.ShareAll !== this.viewScopeType();
}, this);
this.shareToAll = ko.computed(function() {
return Enums.ContactScopeType.ShareAll === this.viewScopeType();
}, this);
this.viewHasNonEmptyRequaredProperties = ko.computed(function() {
var
@ -93,11 +69,7 @@ function PopupsContactsViewModel()
}, this);
this.viewPropertiesPhones = this.viewProperties.filter(function(oProperty) {
return -1 < Utils.inArray(oProperty.type(), aPhonesTypes);
});
this.viewPropertiesOther = this.viewProperties.filter(function(oProperty) {
return -1 < Utils.inArray(oProperty.type(), aOtherTypes);
return Enums.ContactPropertyType.Phone === oProperty.type();
});
this.viewPropertiesEmailsNonEmpty = this.viewPropertiesNames.filter(function(oProperty) {
@ -167,6 +139,10 @@ function PopupsContactsViewModel()
this.selector.on('onItemSelect', _.bind(function (oContact) {
this.populateViewContact(oContact ? oContact : null);
if (!oContact)
{
this.emptySelection(true);
}
}, this));
this.selector.on('onItemGetUid', function (oContact) {
@ -236,7 +212,7 @@ function PopupsContactsViewModel()
_.each(this.viewProperties(), function (oItem) {
if (oItem.type() && '' !== Utils.trim(oItem.value()))
{
aProperties.push([oItem.type(), oItem.value()]);
aProperties.push([oItem.type(), oItem.value(), oItem.typeStr()]);
}
});
@ -253,11 +229,6 @@ function PopupsContactsViewModel()
self.viewID(Utils.pInt(oData.Result.ResultID));
}
if ('' === self.viewIDStr())
{
self.viewIDStr(Utils.pString(oData.Result.ResultIDStr));
}
self.reloadContactList();
bRes = true;
}
@ -275,7 +246,7 @@ function PopupsContactsViewModel()
}, 1000);
}
}, sRequestUid, this.viewID(), this.viewIDStr(), this.viewScopeType(), aProperties);
}, sRequestUid, this.viewID(), aProperties);
}, function () {
var
@ -285,13 +256,43 @@ function PopupsContactsViewModel()
return !this.viewSaving() && bV && !bReadOnly;
});
this.syncCommand = Utils.createCommand(this, function () {
if (this.contacts.syncing())
{
return false;
}
var self = this;
this.contacts.syncing(true);
RL.remote().contactsSync(function (sResult, oData) {
self.contacts.syncing(false);
if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result)
{
window.alert(Utils.getNotification(
oData && oData.ErrorCode ? oData.ErrorCode : Enums.Notification.ContactsSyncError));
}
self.reloadContactList(true);
});
}, function () {
return !this.contacts.syncing() && !this.contacts.importing();
});
this.bDropPageAfterDelete = false;
this.watchDirty = ko.observable(false);
this.watchHash = ko.observable(false);
this.viewHash = ko.computed(function () {
return '' + self.viewScopeType() + ' - ' + _.map(self.viewProperties(), function (oItem) {
return '' + _.map(self.viewProperties(), function (oItem) {
return oItem.value();
}).join('');
});
@ -312,31 +313,21 @@ function PopupsContactsViewModel()
Utils.extendAsViewModel('PopupsContactsViewModel', PopupsContactsViewModel);
PopupsContactsViewModel.prototype.setShareToNone = function ()
PopupsContactsViewModel.prototype.addNewProperty = function (sType, sTypeStr)
{
this.viewScopeType(Enums.ContactScopeType.Default);
};
PopupsContactsViewModel.prototype.setShareToAll = function ()
{
this.viewScopeType(Enums.ContactScopeType.ShareAll);
};
PopupsContactsViewModel.prototype.addNewProperty = function (sType)
{
var oItem = new ContactPropertyModel(sType, '');
var oItem = new ContactPropertyModel(sType, sTypeStr || '', '');
oItem.focused(true);
this.viewProperties.push(oItem);
};
PopupsContactsViewModel.prototype.addNewEmail = function ()
{
this.addNewProperty(Enums.ContactPropertyType.EmailPersonal);
this.addNewProperty(Enums.ContactPropertyType.Email, 'Home');
};
PopupsContactsViewModel.prototype.addNewPhone = function ()
{
this.addNewProperty(Enums.ContactPropertyType.MobilePersonal);
this.addNewProperty(Enums.ContactPropertyType.Phone, 'Mobile');
};
PopupsContactsViewModel.prototype.initUploader = function ()
@ -463,7 +454,6 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
{
var
sId = '',
sIdStr = '',
sLastName = '',
sFirstName = '',
aList = []
@ -473,13 +463,10 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
this.emptySelection(false);
this.viewReadOnly(false);
this.viewScopeType(Enums.ContactScopeType.Default);
if (oContact)
{
sId = oContact.idContact;
sIdStr = oContact.idContactStr;
if (Utils.isNonEmptyArray(oContact.properties))
{
_.each(oContact.properties, function (aProperty) {
@ -493,23 +480,21 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
{
sFirstName = aProperty[1];
}
else if (-1 === Utils.inArray(aProperty[0], [Enums.ContactPropertyType.FullName]))
else
{
aList.push(new ContactPropertyModel(aProperty[0], aProperty[1]));
aList.push(new ContactPropertyModel(aProperty[0], aProperty[2] || '', aProperty[1]));
}
}
});
}
this.viewReadOnly(!!oContact.readOnly);
this.viewScopeType(oContact.scopeType);
}
aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.LastName, sLastName, !oContact, 'CONTACTS/PLACEHOLDER_ENTER_LAST_NAME'));
aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.FirstName, sFirstName, false, 'CONTACTS/PLACEHOLDER_ENTER_FIRST_NAME'));
aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.LastName, '', sLastName, !oContact, 'CONTACTS/PLACEHOLDER_ENTER_LAST_NAME'));
aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.FirstName, '', sFirstName, false, 'CONTACTS/PLACEHOLDER_ENTER_FIRST_NAME'));
this.viewID(sId);
this.viewIDStr(sIdStr);
this.viewProperties([]);
this.viewProperties(aList);
@ -548,7 +533,7 @@ PopupsContactsViewModel.prototype.reloadContactList = function (bDropPagePositio
{
aList = _.map(oData.Result.List, function (oItem) {
var oContact = new ContactModel();
return oContact.parse(oItem, self.contactsSharingIsAllowed) ? oContact : null;
return oContact.parse(oItem) ? oContact : null;
});
aList = _.compact(aList);
@ -564,11 +549,6 @@ PopupsContactsViewModel.prototype.reloadContactList = function (bDropPagePositio
self.viewClearSearch('' !== self.search());
self.contacts.loading(false);
// if ('' !== self.viewID() && !self.currentContact() && self.contacts.setSelectedByUid)
// {
// self.contacts.setSelectedByUid('' + self.viewID());
// }
}, iOffset, Consts.Defaults.ContactsPerPage, this.search());
};

View file

@ -2,7 +2,7 @@
"name": "RainLoop",
"title": "RainLoop Webmail",
"version": "1.6.5",
"release": "903",
"release": "908",
"description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net",
"main": "Gruntfile.js",

View file

@ -18,7 +18,7 @@ if (!\defined('RAINLOOP_APP_ROOT_PATH'))
if (!RAINLOOP_MB_SUPPORTED && !defined('RL_MB_FIXED'))
{
\define('RL_MB_FIXED', true);
include_once RAINLOOP_APP_LIBRARIES_PATH.'RainLoop/SabreDAV/MbStringFix.php';
include_once RAINLOOP_APP_LIBRARIES_PATH.'RainLoop/Common/MbStringFix.php';
}
return include RAINLOOP_APP_LIBRARIES_PATH.'Sabre/'.\str_replace('\\', '/', \substr($sClassName, 6)).'.php';

View file

@ -77,11 +77,6 @@ class Actions
*/
private $oAddressBookProvider;
/**
* @var \RainLoop\Providers\PersonalAddressBook
*/
private $oPersonalAddressBookProvider;
/**
* @var \RainLoop\Providers\Suggestions
*/
@ -127,7 +122,6 @@ class Actions
$this->oSettingsProvider = null;
$this->oDomainProvider = null;
$this->oAddressBookProvider = null;
$this->oPersonalAddressBookProvider = null;
$this->oSuggestionsProvider = null;
$this->oChangePasswordProvider = null;
$this->oTwoFactorAuthProvider = null;
@ -242,26 +236,6 @@ class Actions
$oResult = new \RainLoop\Providers\Domain\DefaultDomain(APP_PRIVATE_DATA.'domains',
$this->Cacher());
break;
case 'personal-address-book':
// \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface
$sDsn = \trim($this->Config()->Get('contacts', 'pdo_dsn', ''));
$sUser = \trim($this->Config()->Get('contacts', 'pdo_user', ''));
$sPassword = (string) $this->Config()->Get('contacts', 'pdo_password', '');
$sDsnType = $this->ValidateContactPdoType(\trim($this->Config()->Get('contacts', 'type', 'sqlite')));
if ('sqlite' === $sDsnType)
{
$oResult = new \RainLoop\Providers\PersonalAddressBook\PdoPersonalAddressBook(
'sqlite:'.APP_PRIVATE_DATA.'PersonalAddressBook.sqlite', '', '', 'sqlite');
}
else
{
$oResult = new \RainLoop\Providers\PersonalAddressBook\PdoPersonalAddressBook($sDsn, $sUser, $sPassword, $sDsnType);
}
$oResult->SetLogger($this->Logger());
break;
case 'address-book':
// \RainLoop\Providers\AddressBook\AddressBookInterface
@ -625,32 +599,6 @@ class Actions
return $this->oAddressBookProvider;
}
/**
* @param \RainLoop\Account $oAccount = null
* @param bool $bForceEnable = false
*
* @return \RainLoop\Providers\PersonalAddressBook
*/
public function PersonalAddressBookProvider($oAccount = null, $bForceEnable = false)
{
if (null === $this->oPersonalAddressBookProvider)
{
$this->oPersonalAddressBookProvider = new \RainLoop\Providers\PersonalAddressBook(
$this->Config()->Get('contacts', 'enable', false) || $bForceEnable ? $this->fabrica('personal-address-book', $oAccount) : null);
$this->oPersonalAddressBookProvider->SetLogger($this->Logger());
$this->oPersonalAddressBookProvider->ConsiderShare(
!!$this->Config()->Get('contacts', 'allow_sharing', false));
}
else if ($oAccount && $this->oPersonalAddressBookProvider->IsSupported())
{
$this->oPersonalAddressBookProvider->SetAccount($oAccount);
}
return $this->oPersonalAddressBookProvider;
}
/**
* @return \OAuth2\Client|null
*/
@ -1033,10 +981,10 @@ class Actions
'CustomLogoutLink' => $oConfig->Get('labs', 'custom_logout_link', ''),
'AllowAdditionalAccounts' => (bool) $oConfig->Get('webmail', 'allow_additional_accounts', true),
'AllowIdentities' => (bool) $oConfig->Get('webmail', 'allow_identities', true),
'DetermineUserLanguage' => (bool) $oConfig->Get('labs', 'determine_user_language', false),
'AllowPrefetch' => (bool) $oConfig->Get('labs', 'allow_prefetch', true),
'AllowCustomLogin' => (bool) $oConfig->Get('login', 'allow_custom_login', false),
'LoginDefaultDomain' => $oConfig->Get('login', 'default_domain', ''),
'DetermineUserLanguage' => (bool) $oConfig->Get('labs', 'determine_user_language', false),
'AllowThemes' => (bool) $oConfig->Get('webmail', 'allow_themes', true),
'AllowCustomTheme' => (bool) $oConfig->Get('webmail', 'allow_custom_theme', true),
'ChangePasswordIsAllowed' => false,
@ -1060,7 +1008,7 @@ class Actions
$oAccount = $this->getAccountFromToken(false);
if ($oAccount instanceof \RainLoop\Account)
{
$oPab = $this->PersonalAddressBookProvider($oAccount);
$oAddressBookProvider = $this->AddressBookProvider($oAccount);
$aResult['Auth'] = true;
$aResult['Email'] = $oAccount->Email();
@ -1069,59 +1017,27 @@ class Actions
$aResult['AccountHash'] = $oAccount->Hash();
$aResult['AccountSignMe'] = $oAccount->SignMe();
$aResult['ChangePasswordIsAllowed'] = $this->ChangePasswordProvider()->PasswordChangePossibility($oAccount);
$aResult['ContactsIsAllowed'] = $oPab->IsActive();
$aResult['ContactsSharingIsAllowed'] = $oPab->IsSharingAllowed();
$aResult['ContactsIsAllowed'] = $oAddressBookProvider->IsActive();
$aResult['ContactsSharingIsAllowed'] = $oAddressBookProvider->IsSharingAllowed();
$aResult['ContactsSyncIsAllowed'] = (bool) $oConfig->Get('contacts', 'allow_sync', false);
$aResult['ContactsSyncServer'] = '';
$aResult['EnableContactsSync'] = false;
$aResult['ContactsSyncUrl'] = '';
$aResult['ContactsSyncUser'] = '';
$aResult['ContactsSyncPassword'] = '';
$aResult['ContactsSyncPabUrl'] = '';
if ($aResult['ContactsSyncIsAllowed'])
if ($aResult['ContactsIsAllowed'] && $aResult['ContactsSyncIsAllowed'])
{
$sDavDomain = (string) $oConfig->Get('labs', 'sync_dav_domain', '');
if (empty($sDavDomain))
$mData = $this->getContactsSyncData($oAccount);
if (\is_array($mData))
{
$aResult['ContactsSyncServer'] = $this->Http()->GetHost(false, true, true);
}
else
{
$sDavDomain = \rtrim($sDavDomain, '/\\ ');
$sDavDomainWithoutScheme = \preg_replace('/https?:\/\//i', '', \trim($sDavDomain));
$aResult['ContactsSyncServer'] = $sDavDomainWithoutScheme;
}
$aResult['ContactsSyncUser'] = $oAccount->ParentEmailHelper();
try
{
$aResult['ContactsSyncPassword'] = $oPab->GetUserHashByEmail($aResult['ContactsSyncUser'], true);
}
catch (\Exception $oException)
{
$this->Logger()->WriteException($oException);
}
if (empty($sDavDomain))
{
$sUrl = \rtrim(\trim($this->Http()->GetScheme().'://'.$this->Http()->GetHost(true, false).$this->Http()->GetPath()), '/\\');
$sUrl = \preg_replace('/index\.php(.*)$/i', '', $sUrl);
$aResult['ContactsSyncPabUrl'] = $sUrl.'/index.php/dav/';
}
else
{
$aResult['ContactsSyncPabUrl'] = \preg_match('/^https?:\/\//i', $sDavDomain) ? $sDavDomain : 'http://'.$sDavDomain;
}
if (!empty($aResult['ContactsSyncPabUrl']))
{
$aResult['ContactsSyncPabUrl'] .= '/addressbooks/'.$oAccount->ParentEmailHelper().'/default/';
$aResult['EnableContactsSync'] = isset($mData['Enable']) ? !!$mData['Enable'] : false;
$aResult['ContactsSyncUrl'] = isset($mData['Url']) ? \trim($mData['Url']) : '';
$aResult['ContactsSyncUser'] = isset($mData['User']) ? \trim($mData['User']) : '';
$aResult['ContactsSyncPassword'] = APP_DUMMY;
}
}
if ($aResult['AccountSignMe'])
{
$sToken = \RainLoop\Utils::GetCookie(self::AUTH_MAILTO_TOKEN_KEY, null);
@ -2273,7 +2189,7 @@ class Actions
return $self->ValidateContactPdoType($sType);
});
$sTestMessage = $this->PersonalAddressBookProvider(null, true)->Test();
$sTestMessage = $this->AddressBookProvider(null, true)->Test();
return $this->DefaultResponse(__FUNCTION__, array(
'Result' => '' === $sTestMessage,
'Message' => \MailSo\Base\Utils::Utf8Clear($sTestMessage, '?')
@ -4571,7 +4487,7 @@ class Actions
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantSendMessage);
}
if ($oMessage && $this->PersonalAddressBookProvider($oAccount)->IsActive())
if ($oMessage && $this->AddressBookProvider($oAccount)->IsActive())
{
$aArrayToFrec = array();
$oToCollection = $oMessage->GetTo();
@ -4588,7 +4504,7 @@ class Actions
{
$oSettings = $this->SettingsProvider()->Load($oAccount);
$this->PersonalAddressBookProvider($oAccount)->IncFrec(
$this->AddressBookProvider($oAccount)->IncFrec(
$oAccount->ParentEmailHelper(), \array_values($aArrayToFrec), !!$oSettings->GetConf('ContactsAutosave', true));
}
}
@ -4684,6 +4600,95 @@ class Actions
return $this->DefaultResponse(__FUNCTION__, $aQuota);
}
private function getContactsSyncData($oAccount)
{
$mResult = null;
$sData = $this->StorageProvider()->Get($oAccount->ParentEmailHelper(),
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'contacts_sync'
);
if (!empty($sData))
{
$mData = \RainLoop\Utils::DecodeKeyValues($sData);
if (\is_array($mData))
{
$mResult = array(
'Enable' => isset($mData['Enable']) ? !!$mData['Enable'] : false,
'Url' => isset($mData['Url']) ? \trim($mData['Url']) : '',
'User' => isset($mData['User']) ? \trim($mData['User']) : '',
'Password' => isset($mData['Password']) ? $mData['Password'] : ''
);
}
}
return $mResult;
}
/**
* @return array
*/
public function DoSaveContactsSyncData()
{
$oAccount = $this->getAccountFromToken();
$oAddressBookProvider = $this->AddressBookProvider($oAccount);
if (!$oAddressBookProvider || !$oAddressBookProvider->IsActive())
{
return $this->FalseResponse(__FUNCTION__);
}
$bEnabled = '1' === (string) $this->GetActionParam('Enable', '0');
$sUrl = $this->GetActionParam('Url', '');
$sUser = $this->GetActionParam('User', '');
$sPassword = $this->GetActionParam('Password', '');
$mData = $this->getContactsSyncData($oAccount);
$bResult = $this->StorageProvider()->Put($oAccount->ParentEmailHelper(),
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'contacts_sync',
\RainLoop\Utils::EncodeKeyValues(array(
'Enable' => $bEnabled,
'User' => $sUser,
'Password' => APP_DUMMY === $sPassword && isset($mData['Password']) ?
$mData['Password'] : (APP_DUMMY === $sPassword ? '' : $sPassword),
'Url' => $sUrl
))
);
return $this->DefaultResponse(__FUNCTION__, $bResult);
}
/**
* @return array
*/
public function DoContactsSync()
{
$bResult = false;
$oAccount = $this->getAccountFromToken();
$oAddressBookProvider = $this->AddressBookProvider($oAccount);
if ($oAddressBookProvider && $oAddressBookProvider->IsActive())
{
$mData = $this->getContactsSyncData($oAccount);
if (\is_array($mData) && isset($mData['Enable'], $mData['User'], $mData['Password'], $mData['Url']) && $mData['Enable'])
{
$bResult = $oAddressBookProvider->Sync(
$oAccount->ParentEmailHelper(),
$mData['Url'], $mData['User'], $mData['Password']);
}
}
if (!$bResult)
{
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError);
}
return $this->TrueResponse(__FUNCTION__);
}
private function getTwoFactorInfo($sEmail, $bRemoveSecret = false)
{
$mData = null;
@ -4950,10 +4955,10 @@ class Actions
$iLimit = 0 > $iLimit ? 20 : $iLimit;
$iResultCount = 0;
if ($this->PersonalAddressBookProvider($oAccount)->IsActive())
if ($this->AddressBookProvider($oAccount)->IsActive())
{
$iResultCount = 0;
$mResult = $this->PersonalAddressBookProvider($oAccount)->GetContacts($oAccount->ParentEmailHelper(),
$mResult = $this->AddressBookProvider($oAccount)->GetContacts($oAccount->ParentEmailHelper(),
$iOffset, $iLimit, $sSearch, $iResultCount);
}
@ -4980,9 +4985,9 @@ class Actions
});
$bResult = false;
if (0 < \count($aFilteredUids) && $this->PersonalAddressBookProvider($oAccount)->IsActive())
if (0 < \count($aFilteredUids) && $this->AddressBookProvider($oAccount)->IsActive())
{
$bResult = $this->PersonalAddressBookProvider($oAccount)->DeleteContacts($oAccount->ParentEmailHelper(), $aFilteredUids);
$bResult = $this->AddressBookProvider($oAccount)->DeleteContacts($oAccount->ParentEmailHelper(), $aFilteredUids);
}
return $this->DefaultResponse(__FUNCTION__, $bResult);
@ -4997,68 +5002,55 @@ class Actions
$bResult = false;
$oPab = $this->PersonalAddressBookProvider($oAccount);
$oAddressBookProvider = $this->AddressBookProvider($oAccount);
$sRequestUid = \trim($this->GetActionParam('RequestUid', ''));
if ($oPab && $oPab->IsActive() && 0 < \strlen($sRequestUid))
if ($oAddressBookProvider && $oAddressBookProvider->IsActive() && 0 < \strlen($sRequestUid))
{
$sUid = \trim($this->GetActionParam('Uid', ''));
$sUidStr = \trim($this->GetActionParam('UidStr', ''));
$iScopeType = (int) $this->GetActionParam('ScopeType', null);
if (!in_array($iScopeType, array(
\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_,
\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::SHARE_ALL)))
{
$iScopeType = \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_;
}
$oContact = null;
if (0 < \strlen($sUid))
{
$oContact = $oPab->GetContactByID($oAccount->ParentEmailHelper(), $sUid);
$oContact = $oAddressBookProvider->GetContactByID($oAccount->ParentEmailHelper(), $sUid);
}
if (!$oContact)
{
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
$oContact = new \RainLoop\Providers\AddressBook\Classes\Contact();
if (0 < \strlen($sUid))
{
$oContact->IdContact = $sUid;
}
}
if (0 < \strlen($sUidStr))
{
$oContact->IdContactStr = $sUidStr;
}
$oContact->ScopeType = $iScopeType;
$oContact->Properties = array();
$aProperties = $this->GetActionParam('Properties', array());
if (\is_array($aProperties))
{
foreach ($aProperties as $aItem)
{
if ($aItem && isset($aItem[0], $aItem[1]) &&
\is_numeric($aItem[0]))
if ($aItem && isset($aItem[0], $aItem[1]) && \is_numeric($aItem[0]))
{
$oProp = new \RainLoop\Providers\PersonalAddressBook\Classes\Property();
$oProp = new \RainLoop\Providers\AddressBook\Classes\Property();
$oProp->Type = (int) $aItem[0];
$oProp->Value = $aItem[1];
$oProp->ScopeType = $iScopeType;
$oProp->TypeStr = empty($aItem[2]) ? '': $aItem[2];
$oContact->Properties[] = $oProp;
}
}
}
$bResult = $oPab->ContactSave($oAccount->ParentEmailHelper(), $oContact);
if (!empty($oContact->Etag))
{
$oContact->Etag = \md5($oContact->ToVCard());
}
$bResult = $oAddressBookProvider->ContactSave($oAccount->ParentEmailHelper(), $oContact);
}
return $this->DefaultResponse(__FUNCTION__, array(
'RequestUid' => $sRequestUid,
'ResultID' => $bResult ? $oContact->IdContact : '',
'ResultIDStr' => $bResult ? $oContact->IdContactStr : '',
'Result' => $bResult
));
}
@ -5087,11 +5079,11 @@ class Actions
if ($iLimit > \count($aResult) && 0 < \strlen($sQuery))
{
// Personal Address Book
$oPab = $this->PersonalAddressBookProvider($oAccount);
if ($oPab && $oPab->IsActive())
// Address Book
$oAddressBookProvider = $this->AddressBookProvider($oAccount);
if ($oAddressBookProvider && $oAddressBookProvider->IsActive())
{
$aSuggestions = $oPab->GetSuggestions($oAccount->ParentEmailHelper(), $sQuery, $iLimit);
$aSuggestions = $oAddressBookProvider->GetSuggestions($oAccount->ParentEmailHelper(), $sQuery, $iLimit);
if (0 === \count($aResult))
{
$aResult = $aSuggestions;
@ -5669,8 +5661,8 @@ class Actions
if ($oAccount && \is_resource($rFile))
{
$oPab = $this->PersonalAddressBookProvider($oAccount);
if ($oPab && $oPab->IsActive())
$oAddressBookProvider = $this->AddressBookProvider($oAccount);
if ($oAddressBookProvider && $oAddressBookProvider->IsActive())
{
$sDelimiter = ((int) \strpos($sFileStart, ',') > (int) \strpos($sFileStart, ';')) ? ',' : ';';
@ -5706,7 +5698,7 @@ class Actions
if (\is_array($aData) && 0 < \count($aData))
{
$this->Logger()->Write('Import contacts from csv');
$iCount = $oPab->ImportCsvArray($oAccount->ParentEmailHelper(), $aData);
$iCount = $oAddressBookProvider->ImportCsvArray($oAccount->ParentEmailHelper(), $aData);
}
}
}
@ -5726,8 +5718,8 @@ class Actions
$iCount = 0;
if ($oAccount && \is_resource($rFile))
{
$oPab = $this->PersonalAddressBookProvider($oAccount);
if ($oPab && $oPab->IsActive())
$oAddressBookProvider = $this->AddressBookProvider($oAccount);
if ($oAddressBookProvider && $oAddressBookProvider->IsActive())
{
$sFile = \stream_get_contents($rFile);
if (\is_resource($rFile))
@ -5738,7 +5730,7 @@ class Actions
if (is_string($sFile) && 5 < \strlen($sFile))
{
$this->Logger()->Write('Import contacts from vcf');
$iCount = $oPab->ImportVcfFile($oAccount->ParentEmailHelper(), $sFile);
$iCount = $oAddressBookProvider->ImportVcfFile($oAccount->ParentEmailHelper(), $sFile);
}
}
}
@ -5762,8 +5754,8 @@ class Actions
if ($oAccount)
{
$oPab = $this->PersonalAddressBookProvider($oAccount);
if ($oPab)
$oAddressBookProvider = $this->AddressBookProvider($oAccount);
if ($oAddressBookProvider && $oAddressBookProvider->IsActive())
{
$iError = UPLOAD_ERR_OK;
$_FILES = isset($_FILES) ? $_FILES : null;
@ -6949,28 +6941,25 @@ class Actions
'Email' => \MailSo\Base\Utils::Utf8Clear($mResponse->GetEmail())
));
}
else if ('RainLoop\Providers\PersonalAddressBook\Classes\Contact' === $sClassName)
else if ('RainLoop\Providers\AddressBook\Classes\Contact' === $sClassName)
{
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array(
/* @var $mResponse \RainLoop\Providers\PersonalAddressBook\Classes\Contact */
/* @var $mResponse \RainLoop\Providers\AddressBook\Classes\Contact */
'IdContact' => $mResponse->IdContact,
'IdContactStr' => $mResponse->IdContactStr,
'Display' => \MailSo\Base\Utils::Utf8Clear($mResponse->Display),
'ReadOnly' => $mResponse->ReadOnly,
'ScopeType' => $mResponse->ScopeType,
'IdPropertyFromSearch' => $mResponse->IdPropertyFromSearch,
'Properties' => $this->responseObject($mResponse->Properties, $sParent, $aParameters)
));
}
else if ('RainLoop\Providers\PersonalAddressBook\Classes\Property' === $sClassName)
else if ('RainLoop\Providers\AddressBook\Classes\Property' === $sClassName)
{
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array(
/* @var $mResponse \RainLoop\Providers\PersonalAddressBook\Classes\Property */
/* @var $mResponse \RainLoop\Providers\AddressBook\Classes\Property */
'IdProperty' => $mResponse->IdProperty,
'Type' => $mResponse->Type,
'TypeCustom' => $mResponse->TypeCustom,
'Value' => \MailSo\Base\Utils::Utf8Clear($mResponse->Value),
'ValueCustom' => \MailSo\Base\Utils::Utf8Clear($mResponse->ValueCustom)
'TypeStr' => $mResponse->TypeStr,
'Value' => \MailSo\Base\Utils::Utf8Clear($mResponse->Value)
));
}
else if ('MailSo\Mail\Attachment' === $sClassName)

View file

@ -14,6 +14,11 @@ abstract class PdoAbstract
*/
protected $bExplain = false;
/**
* @var int
*/
protected $iResetTimer = 0;
/**
* @var \MailSo\Log\Logger
*/
@ -114,13 +119,21 @@ abstract class PdoAbstract
}
/**
* @param string $sName = null
* @param string $sTabelName = null
* @param string $sColumnName = null
*
* @return string
*/
protected function lastInsertId($sName = null)
protected function lastInsertId($sTabelName = null, $sColumnName = null)
{
return $this->getPDO()->lastInsertId($sName);
$mName = null;
if ('pgsql' === $this->sDbType &&
null !== $sTabelName && $sColumnName !== null)
{
$mName = \strtolower($sTabelName.'_'.$sColumnName.'_seq');
}
return null === $mName ? $this->getPDO()->lastInsertId() : $this->getPDO()->lastInsertId($mName);
}
/**

View file

@ -24,6 +24,8 @@ class Notifications
const NewPasswordWeak = 133;
const NewPasswordForbidden = 134;
const ContactsSyncError = 140;
const CantGetMessageList = 201;
const CantGetMessage = 202;
const CantDeleteMessage = 203;
@ -93,6 +95,8 @@ class Notifications
self::NewPasswordShort => 'NewPasswordShort',
self::NewPasswordWeak => 'NewPasswordWeak',
self::NewPasswordForbidden => 'NewPasswordForbidden',
self::ContactsSyncError => 'ContactsSyncError',
self::CantGetMessageList => 'CantGetMessageList',
self::CantGetMessage => 'CantGetMessage',

View file

@ -53,6 +53,28 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
$this->oDriver->IsSupported();
}
/**
* @return bool
*/
public function IsSharingAllowed()
{
return $this->oDriver instanceof \RainLoop\Providers\PersonalAddressBook\AddressBookInterface &&
$this->oDriver->IsSharingAllowed();
}
/**
* @param string $sEmail
* @param string $sUrl
* @param string $sUser
* @param string $sPassword
*
* @return bool
*/
public function Sync($sEmail, $sUrl, $sUser, $sPassword)
{
return $this->IsActive() ? $this->oDriver->Sync($sEmail, $sUrl, $sUser, $sPassword) : false;
}
/**
* @param string $sEmail
* @param \RainLoop\Providers\AddressBook\Classes\Contact $oContact
@ -140,37 +162,64 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
{
$aMap = array(
'Title' => PropertyType::FULLNAME,
'First Name' => PropertyType::FIRST_NAME,
'Middle Name' => PropertyType::MIDDLE_NAME,
'Last Name' => PropertyType::LAST_NAME,
'Name' => PropertyType::FULLNAME,
'FullName' => PropertyType::FULLNAME,
'DisplayName' => PropertyType::FULLNAME,
'First' => PropertyType::FIRST_NAME,
'FirstName' => PropertyType::FIRST_NAME,
'Middle' => PropertyType::MIDDLE_NAME,
'MiddleName' => PropertyType::MIDDLE_NAME,
'Last' => PropertyType::LAST_NAME,
'LastName' => PropertyType::LAST_NAME,
'Suffix' => PropertyType::NAME_SUFFIX,
'Business Fax' => PropertyType::FAX_BUSSINES,
'Business Phone' => PropertyType::PHONE_BUSSINES,
'Business Phone 2' => PropertyType::PHONE_BUSSINES,
'Company Main Phone' => PropertyType::PHONE_BUSSINES,
'Home Fax' => PropertyType::FAX_PERSONAL,
'Home Phone' => PropertyType::PHONE_PERSONAL,
'Home Phone 2' => PropertyType::PHONE_PERSONAL,
'Mobile Phone' => PropertyType::MOBILE_PERSONAL,
'Other Fax' => PropertyType::FAX_OTHER,
'Other Phone' => PropertyType::PHONE_OTHER,
// 'Primary Phone' => PropertyType::PHONE_PERSONAL,
'E-mail Address' => PropertyType::EMAIl_PERSONAL,
'E-mail 2 Address' => PropertyType::EMAIl_OTHER,
'E-mail 3 Address' => PropertyType::EMAIl_OTHER,
'E-mail Display Name' => PropertyType::FULLNAME,
'E-mail 2 Display Name' => PropertyType::FULLNAME,
'E-mail 3 Display Name' => PropertyType::FULLNAME,
'NickName' => PropertyType::NICK_NAME,
'BusinessFax' => array(PropertyType::PHONE, 'Work,Fax'),
'BusinessFax2' => array(PropertyType::PHONE, 'Work,Fax'),
'BusinessPhone' => array(PropertyType::PHONE, 'Work'),
'BusinessPhone2' => array(PropertyType::PHONE, 'Work'),
'BusinessPhone3' => array(PropertyType::PHONE, 'Work'),
'CompanyPhone' => array(PropertyType::PHONE, 'Work'),
'CompanyMainPhone' => array(PropertyType::PHONE, 'Work'),
'HomeFax' => array(PropertyType::PHONE, 'Home,Fax'),
'HomeFax2' => array(PropertyType::PHONE, 'Home,Fax'),
'HomePhone' => array(PropertyType::PHONE, 'Home'),
'HomePhone2' => array(PropertyType::PHONE, 'Home'),
'HomePhone3' => array(PropertyType::PHONE, 'Home'),
'Mobile' => array(PropertyType::PHONE, 'Mobile'),
'MobilePhone' => array(PropertyType::PHONE, 'Mobile'),
'BusinessMobile' => array(PropertyType::PHONE, 'Work,Mobile'),
'BusinessMobilePhone' => array(PropertyType::PHONE, 'Work,Mobile'),
'OtherFax' => array(PropertyType::PHONE, 'Other,Fax'),
'OtherPhone' => array(PropertyType::PHONE, 'Other'),
'PrimaryPhone' => array(PropertyType::PHONE, 'Pref,Home'),
'Email' => array(PropertyType::EMAIl, 'Home'),
'Email2' => array(PropertyType::EMAIl, 'Home'),
'Email3' => array(PropertyType::EMAIl, 'Home'),
'EmailAddress' => array(PropertyType::EMAIl, 'Home'),
'Email2Address' => array(PropertyType::EMAIl, 'Home'),
'Email3Address' => array(PropertyType::EMAIl, 'Home'),
'OtherEmail' => array(PropertyType::EMAIl, 'Other'),
'BusinessEmail' => array(PropertyType::EMAIl, 'Work'),
'BusinessEmail2' => array(PropertyType::EMAIl, 'Work'),
'BusinessEmail3' => array(PropertyType::EMAIl, 'Work'),
'PersonalEmail' => array(PropertyType::EMAIl, 'Home'),
'PersonalEmail2' => array(PropertyType::EMAIl, 'Home'),
'PersonalEmail3' => array(PropertyType::EMAIl, 'Home'),
'Notes' => PropertyType::NOTE,
'Web Page' => PropertyType::WEB_PAGE_PERSONAL,
'WebPage' => PropertyType::WEB_PAGE_PERSONAL,
'Web' => PropertyType::WEB_PAGE,
'BusinessWeb' => array(PropertyType::WEB_PAGE, 'Work'),
'WebPage' => PropertyType::WEB_PAGE,
'BusinessWebPage' => array(PropertyType::WEB_PAGE, 'Work'),
'WebSite' => PropertyType::WEB_PAGE,
'BusinessWebSite' => array(PropertyType::WEB_PAGE, 'Work'),
'PersonalWebSite' => PropertyType::WEB_PAGE
);
$aMap = \array_change_key_case($aMap, CASE_LOWER);
}
$sCsvNameLower = \MailSo\Base\Utils::IsAscii($sCsvName) ? \strtolower($sCsvName) : '';
return isset($aMap[$sCsvNameLower]) ? $aMap[$sCsvNameLower] : PropertyType::UNKNOWN;
$sCsvNameLower = \MailSo\Base\Utils::IsAscii($sCsvName) ? \preg_replace('/[\s\-]+/', '', \strtolower($sCsvName)) : '';
return !empty($sCsvNameLower) && isset($aMap[$sCsvNameLower]) ? $aMap[$sCsvNameLower] : PropertyType::UNKNOWN;
}
/**
@ -182,11 +231,15 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
public function ImportCsvArray($sEmail, $aCsvData)
{
$iCount = 0;
$iResetTimer = 0;
if ($this->IsActive() && \is_array($aCsvData) && 0 < \count($aCsvData))
{
$oContact = new \RainLoop\Providers\AddressBook\Classes\Contact();
foreach ($aCsvData as $aItem)
{
\MailSo\Base\Utils::ResetTimeLimit($iResetTimer);
foreach ($aItem as $sItemName => $sItemValue)
{
$sItemName = \trim($sItemName);
@ -194,12 +247,15 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
if (!empty($sItemName) && !empty($sItemValue))
{
$iType = $this->csvNameToTypeConvertor($sItemName);
$mData = $this->csvNameToTypeConvertor($sItemName);
$iType = \is_array($mData) ? $mData[0] : $mData;
if (PropertyType::UNKNOWN !== $iType)
{
$oProp = new \RainLoop\Providers\AddressBook\Classes\Property();
$oProp->Type = $iType;
$oProp->Value = $sItemValue;
$oProp->TypeStr = \is_array($mData) && !empty($mData[1]) ? $mData[1] : '';
$oContact->Properties[] = $oProp;
}
@ -232,6 +288,8 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
public function ImportVcfFile($sEmail, $sVcfData)
{
$iCount = 0;
$iResetTimer = 0;
if ($this->IsActive() && \is_string($sVcfData))
{
$sVcfData = \trim($sVcfData);
@ -260,12 +318,15 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider
{
if ($oVCard instanceof \Sabre\VObject\Component\VCard)
{
\MailSo\Base\Utils::ResetTimeLimit($iResetTimer);
if (empty($oVCard->UID))
{
$oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID();
}
$oContact->ParseVCard($oVCard, $oVCard->serialize());
$oContact->PopulateByVCard($oVCard->serialize());
if (0 < \count($oContact->Properties))
{
if ($this->ContactSave($sEmail, $oContact))

View file

@ -47,7 +47,7 @@ class Contact
/**
* @var string
*/
public $Hash;
public $Etag;
public function __construct()
{
@ -64,7 +64,7 @@ class Contact
$this->Properties = array();
$this->ReadOnly = false;
$this->IdPropertyFromSearch = 0;
$this->Hash = '';
$this->Etag = '';
}
public function UpdateDependentValues()
@ -101,9 +101,8 @@ class Contact
{
$sFirstName = $oProperty->Value;
}
else if (\in_array($oProperty->Type, array(PropertyType::FULLNAME,
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER
else if (\in_array($oProperty->Type, array(
PropertyType::FULLNAME, PropertyType::PHONE
)))
{
$sOther = $oProperty->Value;
@ -134,20 +133,16 @@ class Contact
}
$this->Display = \trim($sDisplay);
$bNewFull = false;
if (!$oFullNameProperty)
if ($oFullNameProperty)
{
$oFullNameProperty = new \RainLoop\Providers\AddressBook\Classes\Property(PropertyType::FULLNAME, $this->Display);
$bNewFull = true;
$oFullNameProperty->Value = $this->Display;
$oFullNameProperty->UpdateDependentValues();
}
$oFullNameProperty->Value = $this->Display;
$oFullNameProperty->UpdateDependentValues();
if ($bNewFull)
if (!$oFullNameProperty)
{
$this->Properties[] = $oFullNameProperty;
$this->Properties[] = new \RainLoop\Providers\AddressBook\Classes\Property(PropertyType::FULLNAME, $this->Display);
}
}
@ -177,85 +172,189 @@ class Contact
}
/**
* @param \Sabre\VObject\Document $oVCard
* @return string
*/
public function ParseVCard($oVCard)
public function CardDavNameUri()
{
$bNew = empty($this->IdContact);
return $this->IdContactStr.'.vcf';
}
if (!$bNew)
/**
* @return string
*/
public function ToVCard($sPreVCard = '')
{
$this->UpdateDependentValues();
$oVCard = null;
if (0 < \strlen($sPreVCard))
{
$this->Properties = array();
try
{
$oVCard = \Sabre\VObject\Reader::read($sPreVCard);
}
catch (\Exception $oExc) {};
}
if (!$oVCard)
{
$oVCard = new \Sabre\VObject\Component\VCard();
}
$oVCard->VERSION = '3.0';
$oVCard->PRODID = '-//RainLoop//'.APP_VERSION.'//EN';
unset($oVCard->FN, $oVCard->EMAIL, $oVCard->TEL, $oVCard->URL);
$sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = '';
foreach ($this->Properties as /* @var $oProperty \RainLoop\Providers\AddressBook\Classes\Property */ &$oProperty)
{
if ($oProperty)
{
$sAddKey = '';
switch ($oProperty->Type)
{
case PropertyType::FULLNAME:
$oVCard->FN = $oProperty->Value;
break;
case PropertyType::NICK_NAME:
$oVCard->NICKNAME = $oProperty->Value;
break;
case PropertyType::FIRST_NAME:
$sFirstName = $oProperty->Value;
break;
case PropertyType::LAST_NAME:
$sLastName = $oProperty->Value;
break;
case PropertyType::MIDDLE_NAME:
$sMiddleName = $oProperty->Value;
break;
case PropertyType::NAME_SUFFIX:
$sSuffix = $oProperty->Value;
break;
case PropertyType::NAME_PREFIX:
$sPrefix = $oProperty->Value;
break;
case PropertyType::EMAIl:
if (empty($sAddKey))
{
$sAddKey = 'EMAIL';
}
case PropertyType::WEB_PAGE:
if (empty($sAddKey))
{
$sAddKey = 'URL';
}
case PropertyType::PHONE:
if (empty($sAddKey))
{
$sAddKey = 'TEL';
}
$aTypes = $oProperty->TypesAsArray();
$oVCard->add($sAddKey, $oProperty->Value, \is_array($aTypes) && 0 < \count($aTypes) ? array('TYPE' => $aTypes) : null);
break;
}
}
}
$oVCard->UID = $this->IdContactStr;
$oVCard->N = array($sLastName, $sFirstName, $sMiddleName, $sPrefix, $sSuffix);
$oVCard->REV = \gmdate('Ymd', $this->Changed).'T'.\gmdate('His', $this->Changed).'Z';
return (string) $oVCard->serialize();
}
/**
* @param mixed $oProp
* @param bool $bOldVersion
* @return string
*/
private function getPropertyValueHelper($oProp, $bOldVersion)
{
$sValue = \trim($oProp);
if ($bOldVersion && !isset($oProp->parameters['CHARSET']))
{
if (0 < \strlen($sValue))
{
$sEncValue = @\utf8_encode($sValue);
if (0 === \strlen($sEncValue))
{
$sEncValue = $sValue;
}
$sValue = $sEncValue;
}
}
return \MailSo\Base\Utils::Utf8Clear($sValue);
}
/**
* @param mixed $oProp
* @param bool $bOldVersion
* @return string
*/
private function addArrayPropertyHelper(&$aProperties, $oArrayProp, $iType)
{
foreach ($oArrayProp as $oProp)
{
$oTypes = $oProp ? $oProp['TYPE'] : null;
$aTypes = $oTypes ? $oTypes->getParts() : array();
$sValue = $oProp ? \trim($oProp->getValue()) : '';
if (0 < \strlen($sValue))
{
if (!$oTypes || $oTypes->has('PREF'))
{
\array_unshift($aProperties, new Property($iType, $sValue, \implode(',', $aTypes)));
}
else
{
\array_push($aProperties, new Property($iType, $sValue, \implode(',', $aTypes)));
}
}
}
}
public function PopulateByVCard($sVCard, $sEtag = '')
{
$this->Properties = array();
if (!empty($sEtag))
{
$this->Etag = $sEtag;
}
try
{
$oVCard = \Sabre\VObject\Reader::read($sVCard);
}
catch (\Exception $oExc) {};
$aProperties = array();
if ($oVCard)
{
$bOldVersion = empty($oVCard->VERSION) ? false :
$bOldVersion = empty($oVCard->VERSION) ? false :
\in_array((string) $oVCard->VERSION, array('2.1', '2.0', '1.0'));
$this->IdContactStr = !empty($oVCard->UID) ? (string) $oVCard->UID : \Sabre\DAV\UUIDUtil::getUUID();
$this->IdContactStr = $oVCard->UID ? (string) $oVCard->UID : \Sabre\DAV\UUIDUtil::getUUID();
if (isset($oVCard->FN) && '' !== \trim($oVCard->FN))
{
$sValue = \trim($oVCard->FN);
if ($bOldVersion && !isset($oVCard->FN->parameters['CHARSET']))
{
if (0 < \strlen($sValue))
{
$sEncValue = @\utf8_encode($sValue);
if (0 === \strlen($sEncValue))
{
$sEncValue = $sValue;
}
$sValue = $sEncValue;
}
}
$sValue = \MailSo\Base\Utils::Utf8Clear($sValue);
$sValue = $this->getPropertyValueHelper($oVCard->FN, $bOldVersion);
$aProperties[] = new Property(PropertyType::FULLNAME, $sValue);
}
if (isset($oVCard->NICKNAME) && '' !== \trim($oVCard->NICKNAME))
{
$sValue = \trim($oVCard->NICKNAME);
if ($bOldVersion && !isset($oVCard->NICKNAME->parameters['CHARSET']))
{
if (0 < \strlen($sValue))
{
$sEncValue = @\utf8_encode($sValue);
if (0 === \strlen($sEncValue))
{
$sEncValue = $sValue;
}
$sValue = $sEncValue;
}
}
$sValue = \MailSo\Base\Utils::Utf8Clear($sValue);
$sValue = $sValue = $this->getPropertyValueHelper($oVCard->NICKNAME, $bOldVersion);
$aProperties[] = new Property(PropertyType::NICK_NAME, $sValue);
}
// if (isset($oVCard->NOTE) && '' !== \trim($oVCard->NOTE))
// {
// $sValue = \trim($oVCard->NOTE);
// if ($bOldVersion)
// {
// if (0 < \strlen($sValue))
// {
// $sEncValue = @\utf8_encode($sValue);
// if (0 === \strlen($sEncValue))
// {
// $sEncValue = $sValue;
// }
//
// $sValue = $sEncValue;
// }
// }
//
// $sValue = \MailSo\Base\Utils::Utf8Clear($sValue);
// $sValue = $this->getPropertyValueHelper($oVCard->NOTE, $bOldVersion);
// $aProperties[] = new Property(PropertyType::NOTE, $sValue);
// }
@ -302,244 +401,22 @@ class Contact
if (isset($oVCard->EMAIL))
{
$bPref = false;
foreach($oVCard->EMAIL as $oEmail)
{
$oTypes = $oEmail ? $oEmail['TYPE'] : null;
$sEmail = $oEmail ? \trim($oEmail->getValue()) : '';
if (0 < \strlen($sEmail))
{
if ($oTypes)
{
$oProp = new Property($oTypes->has('WORK') ? PropertyType::EMAIl_BUSSINES : PropertyType::EMAIl_PERSONAL, $sEmail);
if (!$bPref && $oTypes->has('pref'))
{
$bPref = true;
\array_unshift($aProperties, $oProp);
}
else
{
\array_push($aProperties, $oProp);
}
}
else
{
\array_unshift($aProperties,
new Property(PropertyType::EMAIl_PERSONAL, $sEmail));
}
}
}
$this->addArrayPropertyHelper(&$aProperties, $oVCard->EMAIL, PropertyType::EMAIl);
}
if (isset($oVCard->URL))
{
$this->addArrayPropertyHelper(&$aProperties, $oVCard->URL, PropertyType::WEB_PAGE);
}
// if (isset($oVCard->URL))
// {
// foreach($oVCard->URL as $oUrl)
// {
// $oTypes = $oUrl ? $oUrl['TYPE'] : null;
// $sUrl = $oUrl ? \trim((string) $oUrl) : '';
//
// if (0 < \strlen($sUrl))
// {
// \array_push($aProperties,
// new Property($oTypes && $oTypes->has('WORK') ?
// PropertyType::WEB_PAGE_BUSSINES : PropertyType::WEB_PAGE_PERSONAL, $sUrl));
// }
// }
// }
if (isset($oVCard->TEL))
{
$bPref = false;
foreach($oVCard->TEL as $oTel)
{
$oTypes = $oTel ? $oTel['TYPE'] : null;
$sTel = $oTypes ? \trim((string) $oTel) : '';
if (0 < \strlen($sTel))
{
if ($oTypes)
{
$oProp = null;
$bWork = $oTypes->has('WORK');
switch (true)
{
case $oTypes->has('VOICE'):
$oProp = new Property($bWork ? PropertyType::PHONE_BUSSINES : PropertyType::PHONE_PERSONAL, $sTel);
break;
case $oTypes->has('CELL'):
$oProp = new Property($bWork ? PropertyType::MOBILE_BUSSINES : PropertyType::MOBILE_PERSONAL, $sTel);
break;
case $oTypes->has('FAX'):
$oProp = new Property($bWork ? PropertyType::FAX_BUSSINES : PropertyType::FAX_PERSONAL, $sTel);
break;
case $oTypes->has('WORK'):
$oProp = new Property(PropertyType::MOBILE_BUSSINES, $sTel);
break;
default:
$oProp = new Property(PropertyType::MOBILE_PERSONAL, $sTel);
break;
}
if ($oProp)
{
if (!$bPref && $oTypes->has('pref'))
{
$bPref = true;
\array_unshift($aProperties, $oProp);
}
else
{
\array_push($aProperties, $oProp);
}
}
}
else
{
\array_unshift($aProperties,
new Property(PropertyType::MOBILE_PERSONAL, $sTel));
}
}
}
$this->addArrayPropertyHelper(&$aProperties, $oVCard->TEL, PropertyType::PHONE);
}
$this->Properties = $aProperties;
}
$this->UpdateDependentValues(false);
}
/**
* @return string
*/
public function ToVCardObject($sPreVCard = '')
{
$oVCard = null;
if (0 < \strlen($sPreVCard))
{
try
{
$oVCard = \Sabre\VObject\Reader::read($sPreVCard);
}
catch (\Exception $oExc) {};
}
if (!$oVCard)
{
$oVCard = new \Sabre\VObject\Component\VCard();
}
$oVCard->VERSION = '3.0';
$oVCard->PRODID = '-//RainLoop//'.APP_VERSION.'//EN';
unset($oVCard->FN, $oVCard->EMAIL, $oVCard->TEL);
$bPrefEmail = $bPrefPhone = false;
$sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = '';
foreach ($this->Properties as /* @var $oProperty \RainLoop\Providers\AddressBook\Classes\Property */ &$oProperty)
{
if ($oProperty)
{
switch ($oProperty->Type)
{
case PropertyType::FULLNAME:
$oVCard->FN = $oProperty->Value;
break;
case PropertyType::NICK_NAME:
$oVCard->NICKNAME = $oProperty->Value;
break;
case PropertyType::FIRST_NAME:
$sFirstName = $oProperty->Value;
break;
case PropertyType::LAST_NAME:
$sLastName = $oProperty->Value;
break;
case PropertyType::MIDDLE_NAME:
$sMiddleName = $oProperty->Value;
break;
case PropertyType::NAME_SUFFIX:
$sSuffix = $oProperty->Value;
break;
case PropertyType::NAME_PREFIX:
$sPrefix = $oProperty->Value;
break;
case PropertyType::EMAIl_PERSONAL:
case PropertyType::EMAIl_BUSSINES:
case PropertyType::EMAIl_OTHER:
$aParams = array('TYPE' => array('INTERNET'));
$aParams['TYPE'][] = PropertyType::EMAIl_BUSSINES === $oProperty->Type ? 'WORK' : 'HOME';
if (!$bPrefEmail)
{
$bPrefEmail = true;
$aParams['TYPE'][] = 'pref';
}
$oVCard->add('EMAIL', $oProperty->Value, $aParams);
break;
case PropertyType::WEB_PAGE_PERSONAL:
case PropertyType::WEB_PAGE_BUSSINES:
case PropertyType::WEB_PAGE_OTHER:
$aParams = array('TYPE' => array());
$aParams['TYPE'][] = PropertyType::WEB_PAGE_BUSSINES === $oProperty->Type ? 'WORK' : 'HOME';
$oVCard->add('URL', $oProperty->Value, $aParams);
break;
case PropertyType::PHONE_PERSONAL:
case PropertyType::PHONE_BUSSINES:
case PropertyType::PHONE_OTHER:
case PropertyType::MOBILE_PERSONAL:
case PropertyType::MOBILE_BUSSINES:
case PropertyType::MOBILE_OTHER:
case PropertyType::FAX_PERSONAL:
case PropertyType::FAX_BUSSINES:
case PropertyType::FAX_OTHER:
$aParams = array('TYPE' => array());
$sType = '';
if (\in_array($oProperty->Type, array(PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER)))
{
$sType = 'VOICE';
}
else if (\in_array($oProperty->Type, array(PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER)))
{
$sType = 'CELL';
}
else if (\in_array($oProperty->Type, array(PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER)))
{
$sType = 'FAX';
}
if (!empty($sType))
{
$aParams['TYPE'][] = $sType;
}
$aParams['TYPE'][] = \in_array($oProperty->Type, array(
PropertyType::PHONE_BUSSINES, PropertyType::MOBILE_BUSSINES, PropertyType::FAX_BUSSINES)) ? 'WORK' : 'HOME';
if (!$bPrefPhone)
{
$bPrefPhone = true;
$aParams['TYPE'][] = 'pref';
}
$oVCard->add('TEL', $oProperty->Value, $aParams);
break;
}
}
}
$oVCard->UID = $this->IdContactStr;
$oVCard->N = array($sLastName, $sFirstName, $sMiddleName, $sPrefix, $sSuffix);
$oVCard->REV = \gmdate('Ymd', $this->Changed).'T'.\gmdate('His', $this->Changed).'Z';
return $oVCard;
}
/**
* @return string
*/
public function VCardUri()
{
return $this->IdContactStr.'.vcf';
$this->UpdateDependentValues();
}
}

View file

@ -19,7 +19,7 @@ class Property
/**
* @var string
*/
public $TypeCustom;
public $TypeStr;
/**
* @var string
@ -37,12 +37,13 @@ class Property
public $Frec;
public function __construct(
$iType = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::UNKNOWN, $sValue = '')
$iType = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::UNKNOWN, $sValue = '', $sTypeStr = '')
{
$this->Clear();
$this->Type = $iType;
$this->Value = $sValue;
$this->TypeStr = $sTypeStr;
}
public function Clear()
@ -50,7 +51,7 @@ class Property
$this->IdProperty = 0;
$this->Type = PropertyType::UNKNOWN;
$this->TypeCustom = '';
$this->TypeStr = '';
$this->Value = '';
$this->ValueCustom = '';
@ -63,9 +64,7 @@ class Property
*/
public function IsEmail()
{
return \in_array($this->Type, array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER
));
return PropertyType::EMAIl === $this->Type;
}
/**
@ -73,18 +72,29 @@ class Property
*/
public function IsPhone()
{
return \in_array($this->Type, array(
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER,
PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER
));
return PropertyType::PHONE === $this->Type;
}
/**
* @return bool
*/
public function TypesAsArray()
{
$aResult = array();
if (!empty($this->TypeStr))
{
$sTypeStr = \preg_replace('/[\s]+/', '', $this->TypeStr);
$aResult = \explode(',', $sTypeStr);
}
return $aResult;
}
public function UpdateDependentValues()
{
$this->Value = \trim($this->Value);
$this->ValueCustom = \trim($this->ValueCustom);
$this->TypeCustom = \trim($this->TypeCustom);
$this->TypeStr = \trim($this->TypeStr);
if (0 < \strlen($this->Value))
{

View file

@ -16,21 +16,11 @@ class PropertyType
const NAME_PREFIX = 20;
const NAME_SUFFIX = 21;
const EMAIl_PERSONAL = 30;
const EMAIl_BUSSINES = 31;
const EMAIl_OTHER = 32;
const PHONE_PERSONAL = 50;
const PHONE_BUSSINES = 51;
const PHONE_OTHER = 52;
const MOBILE_PERSONAL = 60;
const MOBILE_BUSSINES = 61;
const MOBILE_OTHER = 62;
const FAX_PERSONAL = 70;
const FAX_BUSSINES = 71;
const FAX_OTHER = 72;
const EMAIl = 30;
const PHONE = 31;
// const MOBILE = 32;
// const FAX = 33;
const WEB_PAGE = 32;
const FACEBOOK = 90;
const SKYPE = 91;
@ -38,9 +28,5 @@ class PropertyType
const NOTE = 110;
const WEB_PAGE_PERSONAL = 220;
const WEB_PAGE_BUSSINES = 221;
const WEB_PAGE_OTHER = 222;
const CUSTOM = 250;
}

View file

@ -1,9 +0,0 @@
<?php
namespace RainLoop\Providers\AddressBook\Enumerations;
class ScopeType
{
const DEFAULT_ = 0;
const SHARE_ALL = 2;
}

View file

@ -47,15 +47,335 @@ class PdoAddressBook
return \is_array($aDrivers) ? \in_array($this->sDsnType, $aDrivers) : false;
}
/**
* @return bool
*/
public function IsSharingAllowed()
{
return $this->IsSupported() && false; // TODO
}
private function flushDeletedContacts($iUserID)
{
return !!$this->prepareAndExecute('DELETE FROM rainloop_ab_contacts WHERE id_user = :id_user AND deleted = 1', array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
));
}
private function updateContactEtagAndTime($iUserID, $mID, $sEtag, $iChanged)
{
return !!$this->prepareAndExecute('UPDATE rainloop_ab_contacts SET changed = :changed, etag = :etag '.
'WHERE id_user = :id_user AND id_contact = :id_contact', array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':id_contact' => array($mID, \PDO::PARAM_INT),
':changed' => array($iChanged, \PDO::PARAM_INT),
':etag' => array($sEtag, \PDO::PARAM_STR)
)
);
}
private function prepearDatabaseSyncData($iUserID)
{
$aResult = array();
$oStmt = $this->prepareAndExecute('SELECT id_contact, id_contact_str, changed, deleted, etag FROM rainloop_ab_contacts WHERE id_user = :id_user', array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
));
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
foreach ($aFetch as $aItem)
{
if ($aItem && isset($aItem['id_contact'], $aItem['id_contact_str'], $aItem['changed'], $aItem['deleted'], $aItem['etag']) &&
!empty($aItem['id_contact_str']))
{
$aResult[$aItem['id_contact_str']] = array(
'id_contact' => $aItem['id_contact'],
'etag' => $aItem['etag'],
'changed' => (int) $aItem['changed'],
'deleted' => '1' === (string) $aItem['deleted']
);
}
}
}
}
return $aResult;
}
private function prepearRemoteSyncData($oClient, $sPath)
{
$mResult = false;
$aResponse = null;
try
{
$this->oLogger->Write('PROPFIND '.$sPath, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
$aResponse = $oClient->propFind($sPath, array(
'{DAV:}getlastmodified',
'{DAV:}getetag'
), 1);
}
catch (\Exception $oException)
{
$this->oLogger->WriteException($oException);
}
if (\is_array($aResponse))
{
$mResult = array();
foreach ($aResponse as $sKey => $aItem)
{
$sKey = \rtrim(\trim($sKey), '\\/');
if (!empty($sKey) && is_array($aItem))
{
$aItem = \array_change_key_case($aItem, \CASE_LOWER);
if (isset($aItem['{dav:}getetag'], $aItem['{dav:}getlastmodified']))
{
$aMatch = array();
if (\preg_match('/\/([^\/?]+)$/', $sKey, $aMatch) && !empty($aMatch[1]))
{
$sVcfFileName = $aMatch[1];
$sKeyID = \preg_replace('/\.vcf$/i', '', $sVcfFileName);
$mResult[$sKeyID] = array(
'vcf' => $sVcfFileName,
'etag' => \trim(\trim($aItem['{dav:}getetag']), '"\''),
'lastmodified' => $aItem['{dav:}getlastmodified'],
'changed' => \MailSo\Base\DateTimeHelper::ParseRFC2822DateString($aItem['{dav:}getlastmodified']),
'deleted' => false
);
}
}
}
}
}
return $mResult;
}
private function davClientRequest($oClient, $sCmd, $sUrl, $mData = null)
{
\MailSo\Base\Utils::ResetTimeLimit($this->iResetTimer);
$this->oLogger->Write($sCmd.' '.$sUrl.('PUT' === $sCmd && null !== $mData ? ' ('.\strlen($mData).')' : ''),
\MailSo\Log\Enumerations\Type::INFO, 'DAV');
if ('PUT' === $sCmd)
{
$this->oLogger->Write($mData, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
}
$oResponse = false;
try
{
$oResponse = 'PUT' === $sCmd && null !== $mData ?
$oClient->request($sCmd, $sUrl, $mData) : $oClient->request($sCmd, $sUrl);
if ('GET' === $sCmd || false)
{
$this->oLogger->WriteDump($oResponse, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
}
}
catch (\Exception $oException)
{
$this->oLogger->WriteException($oException);
}
return $oResponse;
}
/**
* @param string $sEmail
* @param \RainLoop\Providers\AddressBook\Classes\Contact $oContact
* @param string $sUrl
* @param string $sUser
* @param string $sPassword
* @param string $sProxy = ''
*
* @return bool
*/
public function ContactSave($sEmail, &$oContact)
public function Sync($sEmail, $sUrl, $sUser, $sPassword, $sProxy = '')
{
$this->Sync();
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
if (0 >= $iUserID)
{
return false;
}
$aUrl = \parse_url($sUrl);
if (!\is_array($aUrl))
{
$aUrl = array();
}
$aUrl['scheme'] = isset($aUrl['scheme']) ? $aUrl['scheme'] : 'http';
$aUrl['host'] = isset($aUrl['host']) ? $aUrl['host'] : 'localhost';
$aUrl['port'] = isset($aUrl['port']) ? $aUrl['port'] : 80;
$aUrl['path'] = isset($aUrl['path']) ? \rtrim($aUrl['path'], '\\/').'/' : '/';
$aSettings = array(
'baseUri' => $aUrl['scheme'].'://'.$aUrl['host'].('80' === (string) $aUrl['port'] ? '' : ':'.$aUrl['port']),
'userName' => $sUser,
'password' => $sPassword
);
if (!empty($sProxy))
{
$aSettings['proxy'] = $sProxy;
}
$sPath = $aUrl['path'];
$oClient = new \Sabre\DAV\Client($aSettings);
$oClient->setVerifyPeer(false);
$aRemoteSyncData = $this->prepearRemoteSyncData($oClient, $sPath);
if (false === $aRemoteSyncData)
{
return false;
}
$aDatabaseSyncData = $this->prepearDatabaseSyncData($iUserID);
// $this->oLogger->WriteDump($aDatabaseSyncData);
// $this->oLogger->WriteDump($aRemoteSyncData);
//+++del (from carddav)
foreach ($aDatabaseSyncData as $sKey => $aData)
{
if ($aData['deleted'] &&
isset($aRemoteSyncData[$sKey], $aRemoteSyncData[$sKey]['vcf']))
{
$this->davClientRequest($oClient, 'DELETE', $sPath.$aRemoteSyncData[$sKey]['vcf']);
}
}
//---del
//+++del (from db)
$aIdsForDeletedion = array();
foreach ($aDatabaseSyncData as $sKey => $aData)
{
if (!$aData['deleted'] && !empty($aData['etag']) && !isset($aRemoteSyncData[$sKey]))
{
$aIdsForDeletedion[] = $aData['id_contact'];
}
}
if (0 < \count($aIdsForDeletedion))
{
$this->DeleteContacts($sEmail, $aIdsForDeletedion, false);
}
//---del
$this->flushDeletedContacts($iUserID);
//+++new or newer (from db)
foreach ($aDatabaseSyncData as $sKey => $aData)
{
if (!$aData['deleted'] &&
(empty($aData['etag']) && !isset($aRemoteSyncData[$sKey])) // new
||
(!empty($aData['etag']) && isset($aRemoteSyncData[$sKey]) && // newer
$aRemoteSyncData[$sKey]['etag'] !== $aData['etag'] &&
$aRemoteSyncData[$sKey]['changed'] < $aData['changed']
)
)
{
$mID = $aData['id_contact'];
$oContact = $this->GetContactByID($sEmail, $mID, false);
if ($oContact)
{
$sExsistensBody = '';
$mExsistenRemoteID = isset($aRemoteSyncData[$sKey]['vcf']) && !empty($aData['etag']) ? $aRemoteSyncData[$sKey]['vcf'] : '';
if (0 < \strlen($mExsistenRemoteID))
{
$oResponse = $this->davClientRequest($oClient, 'GET', $sPath.$mExsistenRemoteID);
if ($oResponse && isset($oResponse['headers'], $oResponse['body']))
{
$sExsistensBody = \trim($oResponse['body']);
}
}
$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']), '"\'');
$sDate = !empty($oResponse['headers']['date']) ? \trim($oResponse['headers']['date']) : '';
if (!empty($sEtag))
{
$iChanged = empty($sDate) ? \time() : \MailSo\Base\DateTimeHelper::ParseRFC2822DateString($sDate);
$this->updateContactEtagAndTime($iUserID, $mID, $sEtag, $iChanged);
}
}
}
unset($oContact);
}
}
//---new
//+++new or newer (from carddav)
foreach ($aRemoteSyncData as $sKey => $aData)
{
if (!isset($aDatabaseSyncData[$sKey]) // new
||
($aDatabaseSyncData[$sKey]['etag'] !== $aData['etag'] && // newer
$aDatabaseSyncData[$sKey]['changed'] < $aData['changed'])
)
{
$mExsistenContactID = isset($aDatabaseSyncData[$sKey]['id_contact']) ?
$aDatabaseSyncData[$sKey]['id_contact'] : '';
$oResponse = $this->davClientRequest($oClient, 'GET', $sPath.$aData['vcf']);
if ($oResponse && isset($oResponse['headers'], $oResponse['body']))
{
$sBody = \trim($oResponse['body']);
if (!empty($sBody))
{
$oContact = null;
if ($mExsistenContactID)
{
$oContact = $this->GetContactByID($sEmail, $mExsistenContactID);
}
if (!$oContact)
{
$oContact = new \RainLoop\Providers\AddressBook\Classes\Contact();
}
$oContact->PopulateByVCard($sBody,
!empty($oResponse['headers']['etag']) ? \trim(\trim($oResponse['headers']['etag']), '"\'') : '');
$this->ContactSave($sEmail, $oContact);
unset($oContact);
}
}
}
}
return true;
}
/**
* @param string $sEmail
* @param \RainLoop\Providers\AddressBook\Classes\Contact $oContact
* @param bool $bSyncDb = true
*
* @return bool
*/
public function ContactSave($sEmail, &$oContact, $bSyncDb = true)
{
if ($bSyncDb)
{
$this->SyncDatabase();
}
$iUserID = $this->getUserId($sEmail);
$iIdContact = 0 < \strlen($oContact->IdContact) && \is_numeric($oContact->IdContact) ? (int) $oContact->IdContact : 0;
@ -77,7 +397,7 @@ class PdoAddressBook
{
$aFreq = $this->getContactFreq($iUserID, $iIdContact);
$sSql = 'UPDATE rainloop_ab_contacts SET id_contact_str = :id_contact_str, display = :display, changed = :changed, hash = :hash '.
$sSql = 'UPDATE rainloop_ab_contacts SET id_contact_str = :id_contact_str, display = :display, changed = :changed, etag = :etag '.
'WHERE id_user = :id_user AND id_contact = :id_contact';
$this->prepareAndExecute($sSql,
@ -87,7 +407,7 @@ class PdoAddressBook
':id_contact_str' => array($oContact->IdContactStr, \PDO::PARAM_STR),
':display' => array($oContact->Display, \PDO::PARAM_STR),
':changed' => array($oContact->Changed, \PDO::PARAM_INT),
':hash' => array($oContact->Hash, \PDO::PARAM_STR)
':etag' => array($oContact->Etag, \PDO::PARAM_STR)
)
);
@ -103,8 +423,8 @@ class PdoAddressBook
else
{
$sSql = 'INSERT INTO rainloop_ab_contacts '.
'( id_user, id_contact_str, display, changed, hash) VALUES '.
'(:id_user, :id_contact_str, :display, :changed, :hash)';
'( id_user, id_contact_str, display, changed, etag) VALUES '.
'(:id_user, :id_contact_str, :display, :changed, :etag)';
$this->prepareAndExecute($sSql,
array(
@ -112,11 +432,11 @@ class PdoAddressBook
':id_contact_str' => array($oContact->IdContactStr, \PDO::PARAM_STR),
':display' => array($oContact->Display, \PDO::PARAM_STR),
':changed' => array($oContact->Changed, \PDO::PARAM_INT),
':hash' => array($oContact->Hash, \PDO::PARAM_STR)
':etag' => array($oContact->Etag, \PDO::PARAM_STR)
)
);
$sLast = $this->lastInsertId('id_contact');
$sLast = $this->lastInsertId('rainloop_ab_contacts', 'id_contact');
if (\is_numeric($sLast) && 0 < (int) $sLast)
{
$iIdContact = (int) $sLast;
@ -139,7 +459,7 @@ class PdoAddressBook
':id_contact' => array($iIdContact, \PDO::PARAM_INT),
':id_user' => array($iUserID, \PDO::PARAM_INT),
':prop_type' => array($oProp->Type, \PDO::PARAM_INT),
':prop_type_custom' => array($oProp->TypeCustom, \PDO::PARAM_STR),
':prop_type_str' => array($oProp->TypeStr, \PDO::PARAM_STR),
':prop_value' => array($oProp->Value, \PDO::PARAM_STR),
':prop_value_custom' => array($oProp->ValueCustom, \PDO::PARAM_STR),
':prop_frec' => array($iFreq, \PDO::PARAM_INT),
@ -147,8 +467,8 @@ class PdoAddressBook
}
$sSql = 'INSERT INTO rainloop_ab_properties '.
'( id_contact, id_user, prop_type, prop_type_custom, prop_value, prop_value_custom, prop_frec) VALUES '.
'(:id_contact, :id_user, :prop_type, :prop_type_custom, :prop_value, :prop_value_custom, :prop_frec)';
'( id_contact, id_user, prop_type, prop_type_str, prop_value, prop_value_custom, prop_frec) VALUES '.
'(:id_contact, :id_user, :prop_type, :prop_type_str, :prop_value, :prop_value_custom, :prop_frec)';
$this->prepareAndExecute($sSql, $aParams, true);
}
@ -174,12 +494,17 @@ class PdoAddressBook
/**
* @param string $sEmail
* @param array $aContactIds
* @param bool $bSyncDb = true
*
* @return bool
*/
public function DeleteContacts($sEmail, $aContactIds)
public function DeleteContacts($sEmail, $aContactIds, $bSyncDb = true)
{
$this->Sync();
if ($bSyncDb)
{
$this->SyncDatabase();
}
$iUserID = $this->getUserId($sEmail);
$aContactIds = \array_filter($aContactIds, function (&$mItem) {
@ -196,7 +521,44 @@ class PdoAddressBook
$aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT));
$this->prepareAndExecute('DELETE FROM rainloop_ab_properties WHERE id_user = :id_user AND id_contact IN ('.$sIDs.')', $aParams);
$this->prepareAndExecute('DELETE FROM rainloop_ab_contacts WHERE id_user = :id_user AND id_contact IN ('.$sIDs.')', $aParams);
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':changed' => array(\time(), \PDO::PARAM_INT)
);
$this->prepareAndExecute('UPDATE rainloop_ab_contacts SET deleted = 1, changed = :changed '.
'WHERE id_user = :id_user AND id_contact IN ('.$sIDs.')', $aParams);
return true;
}
/**
* @param string $sEmail
* @param array $aTagsIds
*
* @return bool
*/
public function DeleteTags($sEmail, $aTagsIds)
{
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
$aTagsIds = \array_filter($aTagsIds, function (&$mItem) {
$mItem = (int) \trim($mItem);
return 0 < $mItem;
});
if (0 === \count($aTagsIds))
{
return false;
}
$sIDs = \implode(',', $aTagsIds);
$aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT));
$this->prepareAndExecute('DELETE FROM rainloop_pab_tags_contacts WHERE id_tag IN ('.$sIDs.')');
$this->prepareAndExecute('DELETE FROM rainloop_pab_tags WHERE id_user = :id_user AND id_tag IN ('.$sIDs.')', $aParams);
return true;
}
@ -212,7 +574,7 @@ class PdoAddressBook
*/
public function GetContacts($sEmail, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0)
{
$this->Sync();
$this->SyncDatabase();
$iOffset = 0 <= $iOffset ? $iOffset : 0;
$iLimit = 0 < $iLimit ? (int) $iLimit : 20;
@ -235,11 +597,7 @@ class PdoAddressBook
$sSql = 'SELECT id_user, id_prop, id_contact FROM rainloop_ab_properties '.
'WHERE (id_user = :id_user) AND (prop_value LIKE :search ESCAPE \'=\''.
(0 < \strlen($sCustomSearch) ? ' OR (prop_type IN ('.\implode(',', array(
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER,
PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER
)).') AND prop_value_custom <> \'\' AND prop_value_custom LIKE :search_custom_phone)' : '').
(0 < \strlen($sCustomSearch) ? ' OR prop_type = '.PropertyType::PHONE.' AND prop_value_custom <> \'\' AND prop_value_custom LIKE :search_custom_phone)' : '').
') GROUP BY id_contact, id_prop';
$aParams = array(
@ -297,7 +655,7 @@ class PdoAddressBook
if (0 < $iCount)
{
$sSql = 'SELECT * FROM rainloop_ab_contacts WHERE id_user = :id_user';
$sSql = 'SELECT * FROM rainloop_ab_contacts WHERE deleted = 0 AND id_user = :id_user';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
@ -367,7 +725,7 @@ class PdoAddressBook
$oProperty = new \RainLoop\Providers\AddressBook\Classes\Property();
$oProperty->IdProperty = (int) $aItem['id_prop'];
$oProperty->Type = (int) $aItem['prop_type'];
$oProperty->TypeCustom = isset($aItem['prop_type_custom']) ? (string) $aItem['prop_type_custom'] : '';
$oProperty->TypeStr = isset($aItem['prop_type_str']) ? (string) $aItem['prop_type_str'] : '';
$oProperty->Value = (string) $aItem['prop_value'];
$oProperty->ValueCustom = isset($aItem['prop_value_custom']) ? (string) $aItem['prop_value_custom'] : '';
$oProperty->Frec = isset($aItem['prop_frec']) ? (int) $aItem['prop_frec'] : 0;
@ -403,13 +761,11 @@ class PdoAddressBook
*/
public function GetContactByID($sEmail, $mID, $bIsStrID = false)
{
$this->Sync();
$mID = \trim($mID);
$iUserID = $this->getUserId($sEmail);
$sSql = 'SELECT * FROM rainloop_ab_contacts WHERE id_user = :id_user';
$sSql = 'SELECT * FROM rainloop_ab_contacts WHERE deleted = 0 AND id_user = :id_user';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
@ -450,7 +806,7 @@ class PdoAddressBook
$oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : '';
$oContact->Changed = isset($aItem['changed']) ? (int) $aItem['changed'] : 0;
$oContact->ReadOnly = $iUserID !== (isset($aItem['id_user']) ? (int) $aItem['id_user'] : 0);
$oContact->Hash = empty($aItem['hash']) ? '' : (string) $aItem['hash'];
$oContact->Etag = empty($aItem['etag']) ? '' : (string) $aItem['etag'];
}
}
}
@ -478,7 +834,7 @@ class PdoAddressBook
$oProperty = new \RainLoop\Providers\AddressBook\Classes\Property();
$oProperty->IdProperty = (int) $aItem['id_prop'];
$oProperty->Type = (int) $aItem['prop_type'];
$oProperty->TypeCustom = isset($aItem['prop_type_custom']) ? (string) $aItem['prop_type_custom'] : '';
$oProperty->TypeStr = isset($aItem['prop_type_str']) ? (string) $aItem['prop_type_str'] : '';
$oProperty->Value = (string) $aItem['prop_value'];
$oProperty->ValueCustom = isset($aItem['prop_value_custom']) ? (string) $aItem['prop_value_custom'] : '';
$oProperty->Frec = isset($aItem['prop_frec']) ? (int) $aItem['prop_frec'] : 0;
@ -516,12 +872,12 @@ class PdoAddressBook
throw new \InvalidArgumentException('Empty Search argument');
}
$this->Sync();
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
$sTypes = implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FIRST_NAME, PropertyType::LAST_NAME
PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME
));
$sSql = 'SELECT id_contact, id_prop, prop_type, prop_value FROM rainloop_ab_properties '.
@ -575,7 +931,7 @@ class PdoAddressBook
$oStmt->closeCursor();
$sTypes = \implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FIRST_NAME, PropertyType::LAST_NAME
PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME
));
$sSql = 'SELECT id_prop, id_contact, prop_type, prop_value FROM rainloop_ab_properties '.
@ -607,8 +963,8 @@ class PdoAddressBook
$aNames[$iIdContact][PropertyType::LAST_NAME === $iType ? 0 : 1] = $aItem['prop_value'];
}
else if ((isset($aIdProps[$iIdProp]) || isset($aContactAllAccess[$iIdContact]))&&
\in_array($iType, array(PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES)))
else if ((isset($aIdProps[$iIdProp]) || isset($aContactAllAccess[$iIdContact])) &&
PropertyType::EMAIl === $iType)
{
if (!isset($aEmails[$iIdContact]))
{
@ -676,22 +1032,19 @@ class PdoAddressBook
throw new \InvalidArgumentException('Empty Emails argument');
}
$this->Sync();
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
$sTypes = \implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER
));
$aExists = array();
$aEmailsToCreate = array();
$aEmailsToUpdate = array();
if ($bCreateAuto)
{
$sSql = 'SELECT prop_value FROM rainloop_ab_properties WHERE id_user = :id_user AND prop_type IN ('.$sTypes.')';
$sSql = 'SELECT prop_value FROM rainloop_ab_properties WHERE id_user = :id_user AND prop_type = :prop_type';
$oStmt = $this->prepareAndExecute($sSql, array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
':id_user' => array($iUserID, \PDO::PARAM_INT),
':prop_type' => array(PropertyType::EMAIl, \PDO::PARAM_INT)
));
if ($oStmt)
@ -748,7 +1101,7 @@ class PdoAddressBook
if ('' !== \trim($oEmail->GetEmail()))
{
$oPropEmail = new \RainLoop\Providers\AddressBook\Classes\Property();
$oPropEmail->Type = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::EMAIl_PERSONAL;
$oPropEmail->Type = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::EMAIl;
$oPropEmail->Value = \strtolower(\trim($oEmail->GetEmail()));
$oContact->Properties[] = $oPropEmail;
@ -797,7 +1150,7 @@ class PdoAddressBook
}
}
$sSql = 'UPDATE rainloop_ab_properties SET prop_frec = prop_frec + 1 WHERE id_user = :id_user AND prop_type IN ('.$sTypes;
$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);
@ -805,15 +1158,16 @@ class PdoAddressBook
if (1 === \count($aEmailsQuoted))
{
$sSql .= ') AND prop_value = '.$aEmailsQuoted[0];
$sSql .= ' AND prop_value = '.$aEmailsQuoted[0];
}
else
{
$sSql .= ') AND prop_value IN ('.\implode(',', $aEmailsQuoted).')';
$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)
));
}
@ -825,7 +1179,7 @@ class PdoAddressBook
$sResult = '';
try
{
$this->Sync();
$this->SyncDatabase();
if (0 >= $this->getVersion($this->sDsnType.'-ab-version'))
{
$sResult = 'Unknown database error';
@ -857,14 +1211,13 @@ class PdoAddressBook
CREATE TABLE IF NOT EXISTS rainloop_ab_contacts (
id_contact bigint UNSIGNED NOT NULL AUTO_INCREMENT,
id_contact_str varchar(128) NOT NULL DEFAULT \'\',
id_user int UNSIGNED NOT NULL,
display_name varchar(255) NOT NULL DEFAULT '',
display_email varchar(255) NOT NULL DEFAULT '',
display varchar(255) NOT NULL DEFAULT '',
changed int UNSIGNED NOT NULL DEFAULT 0,
hash varchar(128) NOT NULL DEFAULT \'\',
id_contact bigint UNSIGNED NOT NULL AUTO_INCREMENT,
id_contact_str varchar(128) NOT NULL DEFAULT '',
id_user int UNSIGNED NOT NULL,
display varchar(255) NOT NULL DEFAULT '',
changed int UNSIGNED NOT NULL DEFAULT 0,
deleted tinyint UNSIGNED NOT NULL DEFAULT 0,
etag varchar(128) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT '',
PRIMARY KEY(id_contact),
INDEX id_user_rainloop_ab_contacts_index (id_user)
@ -873,20 +1226,42 @@ CREATE TABLE IF NOT EXISTS rainloop_ab_contacts (
CREATE TABLE IF NOT EXISTS rainloop_ab_properties (
id_prop bigint UNSIGNED NOT NULL AUTO_INCREMENT,
id_contact bigint UNSIGNED NOT NULL,
id_user int UNSIGNED NOT NULL,
prop_type tinyint UNSIGNED NOT NULL,
prop_type_custom varchar(50) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT '',
prop_value varchar(255) NOT NULL DEFAULT '',
prop_value_custom varchar(255) NOT NULL DEFAULT '',
prop_frec int UNSIGNED NOT NULL DEFAULT 0,
id_prop bigint UNSIGNED NOT NULL AUTO_INCREMENT,
id_contact bigint UNSIGNED NOT NULL,
id_user int UNSIGNED NOT NULL,
prop_type tinyint UNSIGNED NOT NULL,
prop_type_str varchar(255) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT '',
prop_value varchar(255) NOT NULL DEFAULT '',
prop_value_custom varchar(255) NOT NULL DEFAULT '',
prop_frec int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY(id_prop),
INDEX id_user_rainloop_ab_properties_index (id_user),
INDEX id_user_id_contact_rainloop_ab_properties_index (id_user, id_contact)
)/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE IF NOT EXISTS rainloop_ab_tags (
id_tag int UNSIGNED NOT NULL AUTO_INCREMENT,
id_user int UNSIGNED NOT NULL,
tag_name varchar(255) NOT NULL,
PRIMARY KEY(id_tag),
INDEX id_user_rainloop_ab_tags_index (id_user),
INDEX id_user_name_rainloop_ab_tags_index (id_user, tag_name)
)/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE IF NOT EXISTS rainloop_ab_tags_contacts (
id_tag int UNSIGNED NOT NULL,
id_contact bigint UNSIGNED NOT NULL,
INDEX id_tag_rainloop_ab_tags_contacts_index (id_tag),
INDEX id_contact_rainloop_ab_tags_contacts_index (id_contact)
)/*!40000 ENGINE=INNODB */;
MYSQLINITIAL;
break;
@ -894,31 +1269,48 @@ MYSQLINITIAL;
$sInitial = <<<POSTGRESINITIAL
CREATE TABLE rainloop_ab_contacts (
id_contact bigserial PRIMARY KEY,
id_user integer NOT NULL,
display_name varchar(128) NOT NULL DEFAULT '',
display_email varchar(128) NOT NULL DEFAULT '',
display varchar(128) NOT NULL DEFAULT '',
changed integer NOT NULL default 0.
hash varchar(128) NOT NULL DEFAULT ''
id_contact bigserial PRIMARY KEY,
id_contact_str varchar(128) NOT NULL DEFAULT '',
id_user integer NOT NULL,
display varchar(255) NOT NULL DEFAULT '',
changed integer NOT NULL default 0,
deleted integer NOT NULL default 0,
etag varchar(128) NOT NULL DEFAULT ''
);
CREATE INDEX id_user_rainloop_ab_contacts_index ON rainloop_ab_contacts (id_user);
CREATE TABLE rainloop_ab_properties (
id_prop bigserial PRIMARY KEY,
id_contact integer NOT NULL,
id_user integer NOT NULL,
prop_type integer NOT NULL,
prop_type_custom varchar(50) NOT NULL DEFAULT '',
prop_value varchar(128) NOT NULL DEFAULT '',
prop_value_custom varchar(128) NOT NULL DEFAULT '',
prop_frec integer NOT NULL default 0
id_prop bigserial PRIMARY KEY,
id_contact integer NOT NULL,
id_user integer NOT NULL,
prop_type integer NOT NULL,
prop_type_str varchar(255) NOT NULL DEFAULT '',
prop_value text NOT NULL DEFAULT '',
prop_value_custom text NOT NULL DEFAULT '',
prop_frec integer NOT NULL default 0
);
CREATE INDEX id_user_rainloop_ab_properties_index ON rainloop_ab_properties (id_user);
CREATE INDEX id_user_id_contact_rainloop_ab_properties_index ON rainloop_ab_properties (id_user, id_contact);
CREATE TABLE rainloop_ab_tags (
id_tag serial PRIMARY KEY,
id_user integer NOT NULL,
tag_name varchar(255) NOT NULL
);
CREATE INDEX id_user_rainloop_ab_tags_index ON rainloop_ab_tags (id_user);
CREATE INDEX id_user_name_rainloop_ab_tags_index ON rainloop_ab_tags (id_user, tag_name);
CREATE TABLE rainloop_ab_tags_contacts (
id_tag integer NOT NULL,
id_contact integer NOT NULL
);
CREATE INDEX id_tag_rainloop_ab_tags_index ON rainloop_ab_tags_contacts (id_tag);
CREATE INDEX id_contact_rainloop_ab_tags_index ON rainloop_ab_tags_contacts (id_contact);
POSTGRESINITIAL;
break;
@ -926,31 +1318,48 @@ POSTGRESINITIAL;
$sInitial = <<<SQLITEINITIAL
CREATE TABLE rainloop_ab_contacts (
id_contact integer NOT NULL PRIMARY KEY,
id_user integer NOT NULL,
display_name text NOT NULL DEFAULT '',
display_email text NOT NULL DEFAULT '',
display text NOT NULL DEFAULT '',
changed integer NOT NULL DEFAULT 0,
hash text NOT NULL DEFAULT ''
id_contact integer NOT NULL PRIMARY KEY,
id_contact_str text NOT NULL DEFAULT '',
id_user integer NOT NULL,
display text NOT NULL DEFAULT '',
changed integer NOT NULL DEFAULT 0,
deleted integer NOT NULL DEFAULT 0,
etag text NOT NULL DEFAULT ''
);
CREATE INDEX id_user_rainloop_ab_contacts_index ON rainloop_ab_contacts (id_user);
CREATE TABLE rainloop_ab_properties (
id_prop integer NOT NULL PRIMARY KEY,
id_contact integer NOT NULL,
id_user integer NOT NULL,
prop_type integer NOT NULL,
prop_type_custom text NOT NULL DEFAULT '',
prop_value text NOT NULL DEFAULT '',
prop_value_custom text NOT NULL DEFAULT '',
prop_frec integer NOT NULL DEFAULT 0
id_prop integer NOT NULL PRIMARY KEY,
id_contact integer NOT NULL,
id_user integer NOT NULL,
prop_type integer NOT NULL,
prop_type_str text NOT NULL DEFAULT '',
prop_value text NOT NULL DEFAULT '',
prop_value_custom text NOT NULL DEFAULT '',
prop_frec integer NOT NULL DEFAULT 0
);
CREATE INDEX id_user_rainloop_ab_properties_index ON rainloop_ab_properties (id_user);
CREATE INDEX id_user_id_contact_rainloop_ab_properties_index ON rainloop_ab_properties (id_user, id_contact);
CREATE TABLE rainloop_ab_tags (
id_tag integer NOT NULL PRIMARY KEY,
id_user integer NOT NULL,
tag_name text NOT NULL
);
CREATE INDEX id_user_rainloop_ab_tags_index ON rainloop_ab_tags (id_user);
CREATE INDEX id_user_name_rainloop_ab_tags_index ON rainloop_ab_tags (id_user, tag_name);
CREATE TABLE rainloop_ab_tags_contacts (
id_tag integer NOT NULL,
id_contact integer NOT NULL
);
CREATE INDEX id_tag_rainloop_ab_tags_index ON rainloop_ab_tags_contacts (id_tag);
CREATE INDEX id_contact_rainloop_ab_tags_index ON rainloop_ab_tags_contacts (id_contact);
SQLITEINITIAL;
break;
}
@ -974,22 +1383,32 @@ SQLITEINITIAL;
/**
* @return bool
*/
public function Sync()
public function SyncDatabase()
{
static $mCache = null;
if (null !== $mCache)
{
return $mCache;
}
$mCache = false;
switch ($this->sDsnType)
{
case 'mysql':
return $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
$mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
));
break;
case 'pgsql':
return $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
$mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
));
break;
case 'sqlite':
return $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
$mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
));
break;
}
return false;
@ -1004,14 +1423,11 @@ SQLITEINITIAL;
{
$aResult = array();
$sTypes = \implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES
));
$sSql = 'SELECT prop_value, prop_frec FROM rainloop_ab_properties WHERE id_user = :id_user AND id_contact = :id_contact AND prop_type IN ('.$sTypes.')';
$sSql = 'SELECT prop_value, prop_frec FROM rainloop_ab_properties WHERE id_user = :id_user AND id_contact = :id_contact AND prop_type = :type';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':id_contact' => array($iIdContact, \PDO::PARAM_INT)
':id_contact' => array($iIdContact, \PDO::PARAM_INT),
':type' => array(PropertyType::EMAIl, \PDO::PARAM_INT)
);
$oStmt = $this->prepareAndExecute($sSql, $aParams);

View file

@ -18,16 +18,33 @@ class Storage extends \RainLoop\Providers\AbstractProvider
}
/**
* @param \RainLoop\Account|null $oAccount
* @param \RainLoop\Account|string|null $oAccount
* @param int $iStorageType
* @param string $iStorageType
*
* @return bool
*/
public function verifyAccount($oAccount, $iStorageType)
{
if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType &&
!($oAccount instanceof \RainLoop\Account || \is_string($oAccount)))
{
return false;
}
return true;
}
/**
* @param \RainLoop\Account|string|null $oAccount
* @param int $iStorageType
* @param string $sKey
* @param string $sValue
*
* @return bool
*/
public function Put($oAccount, $iStorageType, $sKey, $sValue)
{
if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType && !($oAccount instanceof \RainLoop\Account))
if (!$this->verifyAccount($oAccount, $iStorageType))
{
return false;
}
@ -36,7 +53,7 @@ class Storage extends \RainLoop\Providers\AbstractProvider
}
/**
* @param \RainLoop\Account|null $oAccount
* @param \RainLoop\Account|string|null $oAccount
* @param int $iStorageType
* @param string $sKey
* @param mixed $mDefault = false
@ -45,7 +62,7 @@ class Storage extends \RainLoop\Providers\AbstractProvider
*/
public function Get($oAccount, $iStorageType, $sKey, $mDefault = false)
{
if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType && !($oAccount instanceof \RainLoop\Account))
if (!$this->verifyAccount($oAccount, $iStorageType))
{
return $mDefault;
}
@ -54,7 +71,7 @@ class Storage extends \RainLoop\Providers\AbstractProvider
}
/**
* @param \RainLoop\Account|null $oAccount
* @param \RainLoop\Account|string|null $oAccount
* @param int $iStorageType
* @param string $sKey
*
@ -62,7 +79,7 @@ class Storage extends \RainLoop\Providers\AbstractProvider
*/
public function Clear($oAccount, $iStorageType, $sKey)
{
if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType && !($oAccount instanceof \RainLoop\Account))
if (!$this->verifyAccount($oAccount, $iStorageType))
{
return false;
}

View file

@ -20,7 +20,7 @@ class DefaultStorage implements \RainLoop\Providers\Storage\StorageInterface
}
/**
* @param \RainLoop\Account|null $oAccount
* @param \RainLoop\Account|string|null $oAccount
* @param int $iStorageType
* @param string $sKey
* @param string $sValue
@ -34,7 +34,7 @@ class DefaultStorage implements \RainLoop\Providers\Storage\StorageInterface
}
/**
* @param \RainLoop\Account|null $oAccount
* @param \RainLoop\Account|string|null $oAccount
* @param int $iStorageType
* @param string $sKey
* @param mixed $mDefault = false
@ -54,7 +54,7 @@ class DefaultStorage implements \RainLoop\Providers\Storage\StorageInterface
}
/**
* @param \RainLoop\Account|null $oAccount
* @param \RainLoop\Account|string|null $oAccount
* @param int $iStorageType
* @param string $sKey
*
@ -73,22 +73,27 @@ class DefaultStorage implements \RainLoop\Providers\Storage\StorageInterface
}
/**
* @param \RainLoop\Account|null $oAccount
* @param \RainLoop\Account|string|null $mAccount
* @param int $iStorageType
* @param string $sKey
* @param bool $bMkDir = false
*
* @return string
*/
private function generateFileName($oAccount, $iStorageType, $sKey, $bMkDir = false)
private function generateFileName($mAccount, $iStorageType, $sKey, $bMkDir = false)
{
if (!$oAccount)
if (null === $mAccount)
{
$iStorageType = \RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY;
}
$sEmail = $oAccount ? \preg_replace('/[^a-z0-9\-\.@]+/', '_',
('' === $oAccount->ParentEmail() ? '' : $oAccount->ParentEmail().'/').$oAccount->Email()) : '';
$sEmail = $mAccount instanceof \RainLoop\Account ? \preg_replace('/[^a-z0-9\-\.@]+/', '_',
('' === $mAccount->ParentEmail() ? '' : $mAccount->ParentEmail().'/').$mAccount->Email()) : '';
if (\is_string($mAccount) && empty($sEmail))
{
$sEmail = \preg_replace('/[^a-z0-9\-\.@]+/', '_', $mAccount);
}
$sTypePath = $sKeyPath = '';
switch ($iStorageType)

View file

@ -1,46 +0,0 @@
<?php
namespace RainLoop\SabreDAV;
class AuthBasic extends \Sabre\DAV\Auth\Backend\AbstractBasic
{
/**
* @var \RainLoop\Providers\PersonalAddressBook
*/
private $oPersonalAddressBook;
/**
* @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook
*/
public function __construct($oPersonalAddressBook)
{
$this->oPersonalAddressBook = $oPersonalAddressBook;
}
/**
* @param string $sUserName
* @param string $sPassword
*
* @return bool
*/
protected function validateUserPass($sUserName, $sPassword)
{
$sHash = '';
try
{
$sHash = $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true);
}
catch (Exception $oException) {}
// var_dump($sHash);
// exit();
if (!empty($sHash) && $sPassword === $sHash)
{
$this->currentUser = $sUserName;
return true;
}
return false;
}
}

View file

@ -1,46 +0,0 @@
<?php
namespace RainLoop\SabreDAV;
class AuthDigest extends \Sabre\DAV\Auth\Backend\AbstractDigest
{
/**
* @var \RainLoop\Providers\PersonalAddressBook
*/
private $oPersonalAddressBook;
/**
* @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook
*/
public function __construct($oPersonalAddressBook)
{
$this->oPersonalAddressBook = $oPersonalAddressBook;
}
/**
* @param string $sRealm
* @param string $sUserName
*
* @return string|null
*/
public function getDigestHash($sRealm, $sUserName)
{
$sHash = '';
try
{
$sHash = $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true);
}
catch (Exception $oException) {}
// var_dump($sHash);
// exit();
if (!empty($sHash))
{
$this->currentUser = $sUserName;
return \md5($sUserName.':'.$sRealm.':'.$sHash);
}
return null;
}
}

View file

@ -1,421 +0,0 @@
<?php
namespace RainLoop\SabreDAV;
class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface
{
/**
* @var \RainLoop\Providers\PersonalAddressBook
*/
private $oPersonalAddressBook;
/**
* @var \RainLoop\SabreDAV\AuthBasic
*/
private $oAuthBackend;
/**
* @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook
* @param \RainLoop\SabreDAV\AuthBasic $oAuthBackend
*/
public function __construct($oPersonalAddressBook, &$oAuthBackend)
{
$this->oPersonalAddressBook = $oPersonalAddressBook;
$this->oPersonalAddressBook->ConsiderShare(false);
$this->oAuthBackend = $oAuthBackend;
}
/**
* @param mixed $mData
*/
private function writeLog($mData)
{
$this->oPersonalAddressBook->Logger()->WriteMixed($mData);
}
/**
* @param string $sPrincipalUri
*
* @return string
*/
private function getEmailFromPrincipalUri($sPrincipalUri)
{
$sEmail = '';
$aMatch = array();
if (\preg_match('/\/?principals\/([^@\/]+@[^@\/]+)/i', $sPrincipalUri, $aMatch) && !empty($aMatch[1]))
{
$sEmail = \trim($aMatch[1]);
}
return $sEmail;
}
/**
* @param string $sPrincipalUri = ''
* @param string $mAddressBookID = ''
*
* @return string
*/
private function getAuthEmail($sPrincipalUri = '', $mAddressBookID = '')
{
$sGetCurrentUser = \trim($this->oAuthBackend->getCurrentUser());
if (0 < \strlen($sPrincipalUri) && 0 < \strlen($sGetCurrentUser) &&
$sGetCurrentUser !== $this->getEmailFromPrincipalUri($sPrincipalUri))
{
$sGetCurrentUser = '';
}
if (0 < \strlen((string) $mAddressBookID) && 0 < \strlen($sGetCurrentUser) &&
(string) $mAddressBookID !== (string) $this->oPersonalAddressBook->GetUserUidByEmail($sGetCurrentUser))
{
$sGetCurrentUser = '';
}
return $sGetCurrentUser;
}
/**
* Returns the list of addressbooks for a specific user.
*
* @param string $sPrincipalUri
* @return array
*/
public function getAddressBooksForUser($sPrincipalUri)
{
$this->writeLog('::getAddressBooksForUser('.$sPrincipalUri.')');
$aAddressBooks = array();
$sEmail = $this->getAuthEmail($sPrincipalUri);
if (0 < strlen($sEmail))
{
$mAddressBookID = $this->oPersonalAddressBook->GetUserUidByEmail($sEmail);
if (!empty($mAddressBookID))
{
$aAddressBooks[] = array(
'id' => $mAddressBookID,
'uri' => 'default',
'principaluri' => $sPrincipalUri,
'{DAV:}displayname' => 'Personal Address Book',
'{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => 'Personal Address Book',
'{http://calendarserver.org/ns/}getctag' => $this->oPersonalAddressBook->GetCtagByEmail($sEmail),
'{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}supported-address-data' => new \Sabre\CardDAV\Property\SupportedAddressData()
);
}
}
return $aAddressBooks;
}
/**
* Updates an addressbook's properties
*
* See Sabre\DAV\IProperties for a description of the mutations array, as
* well as the return value.
*
* @param mixed $mAddressBookID
* @param array $aMutations
* @see Sabre\DAV\IProperties::updateProperties
* @return bool|array
*/
public function updateAddressBook($mAddressBookID, array $aMutations)
{
$this->writeLog('::updateAddressBook('.$mAddressBookID.', array $aMutations['.\count($aMutations).'])');
return false;
}
/**
* Creates a new address book
*
* @param string $sPrincipalUri
* @param string $sUrl Just the 'basename' of the url.
* @param array $aProperties
*
* @return void
*/
public function createAddressBook($sPrincipalUri, $sUrl, array $aProperties)
{
$this->writeLog('::createAddressBook('.$sPrincipalUri.', '.$sUrl.', array $aProperties['.\count($aProperties).'])');
}
/**
* Deletes an entire addressbook and all its contents
*
* @param mixed $mAddressBookID
*
* @return void
*/
public function deleteAddressBook($mAddressBookID)
{
$this->writeLog('::deleteAddressBook('.$mAddressBookID.')');
}
/**
* Returns all cards for a specific addressbook id.
*
* This method should return the following properties for each card:
* * carddata - raw vcard data
* * uri - Some unique url
* * lastmodified - A unix timestamp
*
* It's recommended to also return the following properties:
* * etag - A unique etag. This must change every time the card changes.
* * size - The size of the card in bytes.
*
* If these last two properties are provided, less time will be spent
* calculating them. If they are specified, you can also ommit carddata.
* This may speed up certain requests, especially with large cards.
*
* @param mixed $mAddressBookID
*
* @return array
*/
public function getCards($mAddressBookID)
{
$this->writeLog('::getCards('.$mAddressBookID.')');
$aResult = array();
if (!empty($mAddressBookID))
{
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
$aList = $this->oPersonalAddressBook->GetContacts($sEmail, 0, 5000);
foreach ($aList as /* @var $oItem \RainLoop\Providers\PersonalAddressBook\Classes\Contact */ $oItem)
{
if (!$oItem->ReadOnly)
{
$aResult[] = array(
'uri' => $oItem->VCardUri(),
'lastmodified' => $oItem->Changed,
'etag' => $oItem->CardDavHash,
'size' => $oItem->CardDavSize
);
}
}
}
}
return $aResult;
}
/**
* Returns a specfic card.
*
* The same set of properties must be returned as with getCards. The only
* exception is that 'carddata' is absolutely required.
*
* @param mixed $mAddressBookID
* @param string $sCardUri
* @return array
*/
public function getCard($mAddressBookID, $sCardUri)
{
$this->writeLog('::getCard('.$mAddressBookID.', '.$sCardUri.')');
$mResult = false;
$oContact = null;
if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4))
{
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
$oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true);
}
}
if ($oContact)
{
$mResult = array(
'uri' => $oContact->VCardUri(),
'lastmodified' => $oContact->Changed,
'etag' => $oContact->CardDavHash,
'size' => $oContact->CardDavSize,
'carddata' => $oContact->CardDavData
);
}
return $mResult;
}
/**
* Creates a new card.
*
* The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getAddressbooksForUser method.
*
* The cardUri is a base uri, and doesn't include the full path. The
* cardData argument is the vcard body, and is passed as a string.
*
* It is possible to return an ETag from this method. This ETag is for the
* newly created resource, and must be enclosed with double quotes (that
* is, the string itself must contain the double quotes).
*
* You should only return the ETag if you store the carddata as-is. If a
* subsequent GET request on the same card does not have the same body,
* byte-by-byte and you did return an ETag here, clients tend to get
* confused.
*
* If you don't return an ETag, you can just return null.
*
* @param mixed $mAddressBookID
* @param string $sCardUri
* @param string $sCardData
*
* @return string|null
*/
public function createCard($mAddressBookID, $sCardUri, $sCardData)
{
$this->writeLog('::createCard('.$mAddressBookID.', '.$sCardUri.', $sCardData)');
$this->writeLog($sCardData);
$oVCard = null;
if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData))
{
try
{
$oVCard = \Sabre\VObject\Reader::read($sCardData);
}
catch (\Exception $oException)
{
$this->writeLog($oException);
}
if ($oVCard)
{
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
if (empty($oVCard->UID))
{
$oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID();
$sCardData = $oVCard->serialize();
}
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
$oContact->ParseVCard($oVCard, $sCardData);
$this->oPersonalAddressBook->ContactSave($sEmail, $oContact);
}
}
}
return null;
}
/**
* Updates a card.
*
* The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getAddressbooksForUser method.
*
* The cardUri is a base uri, and doesn't include the full path. The
* cardData argument is the vcard body, and is passed as a string.
*
* It is possible to return an ETag from this method. This ETag should
* match that of the updated resource, and must be enclosed with double
* quotes (that is: the string itself must contain the actual quotes).
*
* You should only return the ETag if you store the carddata as-is. If a
* subsequent GET request on the same card does not have the same body,
* byte-by-byte and you did return an ETag here, clients tend to get
* confused.
*
* If you don't return an ETag, you can just return null.
*
* @param mixed $mAddressBookID
* @param string $sCardUri
* @param string $sCardData
*
* @return string|null
*/
public function updateCard($mAddressBookID, $sCardUri, $sCardData)
{
$this->writeLog('::updateCard('.$mAddressBookID.', '.$sCardUri.', $sCardData)');
$this->writeLog($sCardData);
if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData))
{
try
{
$oVCard = \Sabre\VObject\Reader::read($sCardData);
}
catch (\Exception $oException)
{
$this->writeLog($oException);
}
if ($oVCard)
{
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
$iRev = 0;
$aMatch = array();
if (!empty($oVCard->REV) && \preg_match('/(20[0-9][0-9])([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])Z/i', $oVCard->REV, $aMatch))
{// 1Y 2m 3d 4H 5i 6s
$iRev = \gmmktime($aMatch[4], $aMatch[5], $aMatch[6], $aMatch[2], $aMatch[3], $aMatch[1]);
}
$oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true);
if ($oContact && (0 === $iRev || $oContact->Changed < $iRev))
{
if (empty($oVCard->UID))
{
$oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID();
$sCardData = $oVCard->serialize();
}
$oContact->ParseVCard($oVCard, $sCardData);
if ($this->oPersonalAddressBook->ContactSave($sEmail, $oContact) && !empty($oContact->CardDavHash))
{
return '"'.$oContact->CardDavHash.'"';
}
}
else
{
if ($oContact && $oContact->Changed < $iRev)
{
$this->writeLog('Obsolete revision: ['.(empty($oVCard->REV) ? '' : $oVCard->REV).', '.$oContact->Changed.', '.$iRev.']');
}
}
}
}
}
return null;
}
/**
* Deletes a card
*
* @param mixed $mAddressBookID
* @param string $sCardUri
*
* @return bool
*/
public function deleteCard($mAddressBookID, $sCardUri)
{
$this->writeLog('::deleteCard('.$mAddressBookID.', '.$sCardUri.')');
$bResult = false;
$oContact = null;
if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4))
{
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
$oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true);
}
}
if ($oContact)
{
$bResult = $this->oPersonalAddressBook->DeleteContacts($sEmail, array($oContact->IdContact));
}
return $bResult;
}
}

View file

@ -1,65 +0,0 @@
<?php
namespace RainLoop\SabreDAV;
class Logger extends \Sabre\DAV\ServerPlugin
{
/**
* @var \MailSo\Log\Logger
*/
private $oLogger;
/**
* @param \MailSo\Log\Logger $oLogger
*/
public function __construct($oLogger)
{
$this->oLogger = null;
if ($oLogger instanceof \MailSo\Log\Logger)
{
$this->oLogger = $oLogger;
}
}
public function initialize(\Sabre\DAV\Server $server)
{
$this->server = $server;
$this->server->subscribeEvent('beforeMethod', array($this, 'beforeMethod'), 30);
}
/**
* Returns a plugin name.
*
* Using this name other plugins will be able to access other plugins
* using \Sabre\DAV\Server::getPlugin
*
* @return string
*/
public function getPluginName()
{
return 'logger';
}
/**
* This method is called before any HTTP method, but after authentication.
*
* @param string $sMethod
* @param string $sPath
* @throws \Sabre\DAV\Exception\NotAuthenticated
* @return bool
*/
public function beforeMethod($sMethod, $sPath)
{
if ($this->oLogger)
{
if (true)
{
$body = $this->server->httpRequest->getBody(true);
$this->server->httpRequest->setBody($body);
$this->oLogger->Write($body, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
}
}
return true;
}
}

View file

@ -1,205 +0,0 @@
<?php
namespace RainLoop\SabreDAV;
class Principal extends \Sabre\DAVACL\PrincipalBackend\AbstractBackend
{
/**
* @var \RainLoop\Providers\PersonalAddressBook
*/
private $oPersonalAddressBook;
/**
* @var \RainLoop\SabreDAV\AuthBasic
*/
private $oAuthBackend;
/**
* @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook
* @param \RainLoop\SabreDAV\AuthBasic $oAuthBackend
*/
public function __construct($oPersonalAddressBook, &$oAuthBackend)
{
$this->oPersonalAddressBook = $oPersonalAddressBook;
$this->oAuthBackend = $oAuthBackend;
}
/**
* Returns a list of principals based on a prefix.
*
* This prefix will often contain something like 'principals'. You are only
* expected to return principals that are in this base path.
*
* You are expected to return at least a 'uri' for every user, you can
* return any additional properties if you wish so. Common properties are:
* {DAV:}displayname
* {http://sabredav.org/ns}email-address - This is a custom SabreDAV
* field that's actually injected in a number of other properties. If
* you have an email address, use this property.
*
* @param string $prefixPath
* @return array
*/
function getPrincipalsByPrefix($prefixPath)
{
$aResult = array();
if (\preg_match('/^principals/', $prefixPath))
{
$sGetCurrentUser = $this->oAuthBackend->getCurrentUser();
if (!empty($sGetCurrentUser))
{
$aResult[] = array(
'uri' => 'principals/'.$sGetCurrentUser,
'{DAV:}displayname' => $sGetCurrentUser,
'{http://sabredav.org/ns}email-address' => $sGetCurrentUser
);
}
}
return $aResult;
}
/**
* Returns a specific principal, specified by it's path.
* The returned structure should be the exact same as from
* getPrincipalsByPrefix.
*
* @param string $sPath
* @return array
*/
function getPrincipalByPath($sPath)
{
$sGetCurrentUser = $this->oAuthBackend->getCurrentUser();
if ('principals/'.$sGetCurrentUser === $sPath)
{
return array(
'uri' => 'principals/'.$sGetCurrentUser,
'{DAV:}displayname' => $sGetCurrentUser,
'{http://sabredav.org/ns}email-address' => $sGetCurrentUser
);
}
return array();
}
/**
* Updates one ore more webdav properties on a principal.
*
* The list of mutations is supplied as an array. Each key in the array is
* a propertyname, such as {DAV:}displayname.
*
* Each value is the actual value to be updated. If a value is null, it
* must be deleted.
*
* This method should be atomic. It must either completely succeed, or
* completely fail. Success and failure can simply be returned as 'true' or
* 'false'.
*
* It is also possible to return detailed failure information. In that case
* an array such as this should be returned:
*
* array(
* 200 => array(
* '{DAV:}prop1' => null,
* ),
* 201 => array(
* '{DAV:}prop2' => null,
* ),
* 403 => array(
* '{DAV:}prop3' => null,
* ),
* 424 => array(
* '{DAV:}prop4' => null,
* ),
* );
*
* In this previous example prop1 was successfully updated or deleted, and
* prop2 was succesfully created.
*
* prop3 failed to update due to '403 Forbidden' and because of this prop4
* also could not be updated with '424 Failed dependency'.
*
* This last example was actually incorrect. While 200 and 201 could appear
* in 1 response, if there's any error (403) the other properties should
* always fail with 423 (failed dependency).
*
* But anyway, if you don't want to scratch your head over this, just
* return true or false.
*
* @param string $path
* @param array $mutations
* @return array|bool
*/
function updatePrincipal($path, $mutations)
{
return true;
}
/**
* This method is used to search for principals matching a set of
* properties.
*
* This search is specifically used by RFC3744's principal-property-search
* REPORT. You should at least allow searching on
* http://sabredav.org/ns}email-address.
*
* The actual search should be a unicode-non-case-sensitive search. The
* keys in searchProperties are the WebDAV property names, while the values
* are the property values to search on.
*
* If multiple properties are being searched on, the search should be
* AND'ed.
*
* This method should simply return an array with full principal uri's.
*
* If somebody attempted to search on a property the backend does not
* support, you should simply return 0 results.
*
* You can also just return 0 results if you choose to not support
* searching at all, but keep in mind that this may stop certain features
* from working.
*
* @param string $prefixPath
* @param array $searchProperties
* @return array
*/
function searchPrincipals($prefixPath, array $searchProperties)
{
return array();
}
/**
* Returns the list of members for a group-principal
*
* @param string $principal
* @return array
*/
function getGroupMemberSet($principal)
{
return array();
}
/**
* Returns the list of groups a principal is a member of
*
* @param string $principal
* @return array
*/
function getGroupMembership($principal)
{
return array();
}
/**
* Updates the list of group members for a group principal.
*
* The principals should be passed as a list of uri's.
*
* @param string $principal
* @param array $members
* @return void
*/
function setGroupMemberSet($principal, array $members)
{
}
}

View file

@ -108,29 +108,14 @@ class Service
$bCached = false;
$sResult = '';
$sPathInfo = \trim(\trim($this->oHttp->GetServer('PATH_INFO', '')), ' /');
if (!empty($sPathInfo))
{
if ('dav' !== \substr($sPathInfo, 0, 3))
{
$sPathInfo = '';
}
}
if (empty($sPathInfo))
$sQuery = \trim(\trim($this->oHttp->GetServer('QUERY_STRING', '')), ' /');
$iPos = \strpos($sQuery, '&');
if (0 < $iPos)
{
$sQuery = \trim(\trim($this->oHttp->GetServer('QUERY_STRING', '')), ' /');
$iPos = \strpos($sQuery, '&');
if (0 < $iPos)
{
$sQuery = \substr($sQuery, 0, $iPos);
}
$sQuery = \substr($sQuery, 0, $iPos);
}
else
{
$sQuery = $sPathInfo;
}
$this->oActions->Plugins()->RunHook('filter.http-query', array(&$sQuery));
$aPaths = \explode('/', $sQuery);
$this->oActions->Plugins()->RunHook('filter.http-paths', array(&$aPaths));

View file

@ -719,85 +719,6 @@ class ServiceActions
));
}
public function HostDav()
{
try
{
\set_error_handler(function ($errno, $errstr, $errfile, $errline ) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
});
$oPersonalAddressBookProvider = $this->oActions->PersonalAddressBookProvider();
$oAuthBackend = null;
if ($this->Config()->Get('labs', 'sync_dav_digest_auth', false))
{
$oAuthBackend = new \RainLoop\SabreDAV\AuthDigest($oPersonalAddressBookProvider);
}
else
{
$oAuthBackend = new \RainLoop\SabreDAV\AuthBasic($oPersonalAddressBookProvider);
}
$oCarddavBackend = new \RainLoop\SabreDAV\CardDAV($oPersonalAddressBookProvider, $oAuthBackend);
$oPrincipalBackend = new \RainLoop\SabreDAV\Principal($oPersonalAddressBookProvider, $oAuthBackend);
$oPrincipalCollection = new \Sabre\DAVACL\PrincipalCollection($oPrincipalBackend);
$oPrincipalCollection->disableListing = true;
$oAddressBookRoot = new \Sabre\CardDAV\AddressBookRoot($oPrincipalBackend, $oCarddavBackend);
$aTree = array($oPrincipalCollection, $oAddressBookRoot);
$this->Plugins()->RunHook('filter.sabre-dav-tree', array(&$aTree));
$oServer = new \Sabre\DAV\Server($aTree);
$sBaseUri = '/';
if (false !== \strpos($this->oHttp->GetUrl(), '/index.php/dav/'))
{
$aPath = \trim($this->oHttp->GetPath(), '/\\ ');
$sBaseUri = (0 < \strlen($aPath) ? '/'.$aPath : '').'/index.php/dav/';
}
$this->Plugins()->RunHook('filter.sabre-dav-base-url', array(&$sBaseUri));
$oServer->setBaseUri($sBaseUri);
// Plugins
$oServer->addPlugin(new \Sabre\DAV\Auth\Plugin($oAuthBackend, 'RainLoop'));
$oServer->addPlugin(new \Sabre\CardDAV\Plugin());
$oServer->addPlugin(new \Sabre\DAVACL\Plugin());
$oServer->addPlugin(new \Sabre\CardDAV\VCFExportPlugin());
$oServer->addPlugin(new \RainLoop\SabreDAV\Logger($this->Logger()));
if ($this->Config()->Get('labs', 'sync_use_dav_browser', false))
{
$oServer->addPlugin(new \Sabre\DAV\Browser\Plugin());
}
$this->Plugins()->RunHook('filter.sabre-dav-before-exec', array(&$oServer));
$oServer->exec();
}
catch (\Exception $oException)
{
$this->Logger()->WriteException($oException);
}
}
/**
* @return string
*/
public function ServiceDav()
{
if ($this->oActions->Config()->Get('contacts', 'allow_sync', false))
{
$this->HostDav();
}
return '';
}
/**
* @return string
*/

View file

@ -315,6 +315,7 @@ class Client {
// Return headers as part of the response
CURLOPT_HEADER => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_USERAGENT => 'RainLoop DAV Client',
// Automatically follow redirects
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,

View file

@ -18,19 +18,21 @@
<i data-bind="css: enableContacts() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
Enable contacts
</label>
<label data-bind="click: function () { contactsSharing(!contactsSharing()); }">
<!-- <label data-bind="click: function () { contactsSharing(!contactsSharing()); }">
<i data-bind="css: contactsSharing() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
Allow contacts sharing
</label>
<br />
Allow contacts sharing (disabled)
</label>-->
<label data-bind="click: function () { contactsSync(!contactsSync()); }">
<i data-bind="css: contactsSync() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
Allow contacts sync server (CardDAV)
Allow contacts sync (with external CardDAV server)
</label>
<a href="http://rainloop.net/docs/web-servers/" target="_blank">Web server configuration examples</a>
</div>
</div>
<br />
</div>
<div class="form-horizontal" data-bind="visible: contactsSupported">
<div class="legend">
Storage (PDO)
</div>
<div class="control-group">
<label class="control-label">
Type
@ -41,10 +43,8 @@
<div data-bind="saveTrigger: contactsTypeTrigger"></div>
</div>
</div>
<br />
<div data-bind="visible: 'sqlite' !== contactsType()">
<div class="legend">
PDO (MySQL / PostgreSQL)
</div>
<div class="control-group">
<label class="control-label">
Dsn
@ -90,15 +90,12 @@
</div>
</div>
<div data-bind="visible: 'sqlite' === contactsType()">
<div class="legend">
PDO (SQLite)
</div>
<div class="control-group">
<div class="controls">
<div class="alert alert-null-left-margin span8">
<h4>Notice!</h4>
<br />
Don't use this type of database with a large number of active users.
Don't use this database type with a large number of active users.
</div>
</div>
</div>

View file

@ -31,7 +31,7 @@
</a>
<a class="btn btn-dark-disabled-border button-delete" data-tooltip-placement="bottom" data-bind="command: deleteCommand, tooltip: 'MESSAGE_LIST/BUTTON_DELETE'">
<i class="icon-trash"></i>
<span data-bind="text: printableMessageCountForDeletion()"></span>
<!--<span data-bind="text: printableMessageCountForDeletion()"></span>-->
</a>
</div>
<div class="btn-group">&nbsp;</div>

View file

@ -75,10 +75,10 @@
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SEARCH/LABEL_ADV_FLAGGED"></span>
</label>
<label data-bind="click: function () { hasAttachments(!hasAttachments()); }">
<i data-bind="css: hasAttachments() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
<label data-bind="click: function () { hasAttachment(!hasAttachment()); }">
<i data-bind="css: hasAttachment() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SEARCH/LABEL_ADV_HAS_ATTACHMENTS"></span>
<span class="i18n" data-i18n-text="SEARCH/LABEL_ADV_HAS_ATTACHMENT"></span>
</label>
</div>
</div>

View file

@ -3,7 +3,7 @@
<div class="modal-header b-header-toolbar g-ui-user-select-none">
<button type="button" class="close" data-bind="command: cancelCommand">&times;</button>
<a class="btn btn-large button-send" data-bind="command: sendCommand, css: {'btn-danger': sendError, 'btn-warning': sendSuccessButSaveError }">
<i data-bind="css: {'icon-rocket': !sending(), 'icon-spinner animated': sending(), 'icon-white': sendError() || sendSuccessButSaveError()}"></i>
<i data-bind="css: {'icon-paper-plane': !sending(), 'icon-spinner animated': sending(), 'icon-white': sendError() || sendSuccessButSaveError()}"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="COMPOSE/BUTTON_SEND"></span>
</a>

View file

@ -14,7 +14,8 @@
<div class="btn-group">
<a class="btn dropdown-toggle buttonMore" data-toggle="dropdown">
<i data-bind="css: {'icon-list': !contacts.importing(), 'icon-spinner animated': contacts.importing()}"></i>
<i data-bind="css: {'icon-list': !contacts.importing() && !contacts.syncing(),
'icon-spinner animated': contacts.importing() || contacts.syncing()}"></i>
</a>
<ul class="dropdown-menu g-ui-menu" role="menu">
<li class="e-item">
@ -24,6 +25,13 @@
<span class="i18n" data-i18n-text="CONTACTS/BUTTON_IMPORT"></span>
</a>
</li>
<li class="e-item" data-bind="visible: enableContactsSync">
<a class="e-link" data-bind="command: syncCommand">
<i class="icon-sync"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="CONTACTS/BUTTON_SYNC"></span>
</a>
</li>
</ul>
</div>
@ -147,32 +155,6 @@
<i class="icon-lock iconsize24" data-tooltip-placement="left" data-bind="tooltip: 'CONTACTS/LABEL_READ_ONLY'"></i>
</div>
<div class="e-share-sign" data-bind="visible: contactsSharingIsAllowed">
<div class="btn-group pull-right">
<a class="btn dropdown-toggle" data-tooltip-placement="left" data-toggle="dropdown" data-bind="tooltip: 'CONTACTS/LABEL_SHARE'">
<i data-bind="css: shareIcon"></i>
&nbsp;&nbsp;
<span class="caret"></span>
</a>
<ul class="dropdown-menu g-ui-menu" role="menu">
<li class="e-item" data-bind="css: {'selected': shareToNone}">
<a class="e-link" data-bind="click: setShareToNone">
<i class="icon-none"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="CONTACTS/BUTTON_SHARE_NONE"></span>
</a>
</li>
<li class="e-item" data-bind="css: {'selected': shareToAll}">
<a class="e-link" data-bind="click: setShareToAll">
<i class="icon-earth"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="CONTACTS/BUTTON_SHARE_ALL"></span>
</a>
</li>
</ul>
</div>
</div>
<button class="btn button-save-contact" data-bind="command: saveCommand, css: {'dirty': watchDirty}">
<i data-bind="css: {'icon-ok': !viewSaving(), 'icon-spinner animated': viewSaving()}"></i>
&nbsp;&nbsp;

View file

@ -13,53 +13,52 @@
</div>
</div>
</div>
<br />
<div class="form-horizontal" data-bind="visible: allowContactsSync">
<div class="legend">
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LEGEND_MOBILE_SYNC"></span>
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LEGEND_CONTACTS_SYNC"></span>
</div>
<div class="control-group">
<label class="control-label" style="padding-top: 0;">
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_SYNC_SERVER"></span>
<div class="controls">
<label data-bind="click: function () { enableContactsSync(!enableContactsSync()); }">
<i data-bind="css: enableContactsSync() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_CONTACTS_SYNC_ENABLE"></span>
</label>
</div>
</div>
<!-- <div class="control-group">
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_CONTACTS_SYNC_SERVER"></span>
</label>
<div class="controls">
<strong data-bind="text: contactsSyncServer"></strong>
<input type="text" data-bind="" />
</div>
</div>
</div>-->
<div class="control-group">
<label class="control-label" style="padding-top: 0;">
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_SYNC_USERNAME"></span>
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_CONTACTS_SYNC_AB_URL"></span>
</label>
<div class="controls">
<strong data-bind="text: contactsSyncUser"></strong>
<input class="input-xxlarge" type="text" data-bind="value: contactsSyncUrl" />
</div>
</div>
<div class="control-group">
<label class="control-label" style="padding-top: 0;">
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_SYNC_PASSWORD"></span>
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_CONTACTS_SYNC_USER"></span>
</label>
<div class="controls">
<strong style="color:red" data-bind="text: contactsSyncPass, visible: showPassword"></strong>
<span class="g-ui-link i18n" data-bind="visible: !showPassword(), click: toggleShowPassword"
data-i18n-text="SETTINGS_CONTACTS/LINK_SHOW"></span>
&nbsp;&nbsp;
<span class="g-ui-link i18n" data-bind="visible: showPassword, click: toggleShowPassword"
data-i18n-text="SETTINGS_CONTACTS/LINK_HIDE"></span>
<input type="text" data-bind="value: contactsSyncUser" />
</div>
</div>
<div class="control-group">
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_CONTACTS_SYNC_PASSWORD"></span>
</label>
<div class="controls">
<div class="alert alert-info span7" style="margin-left: 0">
<p>
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/DESC_FULL_PAB_URL"></span>
</p>
<p>
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_DESC_PAB"></span>:
<br />
<a href="javascript:void(0);" target="_blank" data-bind="text: contactsSyncPabUrl, attr: {'href': contactsSyncPabUrl}"></a>
</p>
</div>
<br />
<input type="password" data-bind="value: contactsSyncPass" />
</div>
</div>
</div>
</div>
</div>

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "Von"
LABEL_ADV_TO = "An"
LABEL_ADV_SUBJECT = "Thema"
LABEL_ADV_TEXT = "Text"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Anhangdateien vorhanden"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Kontakt aus der Liste wählen um ihn anzuzeigen."
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "Von"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Vom Browser blockiert.)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Vorlagen"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Die Nachrichten-Liste ist nicht verfügbar"
CANT_GET_MESSAGE = "Diese Nachricht ist nicht verfügbar"
CANT_DELETE_MESSAGE = "Diese Nachricht kann nicht gelöscht werden"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "From"
LABEL_ADV_TO = "To"
LABEL_ADV_SUBJECT = "Subject"
LABEL_ADV_TEXT = "Text"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Has attachments"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Select contact in list to view it here."
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "From"
@ -385,15 +389,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Blocked by the browser)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Themes"
@ -539,6 +541,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Can't get message list"
CANT_GET_MESSAGE = "Can't get message"
CANT_DELETE_MESSAGE = "Can't delete message"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "De"
LABEL_ADV_TO = "Para"
LABEL_ADV_SUBJECT = "Asunto"
LABEL_ADV_TEXT = "Texto"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Tiene adjuntos"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Seleccionar un contacto en la lista para verlo aquí."
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "De"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Bloqueado por el explorador)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Temas"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "No se puede obtener la lista de mensajes"
CANT_GET_MESSAGE = "No se puede obtener el mensaje"
CANT_DELETE_MESSAGE = "No se puede eliminar el mensaje"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "De"
LABEL_ADV_TO = "A"
LABEL_ADV_SUBJECT = "Sujet"
LABEL_ADV_TEXT = "Texte"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Pièces jointes"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Sélectionner un contact dans la liste pour l'afficher ici.
LABEL_DISPLAY_NAME = "Nom Affiché"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Téléphone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Ajouter une adresse email"
LINK_ADD_PHONE = "Ajouter un numéro de téléphone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Entrez le nom à afficher"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Lire seulement"
LABEL_SHARE = "Partager"
BUTTON_SHARE_NONE = "Personne"
BUTTON_SHARE_ALL = "Tout le monde"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "De"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Bloqué par le navigateur)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Serveur"
LABEL_SYNC_USERNAME = "Nom d'utilisateur"
LABEL_SYNC_PASSWORD = "Mot de passe"
LINK_HIDE = "cacher"
LINK_SHOW = "montrer"
LABEL_DESC_PAB = "Carnet d'adresses personnel"
DESC_FULL_PAB_URL = "Si votre application (comme Mozilla Thunderbird (Extension SOGo Connector pour Thunderbird) ou une Evolution) nécessite le chemin complet vers le carnet d'adresses de CardDAV, utilisez l'URL ci-dessous."
LABEL_CONTACTS_AUTOSAVE = "Ajouter automatiquement les destinataires de votre carnet d'adresses"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Thèmes"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Impossible d'obtenir la liste des messages"
CANT_GET_MESSAGE = "Impossible d'obtenir le message"
CANT_DELETE_MESSAGE = "Impossible de supprimer le message"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "Feladó"
LABEL_ADV_TO = "Címzett"
LABEL_ADV_SUBJECT = "Tárgy"
LABEL_ADV_TEXT = "Szöveg"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Mellékletek"
LABEL_ADV_FLAGGED = "Megjelölve"
LABEL_ADV_UNSEEN = "Észrevétlen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Select contact in list to view it here."
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "E-mail"
LABEL_PHONE = "Telefonszám"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Adjunk meg e-mail címet"
LINK_ADD_PHONE = "Adjunk meg telefonszámot"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Megosztás"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "From"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Blocked by the browser)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Themes"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Can't get message list"
CANT_GET_MESSAGE = "Can't get message"
CANT_DELETE_MESSAGE = "Can't delete message"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "Frá"
LABEL_ADV_TO = "Til"
LABEL_ADV_SUBJECT = "Viðfangsefni"
LABEL_ADV_TEXT = "Texti"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Hefur viðhengi"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Veldu tengilið í listanum til að skoða hér."
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "Frá"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Lokað af vafra)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Þemur"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Get ekki sótt bréfa lista"
CANT_GET_MESSAGE = "Get ekki sótt bréf"
CANT_DELETE_MESSAGE = "Get ekki eytt bréfi"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "Da"
LABEL_ADV_TO = "A"
LABEL_ADV_SUBJECT = "Oggetto"
LABEL_ADV_TEXT = "Messaggio"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Ha allegati"
LABEL_ADV_FLAGGED = "Preferiti"
LABEL_ADV_UNSEEN = "Non letti"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Seleziona un contatto dalla lista per visualizzarlo qui"
LABEL_DISPLAY_NAME = "Nome visualizzato"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Telefono"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Aggiungi un'indirizzo email"
LINK_ADD_PHONE = "Aggiungi un numero di telefono"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Inserisci il nome visualizzato"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Sola lettura"
LABEL_SHARE = "Condividi"
BUTTON_SHARE_NONE = "Nessuno"
BUTTON_SHARE_ALL = "Tutti"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "Da"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Bloccate dal browser)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contatti"
LEGEND_MOBILE_SYNC = "Sincronizzazione mobile"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "Username"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "nascondi"
LINK_SHOW = "mostra"
LABEL_DESC_PAB = "Rubrica personale"
DESC_FULL_PAB_URL = "Se la tua applicazione (come Mozilla Thunderbird (estensione SOGo Connector) o Evolution) richiedono il percorso completo alla rubrica CardDAV, utilizza l'URL in basso."
LABEL_CONTACTS_AUTOSAVE = "Aggiungi automaticamente le persone che ti inviano mail alla rubrica"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Temi"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Impossibile ottenere la lista dei messaggi"
CANT_GET_MESSAGE = "Impossibile ottenere il messaggio"
CANT_DELETE_MESSAGE = "Impossibile cancellare il messaggio"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "差出人"
LABEL_ADV_TO = "宛先"
LABEL_ADV_SUBJECT = "件名"
LABEL_ADV_TEXT = "キーワード"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "添付ファイルあり"
LABEL_ADV_FLAGGED = "スター付き"
LABEL_ADV_UNSEEN = "未読メール"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Select contact in list to view it here."
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "差出人"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(ブラウザでブロックされて
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Themes"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Can't get message list"
CANT_GET_MESSAGE = "Can't get message"
CANT_DELETE_MESSAGE = "Can't delete message"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "보낸 이"
LABEL_ADV_TO = "받는 이"
LABEL_ADV_SUBJECT = "제목"
LABEL_ADV_TEXT = "본문"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "첨부파일이 있는 메시지"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "선택한 연락처가 이 곳에 표시됩니다"
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "보낸 이"
@ -380,15 +384,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(브라우저에 의해 차단됨)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "테마"
@ -534,6 +536,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "메시지 목록을 불러 올 수 없습니다."
CANT_GET_MESSAGE = "메시지를 가져올 수 없습니다."
CANT_DELETE_MESSAGE = "메시지를 삭제할 수 없습니다."

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "No"
LABEL_ADV_TO = "Kam"
LABEL_ADV_SUBJECT = "Tēma"
LABEL_ADV_TEXT = "Teksts"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Ir pielikumi"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Izvēlaties kontaktu no saraksta lai to apskatītu."
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "No"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Parlūkprogramma bloķēja)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Tēmas"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Nevar ielādēt ziņojumu sarakstu"
CANT_GET_MESSAGE = "Nevar ielādēt ziņojumu"
CANT_DELETE_MESSAGE = "Nevar izdzēst ziņojumu"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "Van"
LABEL_ADV_TO = "Naar"
LABEL_ADV_SUBJECT = "Onderwerp"
LABEL_ADV_TEXT = "Tekst"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Heeft bijlages"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Selecteer het contact in de lijst om hier te bekijken."
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "Van"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Geblokkeerd door de browser)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Themes"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Can't get message list"
CANT_GET_MESSAGE = "Can't get message"
CANT_DELETE_MESSAGE = "Kan bericht niet verwijderen"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "Fra"
LABEL_ADV_TO = "Til"
LABEL_ADV_SUBJECT = "Emne"
LABEL_ADV_TEXT = "Tekst"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Har vedlegg"
LABEL_ADV_FLAGGED = "Markert"
LABEL_ADV_UNSEEN = "Ulest"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Velg kontakt i listen for å vise det her ."
LABEL_DISPLAY_NAME = "Navn"
LABEL_EMAIL = "E-post"
LABEL_PHONE = "Telefon"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Legg til en e-postadresse"
LINK_ADD_PHONE = "Legg til en telefon"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "Fra"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "( Blokkert av nettleseren )"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Tema"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Kan ikke få meldingsliste"
CANT_GET_MESSAGE = "Kan ikke få meldingen"
CANT_DELETE_MESSAGE = "Kan ikke slette meldingen"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "Od"
LABEL_ADV_TO = "Do"
LABEL_ADV_SUBJECT = "Temat"
LABEL_ADV_TEXT = "Tekst"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Ma załączniki"
LABEL_ADV_FLAGGED = "Oznaczona"
LABEL_ADV_UNSEEN = "Nieodczytana"
@ -160,6 +161,8 @@ CONTACT_VIEW_DESC = "Wybierz kontakt z listy w celu jego wyświetlenia"
LABEL_DISPLAY_NAME = "Wyświetlana nazwa"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Telefon"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Dodaj adres email"
LINK_ADD_PHONE = "Dodaj nr Tel"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Nazwa wyświetlana"
@ -169,6 +172,7 @@ LABEL_READ_ONLY = "Tylko do odczytu"
LABEL_SHARE = "Udostępnij kontakt"
BUTTON_SHARE_NONE = "Nikomu"
BUTTON_SHARE_ALL = "Wszystkim"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "Od"
@ -382,15 +386,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Zablokowane przez przeglądarkę)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Kontakty"
LEGEND_MOBILE_SYNC = "Synchronizacja mobilna"
LABEL_SYNC_SERVER = "Serwer"
LABEL_SYNC_USERNAME = "Użytkownik"
LABEL_SYNC_PASSWORD = "Hasło"
LINK_HIDE = "ukryj"
LINK_SHOW = "pokaż"
LABEL_DESC_PAB = "Książka adresowa"
DESC_FULL_PAB_URL = "Jeżeli twoja aplikacja (taka jak Mozilla Thunderbird (rozszerzenie-SOGo Connector Thunderbird) lub pokrewne) wymaga pełnej ścieżki dostępu do książki adresowej CardDAV, użyj poniższego adresu URL."
LABEL_CONTACTS_AUTOSAVE = "Automatycznie dodaj nieznanych nadawców i odbiorców do książki adresowej"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Wygląd"
@ -536,6 +538,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Nie jest możliwe pobranie listy wiadomości"
CANT_GET_MESSAGE = "Nie można pobrać wiadomości"
CANT_DELETE_MESSAGE = "Nie można usunąć wiadomości"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "De"
LABEL_ADV_TO = "Para"
LABEL_ADV_SUBJECT = "Assunto"
LABEL_ADV_TEXT = "Texto"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Tem Anexos"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Selecione o contato na lista para visualizá-lo."
LABEL_DISPLAY_NAME = "Nome público"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Telefone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Adicionar um endereço e-mail"
LINK_ADD_PHONE = "Adicionar um telefone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Digite um nome público"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Ler somente"
LABEL_SHARE = "Compartilhar"
BUTTON_SHARE_NONE = "Ninguém"
BUTTON_SHARE_ALL = "Todo mundo"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "De"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Bloqueado pelo navegador)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contatos"
LEGEND_MOBILE_SYNC = "Sincronia Mobile"
LABEL_SYNC_SERVER = "Servidor"
LABEL_SYNC_USERNAME = "Usuário"
LABEL_SYNC_PASSWORD = "Senha"
LINK_HIDE = "esconder"
LINK_SHOW = "mostrar"
LABEL_DESC_PAB = "Lista de endereços pessoais"
DESC_FULL_PAB_URL = "Se o seu aplicativo (como o Mozilla Thunderbird (SOGo conector extensão Thunderbird) ou Evolution) requer caminho completo para o livro de endereços CardDAV, use a URL abaixo."
LABEL_CONTACTS_AUTOSAVE = "Adicionar automaticamente destinatários a sua lista de endereços"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Temas"
@ -536,6 +538,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Não é possível obter a lista de mensagens"
CANT_GET_MESSAGE = "Não é possível obter a mensagem"
CANT_DELETE_MESSAGE = "Não é possível excluir a mensagem"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "De"
LABEL_ADV_TO = "Para"
LABEL_ADV_SUBJECT = "Assunto"
LABEL_ADV_TEXT = "Texto"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Tem Anexos"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Selecione o contato na lista para visualizá-lo."
LABEL_DISPLAY_NAME = "Nome público"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Telefone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Adicionar um endereço e-mail"
LINK_ADD_PHONE = "Adicionar um telefone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Digite um nome público"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Ler somente"
LABEL_SHARE = "Compartilhar"
BUTTON_SHARE_NONE = "Ninguém"
BUTTON_SHARE_ALL = "Todo mundo"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "De"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Bloqueado pelo navegador)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contatos"
LEGEND_MOBILE_SYNC = "Sincronia Mobile"
LABEL_SYNC_SERVER = "Servidor"
LABEL_SYNC_USERNAME = "Usuário"
LABEL_SYNC_PASSWORD = "Senha"
LINK_HIDE = "esconder"
LINK_SHOW = "mostrar"
LABEL_DESC_PAB = "Lista de endereços pessoais"
DESC_FULL_PAB_URL = "Se o seu aplicativo (como o Mozilla Thunderbird (SOGo conector extensão Thunderbird) ou Evolution) requer caminho completo para o livro de endereços CardDAV, use a URL abaixo."
LABEL_CONTACTS_AUTOSAVE = "Adicionar automaticamente destinatários a sua lista de endereços"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Temas"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Não é possível obter a lista de mensagens"
CANT_GET_MESSAGE = "Não é possível obter a mensagem"
CANT_DELETE_MESSAGE = "Não é possível excluir a mensagem"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "De la"
LABEL_ADV_TO = "către"
LABEL_ADV_SUBJECT = "Subiect"
LABEL_ADV_TEXT = "Conținut"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "Atașamente"
LABEL_ADV_FLAGGED = "Importante"
LABEL_ADV_UNSEEN = "Necitit"
@ -160,6 +161,8 @@ CONTACT_VIEW_DESC = "Selectați un contact pentru al vizualiza"
LABEL_DISPLAY_NAME = "Nume complet"
LABEL_EMAIL = "Adresa de eMail"
LABEL_PHONE = "Тelefon"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Adugă Mail nou"
LINK_ADD_PHONE = "Adaugă un număr nou"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Introduceți numele complet"
@ -169,6 +172,7 @@ LABEL_READ_ONLY = "Doar pentru citire"
LABEL_SHARE = "Distribuie"
BUTTON_SHARE_NONE = "Nimic"
BUTTON_SHARE_ALL = "Toate"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "De la"
@ -382,15 +386,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(browser-ul blocat)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacte"
LEGEND_MOBILE_SYNC = "Sincronizați cu mobilul"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "Utilizator"
LABEL_SYNC_PASSWORD = "Parolă"
LINK_HIDE = "Ascunde"
LINK_SHOW = "Arată"
LABEL_DESC_PAB = "Agendă personală"
DESC_FULL_PAB_URL = "Dacă cerea dumneavoastră (cum ar fi Mozilla Thunderbird (Extensie Sogo Conector Thunderbird) sau Evolution) are nevoie de calea completă la agenda CardDAV, utilizați URL-ul de mai jos."
LABEL_CONTACTS_AUTOSAVE = "Adaugă automat destinatarii la agenda de scrisori"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Tematică"
@ -536,6 +538,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Nu găsesc o lista de scrisori"
CANT_GET_MESSAGE = "Nu pot obține scrisoarea. Încercați din nou"
CANT_DELETE_MESSAGE = "Nu pot șterge scrisoarea. Încercați din nou"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "От"
LABEL_ADV_TO = "Кому"
LABEL_ADV_SUBJECT = "Тема"
LABEL_ADV_TEXT = "Текст"
LABEL_ADV_HAS_ATTACHMENT = "С файлами"
LABEL_ADV_HAS_ATTACHMENTS = "С файлами"
LABEL_ADV_FLAGGED = "Помеченные"
LABEL_ADV_UNSEEN = "Непрочитанные"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Выберите контакт для просмотра."
LABEL_DISPLAY_NAME = "Полное имя"
LABEL_EMAIL = "Электронная почта"
LABEL_PHONE = "Телефон"
LABEL_WEB = "Сайт"
LABEL_BIRTHDAY = "День рождения"
LINK_ADD_EMAIL = "Добавьте адрес электронной почты"
LINK_ADD_PHONE = "Добавьте телефон"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Введите полное имя"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Поделиться"
BUTTON_SHARE_NONE = "Отменить"
BUTTON_SHARE_ALL = "Всем"
BUTTON_SYNC = "Синхронизация (CardDAV)"
[COMPOSE]
TITLE_FROM = "От"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Блокировано браузер
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Контакты"
LEGEND_MOBILE_SYNC = "Мобильная Синхронизация"
LABEL_SYNC_SERVER = "Сервер"
LABEL_SYNC_USERNAME = "Пользователь"
LABEL_SYNC_PASSWORD = "Пароль"
LINK_HIDE = "спрятать"
LINK_SHOW = "показать"
LABEL_DESC_PAB = "Персональная адресная книга"
DESC_FULL_PAB_URL = "Если вашему приложению (например как Mozilla Thunderbird (SOGo Connector Thunderbird extension) или Evolution) необходим полный путь к CardDAV адресной книге, используйте URL ниже."
LABEL_CONTACTS_AUTOSAVE = "Автоматически добавлять получателей писем в адресную книгу"
LEGEND_CONTACTS_SYNC = "Синхронизация (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Включить синхронизация"
LABEL_CONTACTS_SYNC_SERVER = "Сервер"
LABEL_CONTACTS_SYNC_AB_URL = "Адресная книга (URL)"
LABEL_CONTACTS_SYNC_USER = "Пользователь"
LABEL_CONTACTS_SYNC_PASSWORD = "Пароль"
[SETTINGS_THEMES]
LEGEND_THEMES = "Темы Оформления"
@ -538,6 +540,7 @@ CURRENT_PASSWORD_INCORRECT = "Текущий пароль неверный"
NEW_PASSWORD_SHORT = "Пароль слишком короткий"
NEW_PASSWORD_WEAK = "Пароль слишком простой"
NEW_PASSWORD_FORBIDDENT = "Пароль содержит запрещенные символы"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Не могу получить список писем"
CANT_GET_MESSAGE = "Не могу получить письмо"
CANT_DELETE_MESSAGE = "Не могу удалить письмо"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "Odosielateľ"
LABEL_ADV_TO = "Príjemca"
LABEL_ADV_SUBJECT = "Predmet"
LABEL_ADV_TEXT = "Text"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "S prílohami"
LABEL_ADV_FLAGGED = "Označené hviezdičkou"
LABEL_ADV_UNSEEN = "Neprečítané"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "Zvoľte kontakt zo zoznamu pre jeho zobrazenie tu."
LABEL_DISPLAY_NAME = "Zobraziť ako"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Telefón"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Pridať emailovú adresu"
LINK_ADD_PHONE = "Pridať telefón"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Zadajte Zobrazované meno"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Len pre čítanie"
LABEL_SHARE = "Zdielať"
BUTTON_SHARE_NONE = "Žiadne"
BUTTON_SHARE_ALL = "Všetko"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "Odosielateľ"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(Zablokované prehliadačom)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Kontakty"
LEGEND_MOBILE_SYNC = "Mobilná synchronizácia"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "Používateľské meno"
LABEL_SYNC_PASSWORD = "Heslo"
LINK_HIDE = "skryť"
LINK_SHOW = "zobraziť"
LABEL_DESC_PAB = "Osobný adresár"
DESC_FULL_PAB_URL = "Ak vaša aplikácia (ako napríklad Mozilla Thunderbird (Rozšírenie SOGo Connector Thunderbird) alebo Evolution) vyžaduje úplnú cestu ku CardDAV Adresáru, použite nižšie uvedenú adresu."
LABEL_CONTACTS_AUTOSAVE = "Automaticky pridávať príjemcov správ do Adresára"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "Motívy"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "Chyba pri vytváraní zoznamu správ."
CANT_GET_MESSAGE = "Správu sa nepodarilo načítať"
CANT_DELETE_MESSAGE = "Správu sa nepodarilo odstrániť"

View file

@ -22,6 +22,7 @@ LABEL_ADV_FROM = "发送自"
LABEL_ADV_TO = "发送到"
LABEL_ADV_SUBJECT = "主题"
LABEL_ADV_TEXT = "内容"
LABEL_ADV_HAS_ATTACHMENT = "Has attachment"
LABEL_ADV_HAS_ATTACHMENTS = "有附件"
LABEL_ADV_FLAGGED = "Flagged"
LABEL_ADV_UNSEEN = "Unseen"
@ -161,6 +162,8 @@ CONTACT_VIEW_DESC = "在此查看在列表中选中的联系人。"
LABEL_DISPLAY_NAME = "Display name"
LABEL_EMAIL = "Email"
LABEL_PHONE = "Phone"
LABEL_WEB = "Web"
LABEL_BIRTHDAY = "Birthday"
LINK_ADD_EMAIL = "Add an email address"
LINK_ADD_PHONE = "Add a phone"
PLACEHOLDER_ENTER_DISPLAY_NAME = "Enter display name"
@ -170,6 +173,7 @@ LABEL_READ_ONLY = "Read only"
LABEL_SHARE = "Share"
BUTTON_SHARE_NONE = "None"
BUTTON_SHARE_ALL = "Everyone"
BUTTON_SYNC = "Synchronization (CardDAV)"
[COMPOSE]
TITLE_FROM = "发送自"
@ -383,15 +387,13 @@ LABEL_CHROME_NOTIFICATION_DESC_DENIED = "(被浏览器阻止)"
[SETTINGS_CONTACTS]
LEGEND_CONTACTS = "Contacts"
LEGEND_MOBILE_SYNC = "Mobile Sync"
LABEL_SYNC_SERVER = "Server"
LABEL_SYNC_USERNAME = "User Name"
LABEL_SYNC_PASSWORD = "Password"
LINK_HIDE = "hide"
LINK_SHOW = "show"
LABEL_DESC_PAB = "Pesonal Address Book"
DESC_FULL_PAB_URL = "If your application (such as Mozilla Thunderbird (SOGo Connector Thunderbird extension) or Evolution) requires full path to the CardDAV address book, use the URL below."
LABEL_CONTACTS_AUTOSAVE = "Automatically add recipients to your address book"
LEGEND_CONTACTS_SYNC = "Remote Synchronization (CardDAV)"
LABEL_CONTACTS_SYNC_ENABLE = "Enable remote synchronization"
LABEL_CONTACTS_SYNC_SERVER = "Server"
LABEL_CONTACTS_SYNC_AB_URL = "Addressbook URL"
LABEL_CONTACTS_SYNC_USER = "User"
LABEL_CONTACTS_SYNC_PASSWORD = "Password"
[SETTINGS_THEMES]
LEGEND_THEMES = "主题"
@ -537,6 +539,7 @@ CURRENT_PASSWORD_INCORRECT = "Current password incorrect"
NEW_PASSWORD_SHORT = "Password is too short"
NEW_PASSWORD_WEAK = "Password is too easy"
NEW_PASSWORD_FORBIDDENT = "Password contains forbidden characters"
CONTACTS_SYNC_ERROR = "Contacts synchronization error"
CANT_GET_MESSAGE_LIST = "无法获取邮件列表"
CANT_GET_MESSAGE = "无法获取邮件"
CANT_DELETE_MESSAGE = "无法删除邮件"

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 {
@ -1483,7 +1483,22 @@ table {
.icon-filter:before {
content: "\e063";
}
.icon-resize:before {
content: "\e064";
}
.icon-sync:before {
content: "\e065";
}
.icon-ellipsis-alt:before {
content: "\e066";
}
.icon-cloud-up:before {
content: "\e067";
}
.icon-cloud-down:before {
content: "\e068";
}
/** initial setup **/
.nano {
/*
@ -1600,7 +1615,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;
@ -1965,7 +1980,7 @@ img.mfp-img {
right: 0;
padding-top: 0; }
/* overlay at start */
.mfp-fade.mfp-bg {
@ -2011,7 +2026,7 @@ img.mfp-img {
-moz-transform: translateX(50px);
transform: translateX(50px);
}
.simple-pace {
-webkit-pointer-events: none;
pointer-events: none;
@ -2082,7 +2097,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;
@ -2150,7 +2165,7 @@ img.mfp-img {
box-shadow:none;
}
.inputosaurus-input-hidden { display:none; }
.flag-wrapper {
width: 24px;
height: 16px;
@ -2194,7 +2209,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;

File diff suppressed because one or more lines are too long

View file

@ -107,4 +107,9 @@
<glyph unicode="&#57441;" d="M456 343c0 1 0 1 0 1 0 5-2 10-5 13l0 1-40 69c-1 6-6 10-13 10 0 0 0 0-1 0l0 0-283 0 0 0c0 0 0 0 0 0-5 0-9-3-12-7l0 0-42-72 0 0c-3-4-4-9-4-14 0 0 0 0 0-1l0-247c0 0 0 0 0 0 0-12 9-21 20-21 1 0 1 0 1 0l358 0c0 0 0 0 0 0 12 0 21 9 21 21 0 0 0 0 0 0l0 247z m-131-125l-64-90c-1-1-3-2-5-2 0 0 0 0 0 0-2 0-4 1-5 2l-64 90c-1 2-1 4 0 6 1 2 3 3 5 3l30 0 0 81c0 3 3 6 6 6l56 0c3 0 6-3 6-6l0-81 30 0c2 0 4-1 5-3 1-2 1-4 0-6z m-231 147l27 47 270 0 27-47z"/>
<glyph unicode="&#57442;" d="M96 288l64 0 0-32-64 0z m0-64l64 0 0-32-64 0z m384 160l-448 0c-18 0-32-14-32-32l0-224c0-18 14-32 32-32l448 0c18 0 32 14 32 32l0 224c0 18-14 32-32 32m-288-224l-128 0 0 160 128 0z m128 0l-96 0 0 160 32 0 0-128 32 0 0 128 32 0z m128 64l-32 0 0-64-32 0 0 64-32 0 0 96 32 0 0-64 32 0 0 64 32 0z"/>
<glyph unicode="&#57443;" d="M456 428c3-8 2-15-4-20l-141-141 0-212c0-8-4-14-11-17-3-1-5-1-7-1-6 0-10 1-13 5l-73 73c-4 4-6 8-6 13l0 139-141 141c-6 5-7 12-4 20 4 7 9 11 17 11l366 0c8 0 13-4 17-11z"/>
<glyph unicode="&#57444;" d="M148 367l-111-111 111-111 0 74 216 0 0-74 111 111-111 111 0-74-216 0z"/>
<glyph unicode="&#57445;" d="M341 94c0-2-1-4-2-6-2-2-4-3-6-3l-256 0c-2 0-3 1-4 1-1 0-2 1-2 2-1 1-1 1-2 2 0 0 0 1 0 3-1 1-1 2-1 3l0 160-51 0c-5 0-9 2-12 5-3 3-5 7-5 12 0 4 1 8 4 11l85 102c4 4 8 6 13 6 6 0 10-2 13-6l86-102c2-3 4-7 4-11 0-5-2-9-5-12-4-3-8-5-12-5l-51 0 0-102 153 0c3 0 5-1 7-3l42-52c2-1 2-3 2-5z m171 111c0-4-1-8-4-11l-85-103c-4-4-8-6-13-6-6 0-10 2-13 6l-86 103c-2 3-4 7-4 11 0 4 2 8 5 12 4 3 8 5 12 5l51 0 0 102-153 0c-3 0-5 1-7 3l-42 52c-2 1-2 3-2 5 0 2 1 4 2 6 2 2 4 3 6 3l256 0c2 0 3-1 4-1 1 0 2-1 2-2 1-1 1-1 2-2 0-1 0-2 0-3 1-2 1-3 1-3l0-160 51 0c5 0 9-2 12-5 3-4 5-8 5-12z"/>
<glyph unicode="&#57446;" d="M311 155l0-54c0-8-3-15-8-20-5-5-12-8-20-8l-54 0c-8 0-15 3-20 8-5 5-8 12-8 20l0 54c0 8 3 15 8 20 5 5 12 8 20 8l54 0c8 0 15-3 20-8 5-5 8-12 8-20z m0 147l0-55c0-8-3-14-8-20-5-5-12-8-20-8l-54 0c-8 0-15 3-20 8-5 6-8 12-8 20l0 55c0 7 3 14 8 19 5 5 12 8 20 8l54 0c8 0 15-3 20-8 5-5 8-12 8-19z m0 146l0-55c0-7-3-14-8-19-5-6-12-8-20-8l-54 0c-8 0-15 2-20 8-5 5-8 12-8 19l0 55c0 8 3 14 8 19 5 6 12 8 20 8l54 0c8 0 15-2 20-8 5-5 8-11 8-19z"/>
<glyph unicode="&#57447;" d="M341 265c0 2 0 4-2 6l-94 94c-2 1-4 2-6 2-3 0-5-1-6-2l-94-94c-2-2-2-4-2-6 0-3 0-5 2-7 2-1 4-2 6-2l60 0 0-94c0-2 1-4 2-6 2-2 4-2 6-2l52 0c2 0 4 0 6 2 1 2 2 4 2 6l0 94 60 0c2 0 4 1 6 3 1 1 2 3 2 6z m171-77c0-29-10-53-30-73-20-20-44-30-72-30l-291 0c-32 0-61 12-84 35-23 24-35 52-35 85 0 23 6 44 19 64 12 19 29 34 50 44-1 5-1 9-1 11 0 38 14 70 40 97 27 26 59 40 97 40 28 0 53-8 76-23 23-16 40-36 50-62 13 11 28 17 44 17 19 0 35-7 49-20 13-14 20-30 20-49 0-13-4-26-11-37 23-5 42-17 57-36 15-18 22-39 22-63z"/>
<glyph unicode="&#57448;" d="M341 247c0 3 0 5-2 7-2 1-4 2-6 2l-60 0 0 94c0 2-1 4-2 6-2 2-4 2-6 2l-52 0c-2 0-4 0-6-2-1-2-2-4-2-6l0-94-60 0c-2 0-4-1-6-3-2-1-2-3-2-6 0-2 0-4 2-6l94-94c1-1 3-2 6-2 2 0 4 1 6 2l94 94c1 2 2 4 2 6z m171-59c0-29-10-53-30-73-20-20-44-30-72-30l-291 0c-32 0-61 12-84 35-23 24-35 52-35 85 0 23 6 44 19 64 12 19 29 34 50 44-1 5-1 9-1 11 0 38 14 70 40 97 27 26 59 40 97 40 28 0 53-8 76-23 23-16 40-36 50-62 13 11 28 17 44 17 19 0 35-7 49-20 13-14 20-30 20-49 0-13-4-26-11-37 23-5 42-17 57-36 15-18 22-39 22-63z"/>
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 40 KiB

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';
@ -75,14 +75,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 {?}
*/
@ -231,7 +231,7 @@ if (Globals.bAllowPdfPreview && navigator && navigator.mimeTypes)
return oType && 'application/pdf' === oType.type;
});
}
Consts.Defaults = {};
Consts.Values = {};
Consts.DataImages = {};
@ -246,7 +246,7 @@ Consts.Defaults.MessagesPerPage = 20;
* @const
* @type {number}
*/
Consts.Defaults.ContactsPerPage = 20;
Consts.Defaults.ContactsPerPage = 50;
/**
* @const
@ -278,6 +278,12 @@ Consts.Defaults.SendMessageAjaxTimeout = 300000;
*/
Consts.Defaults.SaveMessageAjaxTimeout = 200000;
/**
* @const
* @type {number}
*/
Consts.Defaults.ContactsSyncAjaxTimeout = 200000;
/**
* @const
* @type {string}
@ -349,7 +355,7 @@ Consts.DataImages.UserDotPic = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA
* @type {string}
*/
Consts.DataImages.TranspPic = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=';
/**
* @enum {string}
*/
@ -613,14 +619,6 @@ Enums.Layout = {
'BottomPreview': 2
};
/**
* @enum {number}
*/
Enums.ContactScopeType = {
'Default': 0,
'ShareAll': 2
};
/**
* @enum {number}
*/
@ -650,17 +648,9 @@ Enums.ContactPropertyType = {
'NamePrefix': 20,
'NameSuffix': 21,
'EmailPersonal': 30,
'EmailBussines': 31,
'PhonePersonal': 50,
'PhoneBussines': 51,
'MobilePersonal': 60,
'MobileBussines': 61,
'FaxPesonal': 70,
'FaxBussines': 71,
'Email': 30,
'Phone': 31,
'Web': 32,
'Facebook': 90,
'Skype': 91,
@ -694,6 +684,8 @@ Enums.Notification = {
'NewPasswordShort': 132,
'NewPasswordWeak': 133,
'NewPasswordForbidden': 134,
'ContactsSyncError': 140,
'CantGetMessageList': 201,
'CantGetMessage': 202,
@ -735,7 +727,7 @@ Enums.Notification = {
'UnknownNotification': 999,
'UnknownError': 999
};
Utils.trim = $.trim;
Utils.inArray = $.inArray;
Utils.isArray = _.isArray;
@ -1313,6 +1305,8 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.NewPasswordWeak] = Utils.i18n('NOTIFICATIONS/NEW_PASSWORD_WEAK');
NotificationI18N[Enums.Notification.NewPasswordForbidden] = Utils.i18n('NOTIFICATIONS/NEW_PASSWORD_FORBIDDENT');
NotificationI18N[Enums.Notification.ContactsSyncError] = Utils.i18n('NOTIFICATIONS/CONTACTS_SYNC_ERROR');
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
@ -2098,6 +2092,15 @@ Utils.fakeMd5 = function(iLen)
return sResult;
};
/* jshint ignore:start */
/**
* @param {string} s
* @return {string}
*/
Utils.md5 = function(s){function L(k,d){return(k<<d)|(k>>>(32-d))}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H)}if(I|d){if(x&1073741824){return(x^3221225472^F^H)}else{return(x^1073741824^F^H)}}else{return(x^F^H)}}function r(d,F,k){return(d&F)|((~d)&k)}function q(d,F,k){return(d&k)|(F&(~k))}function p(d,F,k){return(d^F^k)}function n(d,F,k){return(F^(d|(~k)))}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F)}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F)}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F)}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F)}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H<F){Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=(aa[Z]|(G.charCodeAt(H)<<d));H++}Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=aa[Z]|(128<<d);aa[I-2]=F<<3;aa[I-1]=F>>>29;return aa}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2)}return k}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F<k.length;F++){var x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x)}else{if((x>127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128)}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128)}}}return d}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g)}var i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase()};
/* jshint ignore:end */
Utils.convertPlainTextToHtml = function (sPlain)
{
return sPlain.toString()
@ -2112,11 +2115,12 @@ Utils.draggeblePlace = function ()
Utils.defautOptionsAfterRender = function (oOption, oItem)
{
if (oItem && !Utils.isUnd(oItem.disabled))
if (oItem && !Utils.isUnd(oItem.disabled) && oOption)
{
ko.applyBindingsToNode(oOption, {
'disabled': oItem.disabled
}, oItem);
$(oOption)
.toggleClass('disabled', oItem.disabled)
.prop('disabled', oItem.disabled)
;
}
};
@ -2511,7 +2515,7 @@ Utils.detectDropdownVisibility = _.debounce(function () {
Globals.dropdownVisibility(!!_.find(BootstrapDropdowns, function (oItem) {
return oItem.hasClass('open');
}));
}, 50);
}, 50);
// Base64 encode / decode
// http://www.webtoolkit.info/
@ -2674,7 +2678,7 @@ Base64 = {
}
};
/*jslint bitwise: false*/
/*jslint bitwise: false*/
ko.bindingHandlers.tooltip = {
'init': function (oElement, fValueAccessor) {
if (!Globals.bMobileDevice)
@ -3381,7 +3385,7 @@ ko.observable.fn.validateFunc = function (fFunc)
return this;
};
/**
* @constructor
*/
@ -3679,7 +3683,7 @@ LinkBuilder.prototype.socialFacebook = function ()
{
return this.sServer + 'SocialFacebook' + ('' !== this.sSpecSuffix ? '/' + this.sSpecSuffix + '/' : '');
};
/**
* @type {Object}
*/
@ -3773,7 +3777,7 @@ Plugins.settingsGet = function (sPluginSection, sName)
};
/**
* @constructor
*/
@ -3847,7 +3851,7 @@ CookieDriver.prototype.get = function (sKey)
return mResult;
};
/**
* @constructor
*/
@ -3918,7 +3922,7 @@ LocalStorageDriver.prototype.get = function (sKey)
return mResult;
};
/**
* @constructor
*/
@ -3961,7 +3965,7 @@ LocalStorage.prototype.get = function (iKey)
{
return this.oDriver ? this.oDriver.get('p' + iKey) : null;
};
/**
* @constructor
*/
@ -3974,7 +3978,7 @@ KnoinAbstractBoot.prototype.bootstart = function ()
{
};
/**
* @param {string=} sPosition = ''
* @param {string=} sTemplate = ''
@ -4060,7 +4064,7 @@ KnoinAbstractViewModel.prototype.registerPopupEscapeKey = function ()
return true;
});
};
/**
* @param {string} sScreenName
* @param {?=} aViewModels = []
@ -4136,7 +4140,7 @@ KnoinAbstractScreen.prototype.__start = function ()
this.oCross = oRoute;
}
};
/**
* @constructor
*/
@ -4534,7 +4538,7 @@ Knoin.prototype.bootstart = function ()
};
kn = new Knoin();
/**
* @param {string=} sEmail
* @param {string=} sName
@ -4898,7 +4902,7 @@ EmailModel.prototype.inputoTagLine = function ()
{
return 0 < this.name.length ? this.name + ' (' + this.email + ')' : this.email;
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5116,7 +5120,7 @@ PopupsDomainViewModel.prototype.clearForm = function ()
this.smtpAuth(true);
this.whiteList('');
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5253,7 +5257,7 @@ PopupsPluginViewModel.prototype.onBuild = function ()
}
}, this));
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5369,7 +5373,7 @@ PopupsActivateViewModel.prototype.validateSubscriptionKey = function ()
{
var sValue = this.key();
return '' === sValue || !!/^RL[\d]+-[A-Z0-9\-]+Z$/.test(Utils.trim(sValue));
};
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5429,7 +5433,7 @@ PopupsLanguagesViewModel.prototype.changeLanguage = function (sLang)
RL.data().mainLanguage(sLang);
this.cancelCommand();
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5535,7 +5539,7 @@ PopupsAskViewModel.prototype.onBuild = function ()
}, this));
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5622,7 +5626,7 @@ AdminLoginViewModel.prototype.onHide = function ()
{
this.loginFocus(false);
};
/**
* @param {?} oScreen
*
@ -5644,7 +5648,7 @@ AdminMenuViewModel.prototype.link = function (sRoute)
{
return '#/' + sRoute;
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -5666,7 +5670,7 @@ AdminPaneViewModel.prototype.logoutClick = function ()
RL.remote().adminLogout(function () {
RL.loginAndLogoutReload();
});
};
};
/**
* @constructor
*/
@ -5766,7 +5770,7 @@ AdminGeneral.prototype.selectLanguage = function ()
{
kn.showScreenPopup(PopupsLanguagesViewModel);
};
/**
* @constructor
*/
@ -5818,7 +5822,7 @@ AdminLogin.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
@ -5887,7 +5891,7 @@ AdminBranding.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
@ -6078,7 +6082,7 @@ AdminContacts.prototype.onBuild = function ()
'ContactsSync': bValue ? '1' : '0'
});
});
self.contactsType.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f5, {
'ContactsPdoType': sValue
@ -6107,7 +6111,7 @@ AdminContacts.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
@ -6196,7 +6200,7 @@ AdminDomains.prototype.onDomainListChangeRequest = function ()
{
RL.reloadDomainList();
};
/**
* @constructor
*/
@ -6291,7 +6295,7 @@ AdminSecurity.prototype.phpInfoLink = function ()
{
return RL.link().phpInfo();
};
/**
* @constructor
*/
@ -6407,7 +6411,7 @@ AdminSocial.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
@ -6504,7 +6508,7 @@ AdminPlugins.prototype.onPluginDisableRequest = function (sResult, oData)
RL.reloadPluginList();
};
/**
* @constructor
*/
@ -6602,7 +6606,7 @@ AdminPackages.prototype.installPackage = function (oPackage)
RL.remote().packageInstall(this.requestHelper(oPackage, true), oPackage);
}
};
/**
* @constructor
*/
@ -6653,7 +6657,7 @@ AdminLicensing.prototype.licenseExpiredMomentValue = function ()
{
var oDate = moment.unix(this.licenseExpired());
return oDate.format('LL') + ' (' + oDate.from(moment()) + ')';
};
};
/**
* @constructor
*/
@ -6778,7 +6782,7 @@ AbstractData.prototype.populateDataOnStart = function()
this.contactsIsAllowed(!!RL.settingsGet('ContactsIsAllowed'));
};
/**
* @constructor
* @extends AbstractData
@ -6812,7 +6816,7 @@ _.extend(AdminDataStorage.prototype, AbstractData.prototype);
AdminDataStorage.prototype.populateDataOnStart = function()
{
AbstractData.prototype.populateDataOnStart.call(this);
};
};
/**
* @constructor
*/
@ -7086,7 +7090,7 @@ AbstractAjaxRemoteStorage.prototype.jsVersion = function (fCallback, sVersion)
'Version': sVersion
});
};
/**
* @constructor
* @extends AbstractAjaxRemoteStorage
@ -7330,7 +7334,7 @@ AdminAjaxRemoteStorage.prototype.adminPing = function (fCallback)
{
this.defaultRequest(fCallback, 'AdminPing');
};
/**
* @constructor
*/
@ -7339,6 +7343,7 @@ function AbstractCacheStorage()
this.oEmailsPicsHashes = {};
this.oServices = {};
}
/**
* @type {Object}
*/
@ -7359,26 +7364,36 @@ AbstractCacheStorage.prototype.clear = function ()
* @param {string} sEmail
* @return {string}
*/
AbstractCacheStorage.prototype.getUserPic = function (sEmail)
AbstractCacheStorage.prototype.getUserPic = function (sEmail, fCallback)
{
sEmail = Utils.trim(sEmail);
var
sUrl = '',
sService = '',
sEmailLower = sEmail.toLowerCase(),
sPicHash = Utils.isUnd(this.oEmailsPicsHashes[sEmail]) ? '' : this.oEmailsPicsHashes[sEmail]
sPicHash = Utils.isUnd(this.oEmailsPicsHashes[sEmailLower]) ? '' : this.oEmailsPicsHashes[sEmailLower]
;
if ('' === sPicHash)
if ('' !== sPicHash)
{
sUrl = RL.link().getUserPicUrlFromHash(sPicHash);
}
else
{
sService = sEmailLower.substr(sEmail.indexOf('@') + 1);
sUrl = '' !== sService && this.oServices[sService] ? this.oServices[sService] : '';
}
else
{
sUrl = RL.link().getUserPicUrlFromHash(sPicHash);
}
return sUrl;
// if ('' === sUrl) // Gravatar // TODO
// {
// fCallback('//secure.gravatar.com/avatar/' + Utils.md5(sEmailLower) + '.jpg?s=80&d=mm', sEmail);
// }
// else
// {
fCallback(sUrl, sEmail);
// }
};
/**
@ -7396,7 +7411,7 @@ AbstractCacheStorage.prototype.setEmailsPicsHashesData = function (oData)
{
this.oEmailsPicsHashes = oData;
};
/**
* @constructor
* @extends AbstractCacheStorage
@ -7407,7 +7422,7 @@ function AdminCacheStorage()
}
_.extend(AdminCacheStorage.prototype, AbstractCacheStorage.prototype);
/**
* @param {Array} aViewModels
* @constructor
@ -7585,7 +7600,7 @@ AbstractSettings.prototype.routes = function ()
['', oRules]
];
};
/**
* @constructor
* @extends KnoinAbstractScreen
@ -7600,7 +7615,7 @@ _.extend(AdminLoginScreen.prototype, KnoinAbstractScreen.prototype);
AdminLoginScreen.prototype.onShow = function ()
{
RL.setTitle('');
};
};
/**
* @constructor
* @extends AbstractSettings
@ -7620,7 +7635,7 @@ AdminSettingsScreen.prototype.onShow = function ()
// AbstractSettings.prototype.onShow.call(this);
RL.setTitle('');
};
};
/**
* @constructor
* @extends KnoinAbstractBoot
@ -7940,7 +7955,7 @@ AbstractApp.prototype.bootstart = function ()
ssm.ready();
};
/**
* @constructor
* @extends AbstractApp
@ -8179,7 +8194,7 @@ AdminApp.prototype.bootstart = function ()
* @type {AdminApp}
*/
RL = new AdminApp();
$html.addClass(Globals.bMobileDevice ? 'mobile' : 'no-mobile');
$window.keydown(Utils.killCtrlAandS).keyup(Utils.killCtrlAandS);
@ -8230,9 +8245,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

Binary file not shown.

View file

@ -107,4 +107,9 @@
<glyph unicode="&#57441;" d="M456 343c0 1 0 1 0 1 0 5-2 10-5 13l0 1-40 69c-1 6-6 10-13 10 0 0 0 0-1 0l0 0-283 0 0 0c0 0 0 0 0 0-5 0-9-3-12-7l0 0-42-72 0 0c-3-4-4-9-4-14 0 0 0 0 0-1l0-247c0 0 0 0 0 0 0-12 9-21 20-21 1 0 1 0 1 0l358 0c0 0 0 0 0 0 12 0 21 9 21 21 0 0 0 0 0 0l0 247z m-131-125l-64-90c-1-1-3-2-5-2 0 0 0 0 0 0-2 0-4 1-5 2l-64 90c-1 2-1 4 0 6 1 2 3 3 5 3l30 0 0 81c0 3 3 6 6 6l56 0c3 0 6-3 6-6l0-81 30 0c2 0 4-1 5-3 1-2 1-4 0-6z m-231 147l27 47 270 0 27-47z"/>
<glyph unicode="&#57442;" d="M96 288l64 0 0-32-64 0z m0-64l64 0 0-32-64 0z m384 160l-448 0c-18 0-32-14-32-32l0-224c0-18 14-32 32-32l448 0c18 0 32 14 32 32l0 224c0 18-14 32-32 32m-288-224l-128 0 0 160 128 0z m128 0l-96 0 0 160 32 0 0-128 32 0 0 128 32 0z m128 64l-32 0 0-64-32 0 0 64-32 0 0 96 32 0 0-64 32 0 0 64 32 0z"/>
<glyph unicode="&#57443;" d="M456 428c3-8 2-15-4-20l-141-141 0-212c0-8-4-14-11-17-3-1-5-1-7-1-6 0-10 1-13 5l-73 73c-4 4-6 8-6 13l0 139-141 141c-6 5-7 12-4 20 4 7 9 11 17 11l366 0c8 0 13-4 17-11z"/>
<glyph unicode="&#57444;" d="M148 367l-111-111 111-111 0 74 216 0 0-74 111 111-111 111 0-74-216 0z"/>
<glyph unicode="&#57445;" d="M341 94c0-2-1-4-2-6-2-2-4-3-6-3l-256 0c-2 0-3 1-4 1-1 0-2 1-2 2-1 1-1 1-2 2 0 0 0 1 0 3-1 1-1 2-1 3l0 160-51 0c-5 0-9 2-12 5-3 3-5 7-5 12 0 4 1 8 4 11l85 102c4 4 8 6 13 6 6 0 10-2 13-6l86-102c2-3 4-7 4-11 0-5-2-9-5-12-4-3-8-5-12-5l-51 0 0-102 153 0c3 0 5-1 7-3l42-52c2-1 2-3 2-5z m171 111c0-4-1-8-4-11l-85-103c-4-4-8-6-13-6-6 0-10 2-13 6l-86 103c-2 3-4 7-4 11 0 4 2 8 5 12 4 3 8 5 12 5l51 0 0 102-153 0c-3 0-5 1-7 3l-42 52c-2 1-2 3-2 5 0 2 1 4 2 6 2 2 4 3 6 3l256 0c2 0 3-1 4-1 1 0 2-1 2-2 1-1 1-1 2-2 0-1 0-2 0-3 1-2 1-3 1-3l0-160 51 0c5 0 9-2 12-5 3-4 5-8 5-12z"/>
<glyph unicode="&#57446;" d="M311 155l0-54c0-8-3-15-8-20-5-5-12-8-20-8l-54 0c-8 0-15 3-20 8-5 5-8 12-8 20l0 54c0 8 3 15 8 20 5 5 12 8 20 8l54 0c8 0 15-3 20-8 5-5 8-12 8-20z m0 147l0-55c0-8-3-14-8-20-5-5-12-8-20-8l-54 0c-8 0-15 3-20 8-5 6-8 12-8 20l0 55c0 7 3 14 8 19 5 5 12 8 20 8l54 0c8 0 15-3 20-8 5-5 8-12 8-19z m0 146l0-55c0-7-3-14-8-19-5-6-12-8-20-8l-54 0c-8 0-15 2-20 8-5 5-8 12-8 19l0 55c0 8 3 14 8 19 5 6 12 8 20 8l54 0c8 0 15-2 20-8 5-5 8-11 8-19z"/>
<glyph unicode="&#57447;" d="M341 265c0 2 0 4-2 6l-94 94c-2 1-4 2-6 2-3 0-5-1-6-2l-94-94c-2-2-2-4-2-6 0-3 0-5 2-7 2-1 4-2 6-2l60 0 0-94c0-2 1-4 2-6 2-2 4-2 6-2l52 0c2 0 4 0 6 2 1 2 2 4 2 6l0 94 60 0c2 0 4 1 6 3 1 1 2 3 2 6z m171-77c0-29-10-53-30-73-20-20-44-30-72-30l-291 0c-32 0-61 12-84 35-23 24-35 52-35 85 0 23 6 44 19 64 12 19 29 34 50 44-1 5-1 9-1 11 0 38 14 70 40 97 27 26 59 40 97 40 28 0 53-8 76-23 23-16 40-36 50-62 13 11 28 17 44 17 19 0 35-7 49-20 13-14 20-30 20-49 0-13-4-26-11-37 23-5 42-17 57-36 15-18 22-39 22-63z"/>
<glyph unicode="&#57448;" d="M341 247c0 3 0 5-2 7-2 1-4 2-6 2l-60 0 0 94c0 2-1 4-2 6-2 2-4 2-6 2l-52 0c-2 0-4 0-6-2-1-2-2-4-2-6l0-94-60 0c-2 0-4-1-6-3-2-1-2-3-2-6 0-2 0-4 2-6l94-94c1-1 3-2 6-2 2 0 4 1 6 2l94 94c1 2 2 4 2 6z m171-59c0-29-10-53-30-73-20-20-44-30-72-30l-291 0c-32 0-61 12-84 35-23 24-35 52-35 85 0 23 6 44 19 64 12 19 29 34 50 44-1 5-1 9-1 11 0 38 14 70 40 97 27 26 59 40 97 40 28 0 53-8 76-23 23-16 40-36 50-62 13 11 28 17 44 17 19 0 35-7 49-20 13-14 20-30 20-49 0-13-4-26-11-37 23-5 42-17 57-36 15-18 22-39 22-63z"/>
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Binary file not shown.

View file

@ -445,6 +445,26 @@ h2{font-size:18px;padding:0 0 21px 5px;margin:45px 0 0 0;text-transform:uppercas
<div data-icon="&#xe063;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe063;">
</li>
<li>
<div data-icon="&#xe064;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe064;">
</li>
<li>
<div data-icon="&#xe065;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe065;">
</li>
<li>
<div data-icon="&#xe066;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe066;">
</li>
<li>
<div data-icon="&#xe067;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe067;">
</li>
<li>
<div data-icon="&#xe068;" class="icon"></div>
<input type="text" readonly="readonly" value="&amp;#xe068;">
</li>
</ul>
<h2>CSS mapping</h2>
<ul class="glyphs css-mapping">
@ -848,6 +868,26 @@ h2{font-size:18px;padding:0 0 21px 5px;margin:45px 0 0 0;text-transform:uppercas
<div class="icon icon-filter"></div>
<input type="text" readonly="readonly" value="filter">
</li>
<li>
<div class="icon icon-resize"></div>
<input type="text" readonly="readonly" value="resize">
</li>
<li>
<div class="icon icon-sync"></div>
<input type="text" readonly="readonly" value="sync">
</li>
<li>
<div class="icon icon-ellipsis-alt"></div>
<input type="text" readonly="readonly" value="ellipsis-alt">
</li>
<li>
<div class="icon icon-cloud-up"></div>
<input type="text" readonly="readonly" value="cloud-up">
</li>
<li>
<div class="icon icon-cloud-down"></div>
<input type="text" readonly="readonly" value="cloud-down">
</li>
</ul>
</div><script type="text/javascript">
(function() {

View file

@ -338,3 +338,18 @@
.icon-filter:before {
content: "\e063";
}
.icon-resize:before {
content: "\e064";
}
.icon-sync:before {
content: "\e065";
}
.icon-ellipsis-alt:before {
content: "\e066";
}
.icon-cloud-up:before {
content: "\e067";
}
.icon-cloud-down:before {
content: "\e068";
}