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'],
@ -184,6 +185,12 @@ AdminContacts.prototype.onBuild = function ()
}); });
}); });
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, {
'ContactsPdoType': sValue 'ContactsPdoType': sValue

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)
{ case 'pgsql':
$aQ[] = 'CREATE TABLE rainloop_system ( $aQ[] = 'CREATE TABLE rainloop_system (
sys_name varchar(50) NOT NULL, sys_name varchar(50) NOT NULL,
value_int integer NOT NULL DEFAULT 0, value_int integer NOT NULL DEFAULT 0,
value_str varchar(128) NOT NULL DEFAULT \'\' value_str varchar(128) NOT NULL DEFAULT \'\'
);'; );';
$aQ[] = 'CREATE INDEX sys_name_rainloop_system_index ON rainloop_system (sys_name);';
$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;';
$aQ[] = 'CREATE TABLE rainloop_users (
$aQ[] = 'CREATE SEQUENCE id_user START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1;'; id_user integer DEFAULT nextval(\'id_user\'::text) PRIMARY KEY,
rl_email varchar(128) NOT NULL DEFAULT \'\'
$aQ[] = 'CREATE TABLE rainloop_users (
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);';
}
else if ('sqlite' === $this->sDbType)
{
$aQ[] = 'CREATE TABLE rainloop_system (
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

@ -25,7 +25,6 @@ class AuthBasic extends \Sabre\DAV\Auth\Backend\AbstractBasic
*/ */
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

@ -2,7 +2,7 @@
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
@ -14,15 +14,24 @@ class AuthBasic extends \Sabre\DAV\Auth\Backend\AbstractDigest
*/ */
public function __construct($oPersonalAddressBook) public function __construct($oPersonalAddressBook)
{ {
$this->oPersonalAddressBook = $oPersonalAddressBook; $this->oPersonalAddressBook = $oPersonalAddressBook;
} }
/** /**
* @return bool * @param string $sRealm
* @param string $sUserName
*
* @return string|null
*/ */
public function getDigestHash($sRealm, $sUserName) public function getDigestHash($sRealm, $sUserName)
{ {
$this->currentUser = $sUserName; $sHash = $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true);
return true; // TODO if (!empty($sHash))
{
$this->currentUser = $sUserName;
return \md5($sUserName.':'.$sRealm.':'.$sHash);
}
return null;
} }
} }

View file

@ -2,19 +2,27 @@
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;
/**
* @var \RainLoop\SabreDAV\AuthBasic
*/
private $oAuthBackend;
/** /**
* @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook * @param \RainLoop\Providers\PersonalAddressBook $oPersonalAddressBook
* @param \RainLoop\SabreDAV\AuthBasic $oAuthBackend
*/ */
public function __construct($oPersonalAddressBook) public function __construct($oPersonalAddressBook, &$oAuthBackend)
{ {
$this->oPersonalAddressBook = $oPersonalAddressBook; $this->oPersonalAddressBook = $oPersonalAddressBook;
$this->oPersonalAddressBook->ConsiderShare(false);
$this->oAuthBackend = $oAuthBackend;
} }
/** /**
@ -35,16 +43,27 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface {
} }
/** /**
* @param string $sPrincipalUri * @param string $sPrincipalUri = ''
* @param string $mAddressBookID = ''
* *
* @return string * @return string
*/ */
private function getCalendarID($sPrincipalUri) private function getAuthEmail($sPrincipalUri = '', $mAddressBookID = '')
{ {
$mUserId = (string) $this->oPersonalAddressBook->GetUserUidByEmail( $sGetCurrentUser = \trim($this->oAuthBackend->getCurrentUser());
$this->getEmailFromPrincipalUri($sPrincipalUri)); if (0 < \strlen($sPrincipalUri) && 0 < \strlen($sGetCurrentUser) &&
$sGetCurrentUser !== $this->getEmailFromPrincipalUri($sPrincipalUri))
{
$sGetCurrentUser = '';
}
return !empty($mUserId) ? $mUserId : ''; if (0 < \strlen((string) $mAddressBookID) && 0 < \strlen($sGetCurrentUser) &&
(string) $mAddressBookID !== (string) $this->oPersonalAddressBook->GetUserUidByEmail($sGetCurrentUser))
{
$sGetCurrentUser = '';
}
return $sGetCurrentUser;
} }
/** /**
@ -55,20 +74,24 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface {
*/ */
public function getAddressBooksForUser($sPrincipalUri) public function getAddressBooksForUser($sPrincipalUri)
{ {
$mAddressBookID = $this->getCalendarID($sPrincipalUri);
$aAddressBooks = array(); $aAddressBooks = array();
if (!empty($mAddressBookID))
$sEmail = $this->getAuthEmail($sPrincipalUri);
if (0 < strlen($sEmail))
{ {
$aAddressBooks[] = array( $mAddressBookID = $this->oPersonalAddressBook->GetUserUidByEmail($sEmail);
'id' => $mAddressBookID, if (!empty($mAddressBookID))
'uri' => 'default', {
'principaluri' => $sPrincipalUri, $aAddressBooks[] = array(
'{DAV:}displayname' => 'Personal Address Book', 'id' => $mAddressBookID,
'{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => 'Personal Address Book', 'uri' => 'default',
'{http://calendarserver.org/ns/}getctag' => 1, 'principaluri' => $sPrincipalUri,
'{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}supported-address-data' => new \Sabre\CardDAV\Property\SupportedAddressData() '{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()
);
}
} }
return $aAddressBooks; return $aAddressBooks;
@ -85,7 +108,7 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface {
* @see Sabre\DAV\IProperties::updateProperties * @see Sabre\DAV\IProperties::updateProperties
* @return bool|array * @return bool|array
*/ */
public function updateAddressBook($mAddressBookId, array $aMutations) public function updateAddressBook($mAddressBookID, array $aMutations)
{ {
return false; return false;
} }
@ -106,38 +129,14 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface {
/** /**
* Deletes an entire addressbook and all its contents * Deletes an entire addressbook and all its contents
* *
* @param mixed $mAddressBookId * @param mixed $mAddressBookID
* *
* @return void * @return void
*/ */
public function deleteAddressBook($mAddressBookId) public function deleteAddressBook($mAddressBookID)
{ {
} }
private function simpleGetCards($mAddressBookId, $sCardUri = '')
{
$aResult = array();
if (!empty($mAddressBookId))
{
$aList = $this->oPersonalAddressBook->GetContacts($mAddressBookId, 0, 20);
foreach ($aList as /* @var $oItem \RainLoop\Providers\PersonalAddressBook\Classes\Contact */ $oItem)
{
$oVCard = $oItem->ToVCardObject();
if ('' === $sCardUri || $sCardUri === $oVCard->UID)
{
$aResult[] = array(
'id' => $oItem->IdContact,
'uri' => $oVCard->UID,
'lastmodified' => $oItem->Changed,
'carddata' => $oVCard->serialize()
);
}
}
}
return $aResult;
}
/** /**
* Returns all cards for a specific addressbook id. * Returns all cards for a specific addressbook id.
* *
@ -154,13 +153,36 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface {
* 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)
{ {
return $this->simpleGetCards($mAddressBookId); $aResult = array();
if (!empty($mAddressBookID))
{
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
$aList = $this->oPersonalAddressBook->GetContacts($sEmail, 0, 500);
foreach ($aList as /* @var $oItem \RainLoop\Providers\PersonalAddressBook\Classes\Contact */ $oItem)
{
if (!$oItem->ReadOnly)
{
$sCardData = $oItem->ToVCardObject()->serialize();
$aResult[] = array(
'uri' => $oItem->VCardUID(),
'lastmodified' => $oItem->Changed,
'etag' => \md5($sCardData),
'size' => \strlen($sCardData)
);
}
}
}
}
return $aResult;
} }
/** /**
@ -169,14 +191,35 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface {
* The same set of properties must be returned as with getCards. The only * The same set of properties must be returned as with getCards. The only
* exception is that 'carddata' is absolutely required. * exception is that 'carddata' is absolutely required.
* *
* @param mixed $mAddressBookId * @param mixed $mAddressBookID
* @param string $sCardUri * @param string $sCardUri
* @return array * @return array
*/ */
public function getCard($mAddressBookId, $sCardUri) public function getCard($mAddressBookID, $sCardUri)
{ {
$aList = $this->simpleGetCards($mAddressBookId, $sCardUri); $oContact = null;
return 0 < count($aList) && isset($aList) ? $aList[0] : false; 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)
{
$sCardData = $oContact->ToVCardObject()->serialize();
return array(
'uri' => $oContact->VCardUID(),
'lastmodified' => $oContact->Changed,
'etag' => \md5($sCardData),
'size' => \strlen($sCardData),
'carddata' => $sCardData
);
}
return false;
} }
/** /**
@ -199,23 +242,24 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface {
* *
* If you don't return an ETag, you can just return null. * If you don't return an ETag, you can just return null.
* *
* @param mixed $mAddressBookId * @param mixed $mAddressBookID
* @param string $sCardUri * @param string $sCardUri
* @param string $sCardData * @param string $sCardData
*
* @return string|null * @return string|null
*/ */
public function createCard($mAddressBookId, $sCardUri, $sCardData) public function createCard($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; return null;
//
// $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)');
//
// $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId));
//
// $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?');
// $stmt2->execute(array($addressBookId));
//
// return '"' . md5($cardData) . '"';
} }
/** /**
@ -238,41 +282,52 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface {
* *
* If you don't return an ETag, you can just return null. * If you don't return an ETag, you can just return null.
* *
* @param mixed $mAddressBookId * @param mixed $mAddressBookID
* @param string $sCardUri * @param string $sCardUri
* @param string $sCardData * @param string $sCardData
*
* @return string|null * @return string|null
*/ */
public function updateCard($mAddressBookId, $sCardUri, $sCardData) public function updateCard($mAddressBookID, $sCardUri, $sCardData)
{ {
return null; if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData))
{
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
// TODO
}
}
// $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?'); return null;
// $stmt->execute(array($cardData, time(), $cardUri, $addressBookId));
//
// $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?');
// $stmt2->execute(array($addressBookId));
//
// return '"' . md5($cardData) . '"';
} }
/** /**
* Deletes a card * Deletes a card
* *
* @param mixed $mAddressBookId * @param mixed $mAddressBookID
* @param string $sCardUri * @param string $sCardUri
* *
* @return bool * @return bool
*/ */
public function deleteCard($mAddressBookId, $sCardUri) public function deleteCard($mAddressBookID, $sCardUri)
{ {
return false; $bResult = false;
// $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); $oContact = null;
// $stmt->execute(array($addressBookId, $cardUri)); if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4))
// {
// $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); $sEmail = $this->getAuthEmail('', $mAddressBookID);
// $stmt2->execute(array($addressBookId)); if (!empty($sEmail))
// {
// return $stmt->rowCount()===1; $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,9 +714,12 @@ 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
{ {
@ -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'],
@ -5490,6 +5491,12 @@ AdminContacts.prototype.onBuild = function ()
}); });
}); });
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, {
'ContactsPdoType': sValue 'ContactsPdoType': sValue

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
}); });