Import contacts (close #44)

+ Some small improvements and fixes
This commit is contained in:
RainLoop Team 2014-01-15 00:02:30 +04:00
parent 9cc791caba
commit 8c0276631b
36 changed files with 628 additions and 66 deletions

View file

@ -5122,6 +5122,235 @@ class Actions
return $this->DefaultResponse(__FUNCTION__, $aResponse);
}
/**
* @param \RainLoop\Account $oAccount
* @param resource $rFile
* @param string $sFileStart
*
* @return int
*/
private function importContactsFromCsvFile($oAccount, $rFile, $sFileStart)
{
$this->Logger()->Write('Import Csv');
$iCount = 0;
$aHeaders = null;
$aData = array();
if ($oAccount && \is_resource($rFile))
{
$oPab = $this->PersonalAddressBookProvider($oAccount);
if ($oPab)
{
$sDelimiter = ((int) \strpos($sFileStart, ',') > (int) \strpos($sFileStart, ';')) ? ',' : ';';
@\setlocale(LC_CTYPE, 'en_US.UTF-8');
while (false !== ($mRow = \fgetcsv($rFile, 5000, $sDelimiter, '"')))
{
if (null === $aHeaders)
{
if (3 >= \count($mRow))
{
return 0;
}
$aHeaders = $mRow;
}
else
{
$aNewItem = array();
foreach ($aHeaders as $iIndex => $sHeaderValue)
{
$aNewItem[@\iconv('utf-8', 'utf-8//IGNORE', $sHeaderValue)] =
isset($mRow[$iIndex]) ? $mRow[$iIndex] : '';
}
$aData[] = $aNewItem;
}
}
if (\is_array($aData) && 0 < \count($aData))
{
$this->Logger()->Write('Start to import '.\count($aData).' contacts from csv file');
$oPab->ImportCsvArray($oAccount->ParentEmailHelper(), $aData);
$iCount = \count($aData);
}
}
}
return $iCount;
}
/**
* @param \RainLoop\Account $oAccount
* @param resource $rFile
* @param string $sFileStart
*
*
* @return int
*/
private function importContactsFromVcfFile($oAccount, $rFile, $sFileStart)
{
$this->Logger()->Write('Import Vcf');
$iCount = 0;
if ($oAccount && \is_resource($rFile))
{
$oPab = $this->PersonalAddressBookProvider($oAccount);
if ($oPab)
{
$sFile = \stream_get_contents($rFile);
if (\is_resource($rFile))
{
@\fclose($rFile);
}
if (is_string($sFile) && 5 < \strlen($sFile))
{
$sFile = \trim($sFile);
if ("\xef\xbb\xbf" === \substr($sFile, 0, 3))
{
$sFile = \substr($sFile, 3);
}
$oVCard = null;
try
{
$oVCardSplitter = new \Sabre\VObject\Splitter\VCard($sFile);
}
catch (\Exception $oExc)
{
$this->Logger()->WriteException($oExc);
};
if ($oVCardSplitter)
{
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
$oVCard = null;
$sEmail = $oAccount->ParentEmailHelper();
$this->Logger()->Write('Start to import contacts from vcf');
while ($oVCard = $oVCardSplitter->getNext())
{
if ($oVCard instanceof \Sabre\VObject\Component\VCard)
{
if (empty($oVCard->UID))
{
$oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID();
}
$oContact->ParseVCard($oVCard, $oVCard->serialize());
if ($oPab->ContactSave($sEmail, $oContact))
{
$iCount++;
}
$oContact->Clear();
}
}
}
}
}
}
return $iCount;
}
/**
* @return array
*/
public function UploadContacts()
{
$oAccount = $this->getAccountFromToken();
$oConfig = $this->Config();
$sInputName = 'uploader';
$mResponse = false;
$iError = UploadError::UNKNOWN;
$iSizeLimit = ((int) $oConfig->Get('webmail', 'attachment_size_limit', 0)) * 1024 * 1024;
if ($oAccount)
{
$oPab = $this->PersonalAddressBookProvider($oAccount);
if ($oPab)
{
$iError = UPLOAD_ERR_OK;
$_FILES = isset($_FILES) ? $_FILES : null;
if (isset($_FILES, $_FILES[$sInputName], $_FILES[$sInputName]['name'], $_FILES[$sInputName]['tmp_name'], $_FILES[$sInputName]['size']))
{
$iError = (isset($_FILES[$sInputName]['error'])) ? (int) $_FILES[$sInputName]['error'] : UPLOAD_ERR_OK;
if (UPLOAD_ERR_OK === $iError && 0 < $iSizeLimit && $iSizeLimit < (int) $_FILES[$sInputName]['size'])
{
$iError = UploadError::CONFIG_SIZE;
}
$sSavedName = 'upload-post-'.md5($_FILES[$sInputName]['name'].$_FILES[$sInputName]['tmp_name']);
if (UPLOAD_ERR_OK === $iError)
{
if (!$this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $_FILES[$sInputName]['tmp_name']))
{
$iError = UploadError::ON_SAVING;
}
$mData = $this->FilesProvider()->GetFile($oAccount, $sSavedName);
if ($mData)
{
$sFileStart = @\fread($mData, 20);
\rewind($mData);
if (false !== $sFileStart)
{
$sFileStart = \trim($sFileStart);
if (false !== \strpos($sFileStart, 'BEGIN:VCARD'))
{
$mResponse = $this->importContactsFromVcfFile($oAccount, $mData, $sFileStart);
}
else if (false !== \strpos($sFileStart, ',') || false !== \strpos($sFileStart, ';'))
{
$mResponse = $this->importContactsFromCsvFile($oAccount, $mData, $sFileStart);
}
}
}
if (\is_resource($mData))
{
@\fclose($mData);
}
unset($mData);
$this->FilesProvider()->Clear($oAccount, $sSavedName);
}
}
else if (!isset($_FILES) || !is_array($_FILES) || 0 === count($_FILES))
{
$iError = UPLOAD_ERR_INI_SIZE;
}
else
{
$iError = UploadError::EMPTY_FILES_DATA;
}
}
}
if (UPLOAD_ERR_OK !== $iError)
{
$iClientError = UploadClientError::NORMAL;
$sError = $this->getUploadErrorMessageByCode($iError, $iClientError);
if (!empty($sError))
{
return $this->FalseResponse(__FUNCTION__, $iClientError, $sError);
}
}
return $this->DefaultResponse(__FUNCTION__, $mResponse);
}
/**
* @return array
*/
@ -5200,7 +5429,7 @@ class Actions
$sError = $this->getUploadErrorMessageByCode($iError, $iClientError);
if (!empty($sError))
{
return $this->FalseResponse($sSavedName, $iClientError, $sError);
return $this->FalseResponse(__FUNCTION__, $iClientError, $sError);
}
}

View file

@ -2,6 +2,8 @@
namespace RainLoop\Providers;
use \RainLoop\Providers\PersonalAddressBook\Enumerations\PropertyType as PropertyType;
class PersonalAddressBook extends \RainLoop\Providers\AbstractProvider
{
/**
@ -181,4 +183,99 @@ class PersonalAddressBook extends \RainLoop\Providers\AbstractProvider
{
return $this->IsActive() ? $this->oDriver->IncFrec($sEmail, $aEmails, $bCreateAuto) : false;
}
/**
* @param string $sCsvName
*
* @return int
*/
private function csvNameToTypeConvertor($sCsvName)
{
static $aMap = null;
if (null === $aMap)
{
$aMap = array(
'Title' => PropertyType::FULLNAME,
'First Name' => PropertyType::FIRST_NAME,
'Middle Name' => PropertyType::MIDDLE_NAME,
'Last Name' => PropertyType::LAST_NAME,
'Suffix' => PropertyType::NAME_SUFFIX,
'Business Fax' => PropertyType::FAX_BUSSINES,
'Business Phone' => PropertyType::PHONE_BUSSINES,
'Business Phone 2' => PropertyType::PHONE_BUSSINES,
'Company Main Phone' => PropertyType::PHONE_BUSSINES,
'Home Fax' => PropertyType::FAX_PERSONAL,
'Home Phone' => PropertyType::PHONE_PERSONAL,
'Home Phone 2' => PropertyType::PHONE_PERSONAL,
'Mobile Phone' => PropertyType::MOBILE_PERSONAL,
'Other Fax' => PropertyType::FAX_OTHER,
'Other Phone' => PropertyType::PHONE_OTHER,
// 'Primary Phone' => PropertyType::PHONE_PERSONAL,
'E-mail Address' => PropertyType::EMAIl_PERSONAL,
'E-mail 2 Address' => PropertyType::EMAIl_OTHER,
'E-mail 3 Address' => PropertyType::EMAIl_OTHER,
'E-mail Display Name' => PropertyType::FULLNAME,
'E-mail 2 Display Name' => PropertyType::FULLNAME,
'E-mail 3 Display Name' => PropertyType::FULLNAME,
'Notes' => PropertyType::NOTE,
'Web Page' => PropertyType::WEB_PAGE_PERSONAL,
'WebPage' => PropertyType::WEB_PAGE_PERSONAL,
);
$aMap = array_change_key_case($aMap, CASE_LOWER);
}
$sCsvNameLower = \MailSo\Base\Utils::IsAscii($sCsvName) ? \strtolower($sCsvName) : '';
return isset($aMap[$sCsvNameLower]) ? $aMap[$sCsvNameLower] : PropertyType::UNKNOWN;
}
/**
* @param string $sEmail
* @param array $aCsvData
*
* @return int
*/
public function ImportCsvArray($sEmail, $aCsvData)
{
$iCount = 0;
if ($this->IsActive() && \is_array($aCsvData) && 0 < \count($aCsvData))
{
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
foreach ($aCsvData as $aItem)
{
foreach ($aItem as $sItemName => $sItemValue)
{
$sItemName = \trim($sItemName);
$sItemValue = \trim($sItemValue);
if (!empty($sItemName) && !empty($sItemValue))
{
$iType = $this->csvNameToTypeConvertor($sItemName);
if (PropertyType::UNKNOWN !== $iType)
{
$oProp = new \RainLoop\Providers\PersonalAddressBook\Classes\Property();
$oProp->Type = $iType;
$oProp->Value = $sItemValue;
$oContact->Properties[] = $oProp;
}
}
}
if ($oContact && 0 < \count($oContact->Properties))
{
if ($this->ContactSave($sEmail, $oContact))
{
$iCount++;
}
}
$oContact->Clear();
}
unset($oContact);
}
return $iCount;
}
}

View file

@ -122,8 +122,8 @@ class Contact
$sFirstName = $oProperty->Value;
}
else if (\in_array($oProperty->Type, array(PropertyType::FULLNAME,
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES,
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER
)))
{
$sOther = $oProperty->Value;
@ -287,6 +287,21 @@ class Contact
}
}
}
if (isset($oVCard->URL))
{
foreach($oVCard->URL as $oUrl)
{
$oTypes = $oEmail ? $oEmail['TYPE'] : null;
$sUrl = $oTypes ? \trim((string) $oUrl) : '';
if ($oTypes && 0 < \strlen($sUrl))
{
$oProp = new Property($oTypes->has('WORK') ? PropertyType::WEB_PAGE_BUSSINES : PropertyType::WEB_PAGE_PERSONAL, $sEmail);
\array_push($aProperties, $oProp);
}
}
}
if (isset($oVCard->TEL))
{
@ -402,6 +417,7 @@ class Contact
break;
case PropertyType::EMAIl_PERSONAL:
case PropertyType::EMAIl_BUSSINES:
case PropertyType::EMAIl_OTHER:
$aParams = array('TYPE' => array('INTERNET'));
$aParams['TYPE'][] = PropertyType::EMAIl_BUSSINES === $oProperty->Type ? 'WORK' : 'HOME';
@ -412,23 +428,33 @@ class Contact
}
$oVCard->add('EMAIL', $oProperty->Value, $aParams);
break;
case PropertyType::WEB_PAGE_PERSONAL:
case PropertyType::WEB_PAGE_BUSSINES:
case PropertyType::WEB_PAGE_OTHER:
$aParams = array('TYPE' => array());
$aParams['TYPE'][] = PropertyType::WEB_PAGE_BUSSINES === $oProperty->Type ? 'WORK' : 'HOME';
$oVCard->add('URL', $oProperty->Value, $aParams);
break;
case PropertyType::PHONE_PERSONAL:
case PropertyType::PHONE_BUSSINES:
case PropertyType::PHONE_OTHER:
case PropertyType::MOBILE_PERSONAL:
case PropertyType::MOBILE_BUSSINES:
case PropertyType::MOBILE_OTHER:
case PropertyType::FAX_PERSONAL:
case PropertyType::FAX_BUSSINES:
case PropertyType::FAX_OTHER:
$aParams = array('TYPE' => array());
$sType = '';
if (\in_array($oProperty->Type, array(PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES)))
if (\in_array($oProperty->Type, array(PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER)))
{
$sType = 'VOICE';
}
else if (\in_array($oProperty->Type, array(PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES)))
else if (\in_array($oProperty->Type, array(PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER)))
{
$sType = 'CELL';
}
else if (\in_array($oProperty->Type, array(PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES)))
else if (\in_array($oProperty->Type, array(PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER)))
{
$sType = 'FAX';
}

View file

@ -70,7 +70,7 @@ class Property
public function IsEmail()
{
return \in_array($this->Type, array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER
));
}
@ -80,9 +80,9 @@ class Property
public function IsPhone()
{
return \in_array($this->Type, array(
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES,
PropertyType::FAX_BUSSINES
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER,
PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER
));
}

View file

@ -18,15 +18,19 @@ class PropertyType
const EMAIl_PERSONAL = 30;
const EMAIl_BUSSINES = 31;
const EMAIl_OTHER = 32;
const PHONE_PERSONAL = 50;
const PHONE_BUSSINES = 51;
const PHONE_OTHER = 52;
const MOBILE_PERSONAL = 60;
const MOBILE_BUSSINES = 61;
const MOBILE_OTHER = 62;
const FAX_PERSONAL = 70;
const FAX_BUSSINES = 71;
const FAX_OTHER = 72;
const FACEBOOK = 90;
const SKYPE = 91;
@ -34,5 +38,9 @@ class PropertyType
const NOTE = 110;
const WEB_PAGE_PERSONAL = 220;
const WEB_PAGE_BUSSINES = 221;
const WEB_PAGE_OTHER = 222;
const CUSTOM = 250;
}

View file

@ -403,9 +403,9 @@ class PdoPersonalAddressBook
($this->bConsiderShare ? ' OR scope_type = :scope_type_share_all' : '').
') AND (prop_value LIKE :search ESCAPE \'=\''.
(0 < \strlen($sCustomSearch) ? ' OR (prop_type IN ('.\implode(',', array(
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES,
PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES
PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER,
PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER,
PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER
)).') AND prop_value_custom <> \'\' AND prop_value_custom LIKE :search_custom_phone)' : '').
') GROUP BY id_contact, id_prop';
@ -729,7 +729,7 @@ class PdoPersonalAddressBook
$iUserID = $this->getUserId($sEmail);
$sTypes = implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::FIRST_NAME, PropertyType::LAST_NAME
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FIRST_NAME, PropertyType::LAST_NAME
));
$sSql = 'SELECT id_contact, id_prop, prop_type, prop_value FROM rainloop_pab_properties '.
@ -759,6 +759,7 @@ class PdoPersonalAddressBook
{
$aIdContacts = array();
$aIdProps = array();
$aContactAllAccess = array();
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
@ -767,10 +768,17 @@ class PdoPersonalAddressBook
{
$iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0;
$iIdProp = $aItem && isset($aItem['id_prop']) ? (int) $aItem['id_prop'] : 0;
$iType = $aItem && isset($aItem['prop_type']) ? (int) $aItem['prop_type'] : 0;
if (0 < $iIdContact && 0 < $iIdProp)
{
$aIdContacts[$iIdContact] = $iIdContact;
$aIdProps[$iIdProp] = $iIdProp;
if (\in_array($iType, array(PropertyType::LAST_NAME, PropertyType::FIRST_NAME)))
{
$aContactAllAccess[$iIdContact] = $iIdContact;
}
}
}
}
@ -783,7 +791,7 @@ class PdoPersonalAddressBook
$oStmt->closeCursor();
$sTypes = \implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::FIRST_NAME, PropertyType::LAST_NAME
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FIRST_NAME, PropertyType::LAST_NAME
));
$sSql = 'SELECT id_prop, id_contact, prop_type, prop_value FROM rainloop_pab_properties '.
@ -815,7 +823,8 @@ class PdoPersonalAddressBook
$aNames[$iIdContact][PropertyType::LAST_NAME === $iType ? 0 : 1] = $aItem['prop_value'];
}
else if (isset($aIdProps[$iIdProp]) && \in_array($iType, array(PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES)))
else if ((isset($aIdProps[$iIdProp]) || isset($aContactAllAccess[$iIdContact]))&&
\in_array($iType, array(PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES)))
{
if (!isset($aEmails[$iIdContact]))
{
@ -827,8 +836,8 @@ class PdoPersonalAddressBook
}
}
$this->writeLog($aNames);
$this->writeLog($aEmails);
// $this->writeLog($aNames);
// $this->writeLog($aEmails);
foreach ($aEmails as $iId => $aItems)
{
@ -886,7 +895,7 @@ class PdoPersonalAddressBook
$iUserID = $this->getUserId($sEmail);
$sTypes = \implode(',', array(
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER
));
$aExists = array();

View file

@ -290,6 +290,12 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface
$sEmail = $this->getAuthEmail('', $mAddressBookID);
if (!empty($sEmail))
{
if (empty($oVCard->UID))
{
$oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID();
$sCardData = $oVCard->serialize();
}
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
$oContact->ParseVCard($oVCard, $sCardData);
@ -358,6 +364,12 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface
$oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true);
if ($oContact && (0 === $iRev || $oContact->Changed < $iRev))
{
if (empty($oVCard->UID))
{
$oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID();
$sCardData = $oVCard->serialize();
}
$oContact->ParseVCard($oVCard, $sCardData);
if ($this->oPersonalAddressBook->ContactSave($sEmail, $oContact) && !empty($oContact->CardDavHash))
{

View file

@ -313,6 +313,14 @@ class ServiceActions
{
return $this->privateUpload('Upload');
}
/**
* @return string
*/
public function ServiceUploadContacts()
{
return $this->privateUpload('UploadContacts');
}
/**
* @return string