CardDAV second look :)

This commit is contained in:
RainLoop Team 2013-12-20 03:28:03 +04:00
parent 7648000521
commit a46d6832d9
20 changed files with 852 additions and 468 deletions

View file

@ -10,6 +10,7 @@ function AdminContacts()
this.defautOptionsAfterRender = Utils.defautOptionsAfterRender; this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
this.enableContacts = ko.observable(!!RL.settingsGet('ContactsEnable')); this.enableContacts = ko.observable(!!RL.settingsGet('ContactsEnable'));
this.contactsSharing = ko.observable(!!RL.settingsGet('ContactsSharing')); this.contactsSharing = ko.observable(!!RL.settingsGet('ContactsSharing'));
this.contactsSync = ko.observable(!!RL.settingsGet('ContactsSync'));
var var
aTypes = ['sqlite', 'mysql', 'pgsql'], aTypes = ['sqlite', 'mysql', 'pgsql'],
@ -183,6 +184,12 @@ AdminContacts.prototype.onBuild = function ()
'ContactsSharing': bValue ? '1' : '0' 'ContactsSharing': bValue ? '1' : '0'
}); });
}); });
self.contactsSync.subscribe(function (bValue) {
RL.remote().saveAdminConfig(null, {
'ContactsSync': bValue ? '1' : '0'
});
});
self.contactsType.subscribe(function (sValue) { self.contactsType.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f5, { RL.remote().saveAdminConfig(f5, {

View file

@ -6,6 +6,7 @@
function ContactModel() function ContactModel()
{ {
this.idContact = 0; this.idContact = 0;
this.idContactStr = '';
this.display = ''; this.display = '';
this.properties = []; this.properties = [];
this.readOnly = false; this.readOnly = false;
@ -57,6 +58,7 @@ ContactModel.prototype.parse = function (oItem)
if (oItem && 'Object/Contact' === oItem['@Object']) if (oItem && 'Object/Contact' === oItem['@Object'])
{ {
this.idContact = Utils.pInt(oItem['IdContact']); this.idContact = Utils.pInt(oItem['IdContact']);
this.idContactStr = Utils.pString(oItem['IdContactStr']);
this.display = Utils.pString(oItem['Display']); this.display = Utils.pString(oItem['Display']);
this.readOnly = !!oItem['ReadOnly']; this.readOnly = !!oItem['ReadOnly'];
this.scopeType = Utils.pInt(oItem['ScopeType']); this.scopeType = Utils.pInt(oItem['ScopeType']);

View file

@ -570,11 +570,12 @@ WebMailAjaxRemoteStorage.prototype.contacts = function (fCallback, iOffset, iLim
/** /**
* @param {?Function} fCallback * @param {?Function} fCallback
*/ */
WebMailAjaxRemoteStorage.prototype.contactSave = function (fCallback, sRequestUid, sUid, nScopeType, aProperties) WebMailAjaxRemoteStorage.prototype.contactSave = function (fCallback, sRequestUid, sUid, sUidStr, nScopeType, aProperties)
{ {
this.defaultRequest(fCallback, 'ContactSave', { this.defaultRequest(fCallback, 'ContactSave', {
'RequestUid': sRequestUid, 'RequestUid': sRequestUid,
'Uid': Utils.trim(sUid), 'Uid': Utils.trim(sUid),
'UidStr': Utils.trim(sUidStr),
'ScopeType': nScopeType, 'ScopeType': nScopeType,
'Properties': aProperties 'Properties': aProperties
}); });

View file

@ -48,6 +48,7 @@ function PopupsContactsViewModel()
this.viewClearSearch = ko.observable(false); this.viewClearSearch = ko.observable(false);
this.viewID = ko.observable(''); this.viewID = ko.observable('');
this.viewIDStr = ko.observable('');
this.viewReadOnly = ko.observable(false); this.viewReadOnly = ko.observable(false);
this.viewScopeType = ko.observable(Enums.ContactScopeType.Default); this.viewScopeType = ko.observable(Enums.ContactScopeType.Default);
this.viewProperties = ko.observableArray([]); this.viewProperties = ko.observableArray([]);
@ -251,13 +252,14 @@ function PopupsContactsViewModel()
self.viewID(Utils.pInt(oData.Result.ResultID)); self.viewID(Utils.pInt(oData.Result.ResultID));
} }
if ('' === self.viewIDStr())
{
self.viewIDStr(Utils.pString(oData.Result.ResultIDStr));
}
self.reloadContactList(); self.reloadContactList();
bRes = true; bRes = true;
} }
// else
// {
// // TODO
// }
_.delay(function () { _.delay(function () {
self.viewSaveTrigger(bRes ? Enums.SaveSettingsStep.TrueResult : Enums.SaveSettingsStep.FalseResult); self.viewSaveTrigger(bRes ? Enums.SaveSettingsStep.TrueResult : Enums.SaveSettingsStep.FalseResult);
@ -270,7 +272,7 @@ function PopupsContactsViewModel()
}, 1000); }, 1000);
} }
}, sRequestUid, this.viewID(), this.viewScopeType(), aProperties); }, sRequestUid, this.viewID(), this.viewIDStr(), this.viewScopeType(), aProperties);
}, function () { }, function () {
var var
@ -414,6 +416,7 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
{ {
var var
sId = '', sId = '',
sIdStr = '',
bHasName = false, bHasName = false,
aList = [] aList = []
; ;
@ -427,6 +430,7 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
if (oContact) if (oContact)
{ {
sId = oContact.idContact; sId = oContact.idContact;
sIdStr = oContact.idContactStr;
if (Utils.isNonEmptyArray(oContact.properties)) if (Utils.isNonEmptyArray(oContact.properties))
{ {
@ -454,6 +458,7 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
} }
this.viewID(sId); this.viewID(sId);
this.viewIDStr(sIdStr);
this.viewProperties([]); this.viewProperties([]);
this.viewProperties(aList); this.viewProperties(aList);

View file

@ -995,6 +995,7 @@ class Actions
$aResult['ContactsEnable'] = (bool) $oConfig->Get('contacts', 'enable', false); $aResult['ContactsEnable'] = (bool) $oConfig->Get('contacts', 'enable', false);
$aResult['ContactsSharing'] = (bool) $oConfig->Get('contacts', 'allow_sharing', false); $aResult['ContactsSharing'] = (bool) $oConfig->Get('contacts', 'allow_sharing', false);
$aResult['ContactsSync'] = (bool) $oConfig->Get('contacts', 'allow_carddav_sync', false);
$aResult['ContactsPdoType'] = $this->ValidateContactPdoType(\trim($this->Config()->Get('contacts', 'type', 'sqlite'))); $aResult['ContactsPdoType'] = $this->ValidateContactPdoType(\trim($this->Config()->Get('contacts', 'type', 'sqlite')));
$aResult['ContactsPdoDsn'] = (string) $oConfig->Get('contacts', 'pdo_dsn', ''); $aResult['ContactsPdoDsn'] = (string) $oConfig->Get('contacts', 'pdo_dsn', '');
$aResult['ContactsPdoType'] = (string) $oConfig->Get('contacts', 'type', ''); $aResult['ContactsPdoType'] = (string) $oConfig->Get('contacts', 'type', '');
@ -1875,6 +1876,7 @@ class Actions
$this->setConfigFromParams($oConfig, 'ContactsEnable', 'contacts', 'enable', 'bool'); $this->setConfigFromParams($oConfig, 'ContactsEnable', 'contacts', 'enable', 'bool');
$this->setConfigFromParams($oConfig, 'ContactsSharing', 'contacts', 'allow_sharing', 'bool'); $this->setConfigFromParams($oConfig, 'ContactsSharing', 'contacts', 'allow_sharing', 'bool');
$this->setConfigFromParams($oConfig, 'ContactsSync', 'contacts', 'allow_carddav_sync', 'bool');
$this->setConfigFromParams($oConfig, 'ContactsPdoDsn', 'contacts', 'pdo_dsn', 'string'); $this->setConfigFromParams($oConfig, 'ContactsPdoDsn', 'contacts', 'pdo_dsn', 'string');
$this->setConfigFromParams($oConfig, 'ContactsPdoUser', 'contacts', 'pdo_user', 'string'); $this->setConfigFromParams($oConfig, 'ContactsPdoUser', 'contacts', 'pdo_user', 'string');
$this->setConfigFromParams($oConfig, 'ContactsPdoPassword', 'contacts', 'pdo_password', 'dummy'); $this->setConfigFromParams($oConfig, 'ContactsPdoPassword', 'contacts', 'pdo_password', 'dummy');
@ -4207,7 +4209,7 @@ class Actions
if ($this->PersonalAddressBookProvider($oAccount)->IsActive()) if ($this->PersonalAddressBookProvider($oAccount)->IsActive())
{ {
$iCount = 0; $iCount = 0;
$mResult = $this->PersonalAddressBookProvider($oAccount)->GetContacts($oAccount, $mResult = $this->PersonalAddressBookProvider($oAccount)->GetContacts($oAccount->ParentEmailHelper(),
$iOffset, $iLimit, $sSearch, $iCount); $iOffset, $iLimit, $sSearch, $iCount);
} }
@ -4236,7 +4238,7 @@ class Actions
$bResult = false; $bResult = false;
if (0 < \count($aFilteredUids) && $this->PersonalAddressBookProvider($oAccount)->IsActive()) if (0 < \count($aFilteredUids) && $this->PersonalAddressBookProvider($oAccount)->IsActive())
{ {
$bResult = $this->PersonalAddressBookProvider($oAccount)->DeleteContacts($oAccount, $aFilteredUids); $bResult = $this->PersonalAddressBookProvider($oAccount)->DeleteContacts($oAccount->ParentEmailHelper(), $aFilteredUids);
} }
return $this->DefaultResponse(__FUNCTION__, $bResult); return $this->DefaultResponse(__FUNCTION__, $bResult);
@ -4256,6 +4258,7 @@ class Actions
if ($oPab && $oPab->IsActive() && 0 < \strlen($sRequestUid)) if ($oPab && $oPab->IsActive() && 0 < \strlen($sRequestUid))
{ {
$sUid = \trim($this->GetActionParam('Uid', '')); $sUid = \trim($this->GetActionParam('Uid', ''));
$sUidStr = \trim($this->GetActionParam('UidStr', ''));
$iScopeType = (int) $this->GetActionParam('ScopeType', null); $iScopeType = (int) $this->GetActionParam('ScopeType', null);
if (!in_array($iScopeType, array( if (!in_array($iScopeType, array(
\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_, \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_,
@ -4269,6 +4272,10 @@ class Actions
{ {
$oContact->IdContact = $sUid; $oContact->IdContact = $sUid;
} }
if (0 < \strlen($sUidStr))
{
$oContact->IdContactStr = $sUidStr;
}
$oContact->ScopeType = $iScopeType; $oContact->ScopeType = $iScopeType;
@ -4290,12 +4297,13 @@ class Actions
} }
} }
$bResult = $oPab->ContactSave($oAccount, $oContact); $bResult = $oPab->ContactSave($oAccount->ParentEmailHelper(), $oContact);
} }
return $this->DefaultResponse(__FUNCTION__, array( return $this->DefaultResponse(__FUNCTION__, array(
'RequestUid' => $sRequestUid, 'RequestUid' => $sRequestUid,
'ResultID' => $bResult ? $oContact->IdContact : '', 'ResultID' => $bResult ? $oContact->IdContact : '',
'ResultIDStr' => $bResult ? $oContact->IdContactStr : '',
'Result' => $bResult 'Result' => $bResult
)); ));
} }
@ -4328,7 +4336,7 @@ class Actions
$oPab = $this->PersonalAddressBookProvider($oAccount); $oPab = $this->PersonalAddressBookProvider($oAccount);
if ($oPab && $oPab->IsActive()) if ($oPab && $oPab->IsActive())
{ {
$aSuggestions = $oPab->GetSuggestions($oAccount, $sQuery, $iLimit); $aSuggestions = $oPab->GetSuggestions($oAccount->ParentEmailHelper(), $sQuery, $iLimit);
if (0 === \count($aResult)) if (0 === \count($aResult))
{ {
$aResult = $aSuggestions; $aResult = $aSuggestions;
@ -5938,6 +5946,7 @@ class Actions
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array(
/* @var $mResponse \RainLoop\Providers\PersonalAddressBook\Classes\Contact */ /* @var $mResponse \RainLoop\Providers\PersonalAddressBook\Classes\Contact */
'IdContact' => $mResponse->IdContact, 'IdContact' => $mResponse->IdContact,
'IdContactStr' => $mResponse->IdContactStr,
'Display' => \MailSo\Base\Utils::Utf8Clear($mResponse->Display), 'Display' => \MailSo\Base\Utils::Utf8Clear($mResponse->Display),
'ReadOnly' => $mResponse->ReadOnly, 'ReadOnly' => $mResponse->ReadOnly,
'ScopeType' => $mResponse->ScopeType, 'ScopeType' => $mResponse->ScopeType,

View file

@ -229,12 +229,18 @@ abstract class PdoAbstract
/** /**
* @param string $sEmail * @param string $sEmail
* @param bool $bSkipInsert = false * @param bool $bSkipInsert = false
* @param bool $bSkipUpdateTables = false * @param bool $bCache = true
* *
* @return int * @return int
*/ */
protected function getUserId($sEmail, $bSkipInsert = false) protected function getUserId($sEmail, $bSkipInsert = false, $bCache = true)
{ {
static $aCache = array();
if ($bCache && isset($aCache[$sEmail]))
{
return $aCache[$sEmail];
}
$sEmail = \strtolower(\trim($sEmail)); $sEmail = \strtolower(\trim($sEmail));
if (empty($sEmail)) if (empty($sEmail))
{ {
@ -242,20 +248,38 @@ abstract class PdoAbstract
} }
$oStmt = $this->prepareAndExecute('SELECT id_user FROM rainloop_users WHERE rl_email = :rl_email', $oStmt = $this->prepareAndExecute('SELECT id_user FROM rainloop_users WHERE rl_email = :rl_email',
array(':rl_email' => array($sEmail, \PDO::PARAM_STR))); array(
':rl_email' => array($sEmail, \PDO::PARAM_STR)
)
);
$mRow = $oStmt->fetch(\PDO::FETCH_ASSOC); $mRow = $oStmt->fetch(\PDO::FETCH_ASSOC);
if ($mRow && isset($mRow['id_user']) && \is_numeric($mRow['id_user'])) if ($mRow && isset($mRow['id_user']) && \is_numeric($mRow['id_user']))
{ {
return (int) $mRow['id_user']; $iResult = (int) $mRow['id_user'];
if (0 >= $iResult)
{
throw new \Exception('id_user <= 0');
}
if ($bCache)
{
$aCache[$sEmail] = $iResult;
}
return $iResult;
} }
if (!$bSkipInsert) if (!$bSkipInsert)
{ {
$oStmt->closeCursor(); $oStmt->closeCursor();
$oStmt = $this->prepareAndExecute('INSERT INTO rainloop_users (rl_email) VALUES (:rl_email)', $oStmt = $this->prepareAndExecute('INSERT INTO rainloop_users (rl_email, rl_hash) VALUES (:rl_email, :rl_hash)',
array(':rl_email' => array($sEmail, \PDO::PARAM_STR))); array(
':rl_email' => array($sEmail, \PDO::PARAM_STR),
':rl_hash' => array(\md5($sEmail.\microtime(true)), \PDO::PARAM_STR)
)
);
return $this->getUserId($sEmail, true); return $this->getUserId($sEmail, true);
} }
@ -374,6 +398,8 @@ abstract class PdoAbstract
} }
/** /**
* @param bool $bWatchVersion = true
*
* @throws \Exception * @throws \Exception
*/ */
protected function initSystemTables() protected function initSystemTables()
@ -384,55 +410,51 @@ abstract class PdoAbstract
if ($oPdo) if ($oPdo)
{ {
$aQ = array(); $aQ = array();
if ('mysql' === $this->sDbType) switch ($this->sDbType)
{ {
$aQ[] = 'CREATE TABLE IF NOT EXISTS rainloop_system ( case 'mysql':
sys_name varchar(50) NOT NULL, $aQ[] = 'CREATE TABLE IF NOT EXISTS rainloop_system (
value_int int UNSIGNED NOT NULL DEFAULT 0, sys_name varchar(50) NOT NULL,
value_str varchar(128) NOT NULL DEFAULT \'\', value_int int UNSIGNED NOT NULL DEFAULT 0,
INDEX sys_name_rainloop_system_index (sys_name) value_str varchar(128) NOT NULL DEFAULT \'\',
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;'; INDEX sys_name_rainloop_system_index (sys_name)
) /*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;';
$aQ[] = 'CREATE TABLE IF NOT EXISTS rainloop_users ( $aQ[] = 'CREATE TABLE IF NOT EXISTS rainloop_users (
id_user int UNSIGNED NOT NULL AUTO_INCREMENT, id_user int UNSIGNED NOT NULL AUTO_INCREMENT,
rl_email varchar(128) NOT NULL DEFAULT \'\', rl_email varchar(128) NOT NULL DEFAULT \'\',
PRIMARY KEY(id_user), PRIMARY KEY(id_user),
INDEX rl_email_rainloop_users_index (rl_email) INDEX rl_email_rainloop_users_index (rl_email)
) /*!40000 ENGINE=INNODB */;'; ) /*!40000 ENGINE=INNODB */;';
} break;
else if ('pgsql' === $this->sDbType)
{
$aQ[] = 'CREATE TABLE rainloop_system (
sys_name varchar(50) NOT NULL,
value_int integer NOT NULL DEFAULT 0,
value_str varchar(128) NOT NULL DEFAULT \'\'
);';
$aQ[] = 'CREATE INDEX sys_name_rainloop_system_index ON rainloop_system (sys_name);'; case 'pgsql':
$aQ[] = 'CREATE TABLE rainloop_system (
$aQ[] = 'CREATE SEQUENCE id_user START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1;'; sys_name varchar(50) NOT NULL,
value_int integer NOT NULL DEFAULT 0,
$aQ[] = 'CREATE TABLE rainloop_users ( value_str varchar(128) NOT NULL DEFAULT \'\'
id_user integer DEFAULT nextval(\'id_user\'::text) PRIMARY KEY,
rl_email varchar(128) NOT NULL DEFAULT \'\'
);'; );';
$aQ[] = 'CREATE INDEX rl_email_rainloop_users_index ON rainloop_users (rl_email);'; $aQ[] = 'CREATE INDEX sys_name_rainloop_system_index ON rainloop_system (sys_name);';
} $aQ[] = 'CREATE SEQUENCE id_user START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1;';
else if ('sqlite' === $this->sDbType) $aQ[] = 'CREATE TABLE rainloop_users (
{ id_user integer DEFAULT nextval(\'id_user\'::text) PRIMARY KEY,
$aQ[] = 'CREATE TABLE rainloop_system ( rl_email varchar(128) NOT NULL DEFAULT \'\'
sys_name text NOT NULL,
value_int integer NOT NULL default 0,
value_str text NOT NULL default \'\'
);'; );';
$aQ[] = 'CREATE INDEX rl_email_rainloop_users_index ON rainloop_users (rl_email);';
break;
$aQ[] = 'CREATE INDEX sys_name_rainloop_system_index ON rainloop_system (sys_name);'; case 'sqlite':
$aQ[] = 'CREATE TABLE rainloop_system (
$aQ[] = 'CREATE TABLE rainloop_users ( sys_name text NOT NULL,
id_user integer NOT NULL PRIMARY KEY, value_int integer NOT NULL DEFAULT 0,
rl_email text NOT NULL default \'\' value_str text NOT NULL DEFAULT \'\'
);'; );';
$aQ[] = 'CREATE INDEX rl_email_rainloop_users_index ON rainloop_users (rl_email);'; $aQ[] = 'CREATE INDEX sys_name_rainloop_system_index ON rainloop_system (sys_name);';
$aQ[] = 'CREATE TABLE rainloop_users (
id_user integer NOT NULL PRIMARY KEY,
rl_email text NOT NULL DEFAULT \'\'
);';
$aQ[] = 'CREATE INDEX rl_email_rainloop_users_index ON rainloop_users (rl_email);';
break;
} }
if (0 < \count($aQ)) if (0 < \count($aQ))

View file

@ -84,6 +84,7 @@ class Application extends \RainLoop\Config\AbstractConfig
'contacts' => array( 'contacts' => array(
'enable' => array(false, 'Enable contacts'), 'enable' => array(false, 'Enable contacts'),
'allow_sharing' => array(true), 'allow_sharing' => array(true),
'allow_carddav_sync' => array(false),
'suggestions_limit' => array(30), 'suggestions_limit' => array(30),
'type' => array('sqlite', ''), 'type' => array('sqlite', ''),
'pdo_dsn' => array('mysql:host=127.0.0.1;port=3306;dbname=rainloop', ''), 'pdo_dsn' => array('mysql:host=127.0.0.1;port=3306;dbname=rainloop', ''),

View file

@ -67,8 +67,20 @@ class PersonalAddressBook extends \RainLoop\Providers\AbstractProvider
*/ */
public function GetUserUidByEmail($sEmail) public function GetUserUidByEmail($sEmail)
{ {
return $this->oDriver instanceof \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface && return $this->oDriver instanceof \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface ?
$this->oDriver->GetUserUidByEmail($sEmail); $this->oDriver->GetUserUidByEmail($sEmail) : '';
}
/**
* @param string $sEmail
* @param bool $bCreate = false
*
* @return string
*/
public function GetUserHashByEmail($sEmail, $bCreate = false)
{
return $this->oDriver instanceof \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface ?
$this->oDriver->GetUserHashByEmail($sEmail, $bCreate) : '';
} }
/** /**
@ -121,6 +133,18 @@ class PersonalAddressBook extends \RainLoop\Providers\AbstractProvider
$iOffset, $iLimit, $sSearch, $iResultCount) : array(); $iOffset, $iLimit, $sSearch, $iResultCount) : array();
} }
/**
* @param string $sEmail
* @param string $sID
* @param bool $bIsStrID = false
*
* @return \RainLoop\Providers\PersonalAddressBook\Classes\Contact|null
*/
public function GetContactByID($sEmail, $mID, $bIsStrID = false)
{
return $this->IsActive() ? $this->oDriver->GetContactByID($sEmail, $mID, $bIsStrID) : null;
}
/** /**
* @param \RainLoop\Account $oAccount * @param \RainLoop\Account $oAccount
* @param string $sSearch * @param string $sSearch

View file

@ -11,6 +11,11 @@ class Contact
*/ */
public $IdContact; public $IdContact;
/**
* @var string
*/
public $IdContactStr;
/** /**
* @var string * @var string
*/ */
@ -59,6 +64,7 @@ class Contact
public function Clear() public function Clear()
{ {
$this->IdContact = ''; $this->IdContact = '';
$this->IdContactStr = '';
$this->IdUser = 0; $this->IdUser = 0;
$this->Display = ''; $this->Display = '';
$this->DisplayName = ''; $this->DisplayName = '';
@ -95,6 +101,11 @@ class Contact
} }
} }
if (empty($this->IdContactStr))
{
$this->IdContactStr = \Sabre\DAV\UUIDUtil::getUUID();
}
$this->DisplayName = $sDisplayName; $this->DisplayName = $sDisplayName;
$this->DisplayEmail = $sDisplayEmail; $this->DisplayEmail = $sDisplayEmail;
@ -123,6 +134,8 @@ class Contact
*/ */
public function ToVCardObject() public function ToVCardObject()
{ {
$this->UpdateDependentValues();
$oVCard = new \Sabre\VObject\Component\VCard('VCARD'); $oVCard = new \Sabre\VObject\Component\VCard('VCARD');
$oVCard->VERSION = '3.0'; $oVCard->VERSION = '3.0';
@ -141,6 +154,32 @@ class Contact
case PropertyType::NICK: case PropertyType::NICK:
$oVCard->NICKNAME = $oProperty->Value; $oVCard->NICKNAME = $oProperty->Value;
break; break;
case PropertyType::EMAIl_PERSONAL:
case PropertyType::EMAIl_OTHER:
$oVCard->add('EMAIL', $oProperty->Value, array('TYPE' => 'INTERNET', 'TYPE' => 'HOME'));
break;
case PropertyType::EMAIl_BUSSINES:
$oVCard->add('EMAIL', $oProperty->Value, array('TYPE' => 'INTERNET', 'TYPE' => 'WORK'));
break;
case PropertyType::PHONE_PERSONAL:
case PropertyType::PHONE_OTHER:
$oVCard->add('TEL', $oProperty->Value, array('TYPE' => 'VOICE', 'TYPE' => 'HOME'));
break;
case PropertyType::PHONE_BUSSINES:
$oVCard->add('TEL', $oProperty->Value, array('TYPE' => 'VOICE', 'TYPE' => 'WORK'));
break;
case PropertyType::MOBILE_PERSONAL:
case PropertyType::MOBILE_BUSSINES:
case PropertyType::MOBILE_OTHER:
$oVCard->add('TEL', $oProperty->Value, array('TYPE' => 'VOICE', 'TYPE' => 'CELL'));
break;
case PropertyType::FAX_PERSONAL:
case PropertyType::FAX_OTHER:
$oVCard->add('TEL', $oProperty->Value, array('TYPE' => 'FAX', 'TYPE' => 'HOME'));
break;
case PropertyType::FAX_BUSSINES:
$oVCard->add('TEL', $oProperty->Value, array('TYPE' => 'FAX', 'TYPE' => 'WORK'));
break;
case PropertyType::FIRST_NAME: case PropertyType::FIRST_NAME:
$sFirstName = $oProperty->Value; $sFirstName = $oProperty->Value;
break; break;
@ -148,15 +187,10 @@ class Contact
$sSurName = $oProperty->Value; $sSurName = $oProperty->Value;
break; break;
} }
if (PropertyType::FULLNAME === $oProperty->Type)
{
$oVCard->FN = $oProperty->Value;
}
} }
} }
$oVCard->UID = ((string) $this->IdContact).'.vcf'; $oVCard->UID = $this->VCardUID();
// $oVCard->N = array( // $oVCard->N = array(
// $sSurName, // $sSurName,
@ -169,4 +203,14 @@ class Contact
return $oVCard; return $oVCard;
} }
/**
* @return string
*/
public function VCardUID()
{
return $this->IdContactStr.'.vcf';
}
} }

View file

@ -93,15 +93,54 @@ class PdoPersonalAddressBook
} }
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param bool $bCreate = false
*
* @return string
*/
public function GetUserHashByEmail($sEmail, $bCreate = false)
{
$sHash = '';
$iUserID = $this->getUserId($sEmail);
if (0 < $iUserID)
{
$oStmt = $this->prepareAndExecute('SELECT pass_hash FROM rainloop_pab_users_hashes WHERE id_user = :id_user LIMIT 1',
array(':id_user' => array($iUserID, \PDO::PARAM_INT)));
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if ($aFetch && !empty($aFetch[0]['pass_hash']))
{
$sHash = \rtrim(\base_convert(\md5(\md5($sEmail.'-'.\trim($aFetch[0]['pass_hash']).'-rainloop')), 16, 32), '0');
}
else if ($bCreate)
{
$this->prepareAndExecute('INSERT INTO rainloop_pab_users_hashes (id_user, pass_hash) VALUES (:id_user, :pass_hash);',
array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':pass_hash' => array(\md5($sEmail.\microtime(true)), \PDO::PARAM_STR)
)
);
$this->GetUserHashByEmail($sEmail, false);
}
}
}
return $sHash;
}
/**
* @param string $sEmail
* @param \RainLoop\Providers\PersonalAddressBook\Classes\Contact $oContact * @param \RainLoop\Providers\PersonalAddressBook\Classes\Contact $oContact
* *
* @return bool * @return bool
*/ */
public function ContactSave($oAccount, &$oContact) public function ContactSave($sEmail, &$oContact)
{ {
$this->Sync(); $this->Sync();
$iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $iUserID = $this->getUserId($sEmail);
$iIdContact = \strlen($oContact->IdContact) && \is_numeric($oContact->IdContact) ? (int) $oContact->IdContact : 0; $iIdContact = \strlen($oContact->IdContact) && \is_numeric($oContact->IdContact) ? (int) $oContact->IdContact : 0;
@ -127,13 +166,14 @@ class PdoPersonalAddressBook
{ {
$aFreq = $this->getContactFreq($iUserID, $iIdContact); $aFreq = $this->getContactFreq($iUserID, $iIdContact);
$sSql = 'UPDATE rainloop_pab_contacts SET display = :display, display_name = :display_name, display_email = :display_email, '. $sSql = 'UPDATE rainloop_pab_contacts SET id_contact_str = :id_contact_str, display = :display, display_name = :display_name, display_email = :display_email, '.
'scope_type = :scope_type, changed = :changed WHERE id_user = :id_user AND id_contact = :id_contact'; 'scope_type = :scope_type, changed = :changed WHERE id_user = :id_user AND id_contact = :id_contact';
$this->prepareAndExecute($sSql, $this->prepareAndExecute($sSql,
array( array(
':id_user' => array($iUserID, \PDO::PARAM_INT), ':id_user' => array($iUserID, \PDO::PARAM_INT),
':id_contact' => array($iIdContact, \PDO::PARAM_INT), ':id_contact' => array($iIdContact, \PDO::PARAM_INT),
':id_contact_str' => array($oContact->IdContactStr, \PDO::PARAM_STR),
':display' => array($oContact->Display, \PDO::PARAM_STR), ':display' => array($oContact->Display, \PDO::PARAM_STR),
':display_name' => array($oContact->DisplayName, \PDO::PARAM_STR), ':display_name' => array($oContact->DisplayName, \PDO::PARAM_STR),
':display_email' => array($oContact->DisplayEmail, \PDO::PARAM_STR), ':display_email' => array($oContact->DisplayEmail, \PDO::PARAM_STR),
@ -154,12 +194,13 @@ class PdoPersonalAddressBook
else else
{ {
$sSql = 'INSERT INTO rainloop_pab_contacts '. $sSql = 'INSERT INTO rainloop_pab_contacts '.
'( id_user, display, display_name, display_email, scope_type, changed) VALUES '. '( id_user, id_contact_str, display, display_name, display_email, scope_type, changed) VALUES '.
'(:id_user, :display, :display_name, :display_email, :scope_type, :changed)'; '(:id_user, :id_contact_str, :display, :display_name, :display_email, :scope_type, :changed)';
$this->prepareAndExecute($sSql, $this->prepareAndExecute($sSql,
array( array(
':id_user' => array($iUserID, \PDO::PARAM_INT), ':id_user' => array($iUserID, \PDO::PARAM_INT),
':id_contact_str' => array($oContact->IdContactStr, \PDO::PARAM_STR),
':display' => array($oContact->Display, \PDO::PARAM_STR), ':display' => array($oContact->Display, \PDO::PARAM_STR),
':display_name' => array($oContact->DisplayName, \PDO::PARAM_STR), ':display_name' => array($oContact->DisplayName, \PDO::PARAM_STR),
':display_email' => array($oContact->DisplayEmail, \PDO::PARAM_STR), ':display_email' => array($oContact->DisplayEmail, \PDO::PARAM_STR),
@ -225,14 +266,14 @@ class PdoPersonalAddressBook
} }
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param array $aContactIds * @param array $aContactIds
* *
* @return bool * @return bool
*/ */
public function DeleteContacts($oAccount, $aContactIds) public function DeleteContacts($sEmail, $aContactIds)
{ {
$iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $iUserID = $this->getUserId($sEmail);
$aContactIds = \array_filter($aContactIds, function (&$mItem) { $aContactIds = \array_filter($aContactIds, function (&$mItem) {
$mItem = (int) \trim($mItem); $mItem = (int) \trim($mItem);
@ -255,15 +296,15 @@ class PdoPersonalAddressBook
} }
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param array $aTagsIds * @param array $aTagsIds
* *
* @return bool * @return bool
*/ */
public function DeleteTags($oAccount, $aTagsIds) public function DeleteTags($sEmail, $aTagsIds)
{ {
$this->Sync(); $this->Sync();
$iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $iUserID = $this->getUserId($sEmail);
$aTagsIds = \array_filter($aTagsIds, function (&$mItem) { $aTagsIds = \array_filter($aTagsIds, function (&$mItem) {
$mItem = (int) \trim($mItem); $mItem = (int) \trim($mItem);
@ -285,7 +326,7 @@ class PdoPersonalAddressBook
} }
/** /**
* @param \RainLoop\Account|mixed $mAccountOrId * @param string $sEmail
* @param int $iOffset = 0 * @param int $iOffset = 0
* @param int $iLimit = 20 * @param int $iLimit = 20
* @param string $sSearch = '' * @param string $sSearch = ''
@ -293,7 +334,7 @@ class PdoPersonalAddressBook
* *
* @return array * @return array
*/ */
public function GetContacts($mAccountOrId, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0) public function GetContacts($sEmail, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0)
{ {
$this->Sync(); $this->Sync();
@ -301,11 +342,7 @@ class PdoPersonalAddressBook
$iLimit = 0 < $iLimit ? (int) $iLimit : 20; $iLimit = 0 < $iLimit ? (int) $iLimit : 20;
$sSearch = \trim($sSearch); $sSearch = \trim($sSearch);
$iUserID = $mAccountOrId instanceof \RainLoop\Account ? $this->getUserId($mAccountOrId->ParentEmailHelper()) : (int) $mAccountOrId; $iUserID = $this->getUserId($sEmail);
if (0 === $iUserID)
{
throw new \InvalidArgumentException('Invalid Accoutn ID');
}
$iCount = 0; $iCount = 0;
$aSearchIds = array(); $aSearchIds = array();
@ -428,6 +465,7 @@ class PdoPersonalAddressBook
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
$oContact->IdContact = (string) $iIdContact; $oContact->IdContact = (string) $iIdContact;
$oContact->IdContactStr = isset($aItem['id_contact_str']) ? (string) $aItem['id_contact_str'] : '';
$oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : ''; $oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : '';
$oContact->DisplayName = isset($aItem['display_name']) ? (string) $aItem['display_name'] : ''; $oContact->DisplayName = isset($aItem['display_name']) ? (string) $aItem['display_name'] : '';
$oContact->DisplayEmail = isset($aItem['display_email']) ? (string) $aItem['display_email'] : ''; $oContact->DisplayEmail = isset($aItem['display_email']) ? (string) $aItem['display_email'] : '';
@ -498,7 +536,124 @@ class PdoPersonalAddressBook
} }
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param string $sID
* @param bool $bIsStrID = false
*
* @return \RainLoop\Providers\PersonalAddressBook\Classes\Contact|null
*/
public function GetContactByID($sEmail, $mID, $bIsStrID = false)
{
$this->Sync();
$mID = \trim($mID);
$iUserID = $this->getUserId($sEmail);
$sSql = 'SELECT * FROM rainloop_pab_contacts '.
'WHERE id_user = :id_user'.
($this->bConsiderShare ? ' OR scope_type = :scope_type_share_all' : '')
;
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
);
if ($this->bConsiderShare)
{
$aParams[':scope_type_share_all'] = array(\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::SHARE_ALL, \PDO::PARAM_INT);
}
if ($bIsStrID)
{
$sSql .= ' AND id_contact_str = :id_contact_str';
$aParams[':id_contact_str'] = array($mID, \PDO::PARAM_STR);
}
else
{
$sSql .= ' AND id_contact = :id_contact';
$aParams[':id_contact'] = array($mID, \PDO::PARAM_INT);
}
$sSql .= ' LIMIT 1';
$oContact = null;
$iIdContact = 0;
$oStmt = $this->prepareAndExecute($sSql, $aParams);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
foreach ($aFetch as $aItem)
{
$iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0;
if (0 < $iIdContact)
{
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
$oContact->IdContact = (string) $iIdContact;
$oContact->IdContactStr = isset($aItem['id_contact_str']) ? (string) $aItem['id_contact_str'] : '';
$oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : '';
$oContact->DisplayName = isset($aItem['display_name']) ? (string) $aItem['display_name'] : '';
$oContact->DisplayEmail = isset($aItem['display_email']) ? (string) $aItem['display_email'] : '';
$oContact->ScopeType = isset($aItem['scope_type']) ? (int) $aItem['scope_type'] :
\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_;
$oContact->Changed = isset($aItem['changed']) ? (int) $aItem['changed'] : 0;
$oContact->ReadOnly = $iUserID !== (isset($aItem['id_user']) ? (int) $aItem['id_user'] : 0);
}
}
}
unset($aFetch);
if (0 < $iIdContact && $oContact)
{
$oStmt->closeCursor();
$sSql = 'SELECT * FROM rainloop_pab_properties WHERE id_contact = '.$iIdContact;
$oStmt = $this->prepareAndExecute($sSql);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
foreach ($aFetch as $aItem)
{
if ($aItem && isset($aItem['id_prop'], $aItem['id_contact'], $aItem['prop_type'], $aItem['prop_value']))
{
if ((string) $oContact->IdContact === (string) $aItem['id_contact'])
{
$oProperty = new \RainLoop\Providers\PersonalAddressBook\Classes\Property();
$oProperty->IdProperty = (int) $aItem['id_prop'];
$oProperty->ScopeType = isset($aItem['scope_type']) ? (int) $aItem['scope_type'] :
\RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_;
$oProperty->Type = (int) $aItem['prop_type'];
$oProperty->TypeCustom = isset($aItem['prop_type_custom']) ? (string) $aItem['prop_type_custom'] : '';
$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;
$oContact->Properties[] = $oProperty;
}
}
}
}
unset($aFetch);
$oContact->UpdateDependentValues();
}
}
}
return $oContact;
}
/**
* @param string $sEmail
* @param string $sSearch * @param string $sSearch
* @param int $iLimit = 20 * @param int $iLimit = 20
* *
@ -506,7 +661,7 @@ class PdoPersonalAddressBook
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function GetSuggestions($oAccount, $sSearch, $iLimit = 20) public function GetSuggestions($sEmail, $sSearch, $iLimit = 20)
{ {
$sSearch = \trim($sSearch); $sSearch = \trim($sSearch);
if (0 === \strlen($sSearch)) if (0 === \strlen($sSearch))
@ -516,7 +671,7 @@ class PdoPersonalAddressBook
$this->Sync(); $this->Sync();
$iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $iUserID = $this->getUserId($sEmail);
$sTypes = implode(',', array( $sTypes = implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FULLNAME PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FULLNAME
@ -675,13 +830,13 @@ class PdoPersonalAddressBook
} }
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param array $aEmails * @param array $aEmails
* @param bool $bCreateAuto = true * @param bool $bCreateAuto = true
* *
* @return bool * @return bool
*/ */
public function IncFrec($oAccount, $aEmails, $bCreateAuto = true) public function IncFrec($sEmail, $aEmails, $bCreateAuto = true)
{ {
$self = $this; $self = $this;
$aEmailsObjects = \array_map(function ($mItem) { $aEmailsObjects = \array_map(function ($mItem) {
@ -700,7 +855,7 @@ class PdoPersonalAddressBook
} }
$this->Sync(); $this->Sync();
$iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $iUserID = $this->getUserId($sEmail);
$sTypes = \implode(',', array( $sTypes = \implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER
@ -792,7 +947,7 @@ class PdoPersonalAddressBook
if (0 < \count($oContact->Properties)) if (0 < \count($oContact->Properties))
{ {
$this->ContactSave($oAccount, $oContact); $this->ContactSave($sEmail, $oContact);
} }
$oContact->Clear(); $oContact->Clear();
@ -975,11 +1130,11 @@ POSTGRESINITIAL;
CREATE TABLE rainloop_pab_contacts ( CREATE TABLE rainloop_pab_contacts (
id_contact integer NOT NULL PRIMARY KEY, id_contact integer NOT NULL PRIMARY KEY,
id_user integer NOT NULL, id_user integer NOT NULL,
scope_type integer NOT NULL default 0, scope_type integer NOT NULL DEFAULT 0,
display_name text NOT NULL default '', display_name text NOT NULL DEFAULT '',
display_email text NOT NULL default '', display_email text NOT NULL DEFAULT '',
display text NOT NULL default '', display text NOT NULL DEFAULT '',
changed integer NOT NULL default 0 changed integer NOT NULL DEFAULT 0
); );
CREATE INDEX id_user_scope_type_rainloop_pab_contacts_index ON rainloop_pab_contacts (id_user, scope_type); CREATE INDEX id_user_scope_type_rainloop_pab_contacts_index ON rainloop_pab_contacts (id_user, scope_type);
@ -988,12 +1143,12 @@ CREATE TABLE rainloop_pab_properties (
id_prop integer NOT NULL PRIMARY KEY, id_prop integer NOT NULL PRIMARY KEY,
id_contact integer NOT NULL, id_contact integer NOT NULL,
id_user integer NOT NULL, id_user integer NOT NULL,
scope_type integer NOT NULL default 0, scope_type integer NOT NULL DEFAULT 0,
prop_type integer NOT NULL, prop_type integer NOT NULL,
prop_type_custom text NOT NULL default '', prop_type_custom text NOT NULL DEFAULT '',
prop_value text NOT NULL default '', prop_value text NOT NULL DEFAULT '',
prop_value_custom text NOT NULL default '', prop_value_custom text NOT NULL DEFAULT '',
prop_frec integer NOT NULL default 0 prop_frec integer NOT NULL DEFAULT 0
); );
CREATE INDEX id_user_rainloop_pab_properties_index ON rainloop_pab_properties (id_user); CREATE INDEX id_user_rainloop_pab_properties_index ON rainloop_pab_properties (id_user);
@ -1045,15 +1200,36 @@ SQLITEINITIAL;
{ {
case 'mysql': case 'mysql':
return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array( return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType) 1 => $this->getInitialTablesArray($this->sDsnType),
2 => array(
'ALTER TABLE rainloop_pab_contacts ADD id_contact_str varchar(128) NOT NULL DEFAULT \'\' AFTER id_contact;',
'CREATE TABLE IF NOT EXISTS rainloop_pab_users_hashes (
id_user int UNSIGNED NOT NULL,
pass_hash varchar(128) NOT NULL
)/*!40000 ENGINE=INNODB */;'
)
)); ));
case 'pgsql': case 'pgsql':
return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array( return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType) 1 => $this->getInitialTablesArray($this->sDsnType),
2 => array(
'ALTER TABLE rainloop_pab_contacts ADD id_contact_str varchar(128) NOT NULL DEFAULT \'\';',
'CREATE TABLE rainloop_pab_users_hashes (
id_user integer NOT NULL,
pass_hash varchar(128) NOT NULL
);'
)
)); ));
case 'sqlite': case 'sqlite':
return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array( return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType) 1 => $this->getInitialTablesArray($this->sDsnType),
2 => array(
'ALTER TABLE rainloop_pab_contacts ADD id_contact_str text NOT NULL DEFAULT \'\';',
'CREATE TABLE rainloop_pab_users_hashes (
id_user integer NOT NULL,
pass_hash text NOT NULL
);'
)
)); ));
} }

View file

@ -10,31 +10,31 @@ interface PersonalAddressBookInterface
public function IsSupported(); public function IsSupported();
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param \RainLoop\Providers\PersonalAddressBook\Classes\Contact $oContact * @param \RainLoop\Providers\PersonalAddressBook\Classes\Contact $oContact
* *
* @return bool * @return bool
*/ */
public function ContactSave($oAccount, &$oContact); public function ContactSave($sEmail, &$oContact);
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param array $aContactIds * @param array $aContactIds
* *
* @return bool * @return bool
*/ */
public function DeleteContacts($oAccount, $aContactIds); public function DeleteContacts($sEmail, $aContactIds);
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param array $aTagsIds * @param array $aTagsIds
* *
* @return bool * @return bool
*/ */
public function DeleteTags($oAccount, $aTagsIds); public function DeleteTags($sEmail, $aTagsIds);
/** /**
* @param \RainLoop\Account|mixed $mAccountOrId * @param string $sEmail
* @param int $iOffset = 0 * @param int $iOffset = 0
* @param int $iLimit = 20 * @param int $iLimit = 20
* @param string $sSearch = '' * @param string $sSearch = ''
@ -42,10 +42,10 @@ interface PersonalAddressBookInterface
* *
* @return array * @return array
*/ */
public function GetContacts($mAccountOrId, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0); public function GetContacts($sEmail, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0);
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param string $sSearch * @param string $sSearch
* @param int $iLimit = 20 * @param int $iLimit = 20
* *
@ -53,13 +53,13 @@ interface PersonalAddressBookInterface
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function GetSuggestions($oAccount, $sSearch, $iLimit = 20); public function GetSuggestions($sEmail, $sSearch, $iLimit = 20);
/** /**
* @param \RainLoop\Account $oAccount * @param string $sEmail
* @param array $aEmails * @param array $aEmails
* *
* @return bool * @return bool
*/ */
public function IncFrec($oAccount, $aEmails); public function IncFrec($sEmail, $aEmails);
} }

View file

@ -1,31 +1,30 @@
<?php <?php
namespace RainLoop\SabreDAV; namespace RainLoop\SabreDAV;
class AuthBasic extends \Sabre\DAV\Auth\Backend\AbstractBasic class AuthBasic extends \Sabre\DAV\Auth\Backend\AbstractBasic
{ {
/** /**
* @var \RainLoop\Providers\PersonalAddressBook * @var \RainLoop\Providers\PersonalAddressBook
*/ */
private $oPersonalAddressBook; private $oPersonalAddressBook;
/** /**
* @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook * @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook
*/ */
public function __construct($oPersonalAddressBook) public function __construct($oPersonalAddressBook)
{ {
$this->oPersonalAddressBook = $oPersonalAddressBook; $this->oPersonalAddressBook = $oPersonalAddressBook;
} }
/** /**
* @param string $sUserName * @param string $sUserName
* @param string $sPassword * @param string $sPassword
* *
* @return bool * @return bool
*/ */
protected function validateUserPass($sUserName, $sPassword) protected function validateUserPass($sUserName, $sPassword)
{ {
$this->currentUser = $sUserName; return $sPassword === $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true);
return true; // TODO }
} }
}

View file

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

View file

@ -1,278 +1,333 @@
<?php <?php
namespace RainLoop\SabreDAV; namespace RainLoop\SabreDAV;
class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface { class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface
{
/** /**
* @var \RainLoop\Providers\PersonalAddressBook * @var \RainLoop\Providers\PersonalAddressBook
*/ */
private $oPersonalAddressBook; private $oPersonalAddressBook;
/** /**
* @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook * @var \RainLoop\SabreDAV\AuthBasic
*/ */
public function __construct($oPersonalAddressBook) private $oAuthBackend;
{
$this->oPersonalAddressBook = $oPersonalAddressBook; /**
} * @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook
* @param \RainLoop\SabreDAV\AuthBasic $oAuthBackend
/** */
* @param string $sPrincipalUri public function __construct($oPersonalAddressBook, &$oAuthBackend)
* {
* @return string $this->oPersonalAddressBook = $oPersonalAddressBook;
*/ $this->oPersonalAddressBook->ConsiderShare(false);
private function getEmailFromPrincipalUri($sPrincipalUri) $this->oAuthBackend = $oAuthBackend;
{ }
$sEmail = '';
$aMatch = array(); /**
if (\preg_match('/\/?principals\/([^@\/]+@[^@\/]+)/i', $sPrincipalUri, $aMatch) && !empty($aMatch[1])) * @param string $sPrincipalUri
{ *
$sEmail = \trim($aMatch[1]); * @return string
} */
private function getEmailFromPrincipalUri($sPrincipalUri)
return $sEmail; {
} $sEmail = '';
$aMatch = array();
/** if (\preg_match('/\/?principals\/([^@\/]+@[^@\/]+)/i', $sPrincipalUri, $aMatch) && !empty($aMatch[1]))
* @param string $sPrincipalUri {
* $sEmail = \trim($aMatch[1]);
* @return string }
*/
private function getCalendarID($sPrincipalUri) return $sEmail;
{ }
$mUserId = (string) $this->oPersonalAddressBook->GetUserUidByEmail(
$this->getEmailFromPrincipalUri($sPrincipalUri)); /**
* @param string $sPrincipalUri = ''
return !empty($mUserId) ? $mUserId : ''; * @param string $mAddressBookID = ''
} *
* @return string
/** */
* Returns the list of addressbooks for a specific user. private function getAuthEmail($sPrincipalUri = '', $mAddressBookID = '')
* {
* @param string $sPrincipalUri $sGetCurrentUser = \trim($this->oAuthBackend->getCurrentUser());
* @return array if (0 < \strlen($sPrincipalUri) && 0 < \strlen($sGetCurrentUser) &&
*/ $sGetCurrentUser !== $this->getEmailFromPrincipalUri($sPrincipalUri))
public function getAddressBooksForUser($sPrincipalUri) {
{ $sGetCurrentUser = '';
$mAddressBookID = $this->getCalendarID($sPrincipalUri); }
$aAddressBooks = array(); if (0 < \strlen((string) $mAddressBookID) && 0 < \strlen($sGetCurrentUser) &&
if (!empty($mAddressBookID)) (string) $mAddressBookID !== (string) $this->oPersonalAddressBook->GetUserUidByEmail($sGetCurrentUser))
{ {
$aAddressBooks[] = array( $sGetCurrentUser = '';
'id' => $mAddressBookID, }
'uri' => 'default',
'principaluri' => $sPrincipalUri, return $sGetCurrentUser;
'{DAV:}displayname' => 'Personal Address Book', }
'{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => 'Personal Address Book',
'{http://calendarserver.org/ns/}getctag' => 1, /**
'{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}supported-address-data' => new \Sabre\CardDAV\Property\SupportedAddressData() * Returns the list of addressbooks for a specific user.
); *
} * @param string $sPrincipalUri
* @return array
return $aAddressBooks; */
} public function getAddressBooksForUser($sPrincipalUri)
{
/** $aAddressBooks = array();
* Updates an addressbook's properties
* $sEmail = $this->getAuthEmail($sPrincipalUri);
* See Sabre\DAV\IProperties for a description of the mutations array, as if (0 < strlen($sEmail))
* well as the return value. {
* $mAddressBookID = $this->oPersonalAddressBook->GetUserUidByEmail($sEmail);
* @param mixed $mAddressBookId if (!empty($mAddressBookID))
* @param array $aMutations {
* @see Sabre\DAV\IProperties::updateProperties $aAddressBooks[] = array(
* @return bool|array 'id' => $mAddressBookID,
*/ 'uri' => 'default',
public function updateAddressBook($mAddressBookId, array $aMutations) 'principaluri' => $sPrincipalUri,
{ '{DAV:}displayname' => 'Personal Address Book',
return false; '{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => 'Personal Address Book',
} '{http://calendarserver.org/ns/}getctag' => 1,
'{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}supported-address-data' => new \Sabre\CardDAV\Property\SupportedAddressData()
/** );
* Creates a new address book }
* }
* @param string $sPrincipalUri
* @param string $sUrl Just the 'basename' of the url. return $aAddressBooks;
* @param array $aProperties }
*
* @return void /**
*/ * Updates an addressbook's properties
public function createAddressBook($sPrincipalUri, $sUrl, array $aProperties) *
{ * See Sabre\DAV\IProperties for a description of the mutations array, as
} * well as the return value.
*
/** * @param mixed $mAddressBookId
* Deletes an entire addressbook and all its contents * @param array $aMutations
* * @see Sabre\DAV\IProperties::updateProperties
* @param mixed $mAddressBookId * @return bool|array
* */
* @return void public function updateAddressBook($mAddressBookID, array $aMutations)
*/ {
public function deleteAddressBook($mAddressBookId) return false;
{ }
}
/**
private function simpleGetCards($mAddressBookId, $sCardUri = '') * Creates a new address book
{ *
$aResult = array(); * @param string $sPrincipalUri
if (!empty($mAddressBookId)) * @param string $sUrl Just the 'basename' of the url.
{ * @param array $aProperties
$aList = $this->oPersonalAddressBook->GetContacts($mAddressBookId, 0, 20); *
foreach ($aList as /* @var $oItem \RainLoop\Providers\PersonalAddressBook\Classes\Contact */ $oItem) * @return void
{ */
$oVCard = $oItem->ToVCardObject(); public function createAddressBook($sPrincipalUri, $sUrl, array $aProperties)
if ('' === $sCardUri || $sCardUri === $oVCard->UID) {
{ }
$aResult[] = array(
'id' => $oItem->IdContact, /**
'uri' => $oVCard->UID, * Deletes an entire addressbook and all its contents
'lastmodified' => $oItem->Changed, *
'carddata' => $oVCard->serialize() * @param mixed $mAddressBookID
); *
} * @return void
} */
} public function deleteAddressBook($mAddressBookID)
{
return $aResult; }
}
/**
/** * Returns all cards for a specific addressbook id.
* Returns all cards for a specific addressbook id. *
* * This method should return the following properties for each card:
* This method should return the following properties for each card: * * carddata - raw vcard data
* * carddata - raw vcard data * * uri - Some unique url
* * uri - Some unique url * * lastmodified - A unix timestamp
* * lastmodified - A unix timestamp *
* * It's recommended to also return the following properties:
* It's recommended to also return the following properties: * * etag - A unique etag. This must change every time the card changes.
* * etag - A unique etag. This must change every time the card changes. * * size - The size of the card in bytes.
* * size - The size of the card in bytes. *
* * If these last two properties are provided, less time will be spent
* If these last two properties are provided, less time will be spent * calculating them. If they are specified, you can also ommit carddata.
* calculating them. If they are specified, you can also ommit carddata. * This may speed up certain requests, especially with large cards.
* This may speed up certain requests, especially with large cards. *
* * @param mixed $mAddressBookID
* @param mixed $mAddressbookId *
* * @return array
* @return array */
*/ public function getCards($mAddressBookID)
public function getCards($mAddressBookId) {
{ $aResult = array();
return $this->simpleGetCards($mAddressBookId); if (!empty($mAddressBookID))
} {
$sEmail = $this->getAuthEmail('', $mAddressBookID);
/** if (!empty($sEmail))
* Returns a specfic card. {
* $aList = $this->oPersonalAddressBook->GetContacts($sEmail, 0, 500);
* The same set of properties must be returned as with getCards. The only foreach ($aList as /* @var $oItem \RainLoop\Providers\PersonalAddressBook\Classes\Contact */ $oItem)
* exception is that 'carddata' is absolutely required. {
* if (!$oItem->ReadOnly)
* @param mixed $mAddressBookId {
* @param string $sCardUri $sCardData = $oItem->ToVCardObject()->serialize();
* @return array $aResult[] = array(
*/ 'uri' => $oItem->VCardUID(),
public function getCard($mAddressBookId, $sCardUri) 'lastmodified' => $oItem->Changed,
{ 'etag' => \md5($sCardData),
$aList = $this->simpleGetCards($mAddressBookId, $sCardUri); 'size' => \strlen($sCardData)
return 0 < count($aList) && isset($aList) ? $aList[0] : false; );
} }
}
/** }
* Creates a new card. }
*
* The addressbook id will be passed as the first argument. This is the return $aResult;
* 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. * Returns a specfic card.
* *
* It is possible to return an ETag from this method. This ETag is for the * The same set of properties must be returned as with getCards. The only
* newly created resource, and must be enclosed with double quotes (that * exception is that 'carddata' is absolutely required.
* is, the string itself must contain the double quotes). *
* * @param mixed $mAddressBookID
* You should only return the ETag if you store the carddata as-is. If a * @param string $sCardUri
* subsequent GET request on the same card does not have the same body, * @return array
* byte-by-byte and you did return an ETag here, clients tend to get */
* confused. public function getCard($mAddressBookID, $sCardUri)
* {
* If you don't return an ETag, you can just return null. $oContact = null;
* if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4))
* @param mixed $mAddressBookId {
* @param string $sCardUri $sEmail = $this->getAuthEmail('', $mAddressBookID);
* @param string $sCardData if (!empty($sEmail))
* @return string|null {
*/ $oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true);
public function createCard($mAddressBookId, $sCardUri, $sCardData) }
{ }
return null;
// if ($oContact)
// $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)'); {
// $sCardData = $oContact->ToVCardObject()->serialize();
// $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId)); return array(
// 'uri' => $oContact->VCardUID(),
// $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); 'lastmodified' => $oContact->Changed,
// $stmt2->execute(array($addressBookId)); 'etag' => \md5($sCardData),
// 'size' => \strlen($sCardData),
// return '"' . md5($cardData) . '"'; 'carddata' => $sCardData
} );
}
/**
* Updates a card. return false;
* }
* The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getAddressbooksForUser method. /**
* * Creates a new card.
* 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. * The addressbook id will be passed as the first argument. This is the
* * same id as it is returned from the getAddressbooksForUser method.
* 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 * The cardUri is a base uri, and doesn't include the full path. The
* quotes (that is: the string itself must contain the actual quotes). * cardData argument is the vcard body, and is passed as a string.
* *
* You should only return the ETag if you store the carddata as-is. If a * It is possible to return an ETag from this method. This ETag is for the
* subsequent GET request on the same card does not have the same body, * newly created resource, and must be enclosed with double quotes (that
* byte-by-byte and you did return an ETag here, clients tend to get * is, the string itself must contain the double quotes).
* confused. *
* * You should only return the ETag if you store the carddata as-is. If a
* If you don't return an ETag, you can just return null. * 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
* @param mixed $mAddressBookId * confused.
* @param string $sCardUri *
* @param string $sCardData * If you don't return an ETag, you can just return null.
* @return string|null *
*/ * @param mixed $mAddressBookID
public function updateCard($mAddressBookId, $sCardUri, $sCardData) * @param string $sCardUri
{ * @param string $sCardData
return null; *
* @return string|null
// $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?'); */
// $stmt->execute(array($cardData, time(), $cardUri, $addressBookId)); public function createCard($mAddressBookID, $sCardUri, $sCardData)
// {
// $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData))
// $stmt2->execute(array($addressBookId)); {
// $sEmail = $this->getAuthEmail('', $mAddressBookID);
// return '"' . md5($cardData) . '"'; if (!empty($sEmail))
} {
// TODO
/** }
* Deletes a card }
*
* @param mixed $mAddressBookId return null;
* @param string $sCardUri }
*
* @return bool /**
*/ * Updates a card.
public function deleteCard($mAddressBookId, $sCardUri) *
{ * The addressbook id will be passed as the first argument. This is the
return false; * same id as it is returned from the getAddressbooksForUser method.
// $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); *
// $stmt->execute(array($addressBookId, $cardUri)); * 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.
// $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); *
// $stmt2->execute(array($addressBookId)); * 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
// return $stmt->rowCount()===1; * 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)
{
if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData))
{
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
// TODO
}
}
return null;
}
/**
* Deletes a card
*
* @param mixed $mAddressBookID
* @param string $sCardUri
*
* @return bool
*/
public function 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

@ -72,7 +72,6 @@ class Principal extends \Sabre\DAVACL\PrincipalBackend\AbstractBackend
$sGetCurrentUser = $this->oAuthBackend->getCurrentUser(); $sGetCurrentUser = $this->oAuthBackend->getCurrentUser();
if ('principals/'.$sGetCurrentUser === $sPath) if ('principals/'.$sGetCurrentUser === $sPath)
{ {
// var_dump($sPath);
return array( return array(
'uri' => 'principals/'.$sGetCurrentUser, 'uri' => 'principals/'.$sGetCurrentUser,
'{DAV:}displayname' => $sGetCurrentUser, '{DAV:}displayname' => $sGetCurrentUser,
@ -133,7 +132,7 @@ class Principal extends \Sabre\DAVACL\PrincipalBackend\AbstractBackend
*/ */
function updatePrincipal($path, $mutations) function updatePrincipal($path, $mutations)
{ {
return array(); return true;
} }
/** /**

View file

@ -103,6 +103,14 @@ class Service
$bCached = false; $bCached = false;
$sResult = ''; $sResult = '';
$sPathInfo = \trim(\trim($this->oHttp->GetServer('PATH_INFO', '')), ' /'); $sPathInfo = \trim(\trim($this->oHttp->GetServer('PATH_INFO', '')), ' /');
if (!empty($sPathInfo))
{
if ('dav' !== \substr($sPathInfo, 0, 3))
{
$sPathInfo = '';
}
}
if (empty($sPathInfo)) if (empty($sPathInfo))
{ {
$sQuery = \trim(\trim($this->oHttp->GetServer('QUERY_STRING', '')), ' /'); $sQuery = \trim(\trim($this->oHttp->GetServer('QUERY_STRING', '')), ' /');

View file

@ -714,10 +714,13 @@ class ServiceActions
/** /**
* @return string * @return string
*/ */
public function ServiceCardDav() public function ServiceDav()
{ {
return ''; if (!$this->Config()->Get('contacts', 'allow_carddav_sync', false))
{
return '';
}
try try
{ {
\set_error_handler(function ($errno, $errstr, $errfile, $errline ) { \set_error_handler(function ($errno, $errstr, $errfile, $errline ) {
@ -726,9 +729,10 @@ class ServiceActions
$oPersonalAddressBookProvider = $this->oActions->PersonalAddressBookProvider(); $oPersonalAddressBookProvider = $this->oActions->PersonalAddressBookProvider();
$oAuthBackend = new \RainLoop\SabreDAV\AuthBasic($oPersonalAddressBookProvider); // $oAuthBackend = new \RainLoop\SabreDAV\AuthBasic($oPersonalAddressBookProvider);
$oAuthBackend = new \RainLoop\SabreDAV\AuthDigest($oPersonalAddressBookProvider);
$oCarddavBackend = new \RainLoop\SabreDAV\CardDAV($oPersonalAddressBookProvider); $oCarddavBackend = new \RainLoop\SabreDAV\CardDAV($oPersonalAddressBookProvider, $oAuthBackend);
$oPrincipalBackend = new \RainLoop\SabreDAV\Principal($oPersonalAddressBookProvider, $oAuthBackend); $oPrincipalBackend = new \RainLoop\SabreDAV\Principal($oPersonalAddressBookProvider, $oAuthBackend);
@ -740,7 +744,7 @@ class ServiceActions
$oServer = new \Sabre\DAV\Server($aNodes); $oServer = new \Sabre\DAV\Server($aNodes);
$aPath = \trim($this->oHttp->GetPath(), '/\\ '); $aPath = \trim($this->oHttp->GetPath(), '/\\ ');
$oServer->setBaseUri((0 < \strlen($aPath) ? '/'.$aPath.'/' : '').'index.php/carddav/'); $oServer->setBaseUri((0 < \strlen($aPath) ? '/'.$aPath : '').'/index.php/dav/');
// Plugins // Plugins
$oServer->addPlugin(new \Sabre\DAV\Auth\Plugin($oAuthBackend, 'SabreDAV')); $oServer->addPlugin(new \Sabre\DAV\Auth\Plugin($oAuthBackend, 'SabreDAV'));

View file

@ -23,6 +23,10 @@
<i data-bind="css: contactsSharing() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i> <i data-bind="css: contactsSharing() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
Allow contacts sharing Allow contacts sharing
</label> </label>
<label data-bind="click: function () { contactsSync(!contactsSync()); }">
<i data-bind="css: contactsSync() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
Allow contacts sync (CardDAV)
</label>
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">

View file

@ -5316,6 +5316,7 @@ function AdminContacts()
this.defautOptionsAfterRender = Utils.defautOptionsAfterRender; this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
this.enableContacts = ko.observable(!!RL.settingsGet('ContactsEnable')); this.enableContacts = ko.observable(!!RL.settingsGet('ContactsEnable'));
this.contactsSharing = ko.observable(!!RL.settingsGet('ContactsSharing')); this.contactsSharing = ko.observable(!!RL.settingsGet('ContactsSharing'));
this.contactsSync = ko.observable(!!RL.settingsGet('ContactsSync'));
var var
aTypes = ['sqlite', 'mysql', 'pgsql'], aTypes = ['sqlite', 'mysql', 'pgsql'],
@ -5489,6 +5490,12 @@ AdminContacts.prototype.onBuild = function ()
'ContactsSharing': bValue ? '1' : '0' 'ContactsSharing': bValue ? '1' : '0'
}); });
}); });
self.contactsSync.subscribe(function (bValue) {
RL.remote().saveAdminConfig(null, {
'ContactsSync': bValue ? '1' : '0'
});
});
self.contactsType.subscribe(function (sValue) { self.contactsType.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f5, { RL.remote().saveAdminConfig(f5, {

View file

@ -5784,6 +5784,7 @@ EmailModel.prototype.inputoTagLine = function ()
function ContactModel() function ContactModel()
{ {
this.idContact = 0; this.idContact = 0;
this.idContactStr = '';
this.display = ''; this.display = '';
this.properties = []; this.properties = [];
this.readOnly = false; this.readOnly = false;
@ -5835,6 +5836,7 @@ ContactModel.prototype.parse = function (oItem)
if (oItem && 'Object/Contact' === oItem['@Object']) if (oItem && 'Object/Contact' === oItem['@Object'])
{ {
this.idContact = Utils.pInt(oItem['IdContact']); this.idContact = Utils.pInt(oItem['IdContact']);
this.idContactStr = Utils.pString(oItem['IdContactStr']);
this.display = Utils.pString(oItem['Display']); this.display = Utils.pString(oItem['Display']);
this.readOnly = !!oItem['ReadOnly']; this.readOnly = !!oItem['ReadOnly'];
this.scopeType = Utils.pInt(oItem['ScopeType']); this.scopeType = Utils.pInt(oItem['ScopeType']);
@ -9346,6 +9348,7 @@ function PopupsContactsViewModel()
this.viewClearSearch = ko.observable(false); this.viewClearSearch = ko.observable(false);
this.viewID = ko.observable(''); this.viewID = ko.observable('');
this.viewIDStr = ko.observable('');
this.viewReadOnly = ko.observable(false); this.viewReadOnly = ko.observable(false);
this.viewScopeType = ko.observable(Enums.ContactScopeType.Default); this.viewScopeType = ko.observable(Enums.ContactScopeType.Default);
this.viewProperties = ko.observableArray([]); this.viewProperties = ko.observableArray([]);
@ -9549,13 +9552,14 @@ function PopupsContactsViewModel()
self.viewID(Utils.pInt(oData.Result.ResultID)); self.viewID(Utils.pInt(oData.Result.ResultID));
} }
if ('' === self.viewIDStr())
{
self.viewIDStr(Utils.pString(oData.Result.ResultIDStr));
}
self.reloadContactList(); self.reloadContactList();
bRes = true; bRes = true;
} }
// else
// {
// // TODO
// }
_.delay(function () { _.delay(function () {
self.viewSaveTrigger(bRes ? Enums.SaveSettingsStep.TrueResult : Enums.SaveSettingsStep.FalseResult); self.viewSaveTrigger(bRes ? Enums.SaveSettingsStep.TrueResult : Enums.SaveSettingsStep.FalseResult);
@ -9568,7 +9572,7 @@ function PopupsContactsViewModel()
}, 1000); }, 1000);
} }
}, sRequestUid, this.viewID(), this.viewScopeType(), aProperties); }, sRequestUid, this.viewID(), this.viewIDStr(), this.viewScopeType(), aProperties);
}, function () { }, function () {
var var
@ -9712,6 +9716,7 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
{ {
var var
sId = '', sId = '',
sIdStr = '',
bHasName = false, bHasName = false,
aList = [] aList = []
; ;
@ -9725,6 +9730,7 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
if (oContact) if (oContact)
{ {
sId = oContact.idContact; sId = oContact.idContact;
sIdStr = oContact.idContactStr;
if (Utils.isNonEmptyArray(oContact.properties)) if (Utils.isNonEmptyArray(oContact.properties))
{ {
@ -9752,6 +9758,7 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
} }
this.viewID(sId); this.viewID(sId);
this.viewIDStr(sIdStr);
this.viewProperties([]); this.viewProperties([]);
this.viewProperties(aList); this.viewProperties(aList);
@ -15021,11 +15028,12 @@ WebMailAjaxRemoteStorage.prototype.contacts = function (fCallback, iOffset, iLim
/** /**
* @param {?Function} fCallback * @param {?Function} fCallback
*/ */
WebMailAjaxRemoteStorage.prototype.contactSave = function (fCallback, sRequestUid, sUid, nScopeType, aProperties) WebMailAjaxRemoteStorage.prototype.contactSave = function (fCallback, sRequestUid, sUid, sUidStr, nScopeType, aProperties)
{ {
this.defaultRequest(fCallback, 'ContactSave', { this.defaultRequest(fCallback, 'ContactSave', {
'RequestUid': sRequestUid, 'RequestUid': sRequestUid,
'Uid': Utils.trim(sUid), 'Uid': Utils.trim(sUid),
'UidStr': Utils.trim(sUidStr),
'ScopeType': nScopeType, 'ScopeType': nScopeType,
'Properties': aProperties 'Properties': aProperties
}); });