- Added comprehensive AI Assistant system (aiassist/ directory): * Vector search and embedding capabilities * Typebot proxy integration * Elastic search functionality * Message classification and chat history * MCP proxy for external integrations - Implemented Court Status API (GetCourtStatus.php): * Real-time court document status checking * Integration with external court systems * Comprehensive error handling and logging - Enhanced S3 integration: * Improved file backup system with metadata * Batch processing capabilities * Enhanced error logging and recovery * Copy operations with URL fixing - Added Telegram contact creation API - Improved error logging across all modules - Enhanced callback system for AI responses - Extensive backup file storage with timestamps - Updated documentation and README files - File storage improvements: * Thousands of backup files with proper metadata * Fix operations for broken file references * Project-specific backup and recovery systems * Comprehensive file integrity checking Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
889 lines
33 KiB
PHP
889 lines
33 KiB
PHP
<?php
|
|
/* +***********************************************************************************
|
|
* The contents of this file are subject to the vtiger CRM Public License Version 1.0
|
|
* ("License"); You may not use this file except in compliance with the License
|
|
* The Original Code is: vtiger CRM Open Source
|
|
* The Initial Developer of the Original Code is vtiger.
|
|
* Portions created by vtiger are Copyright (C) vtiger.
|
|
* All Rights Reserved.
|
|
* *********************************************************************************** */
|
|
|
|
vimport('~~/modules/WSAPP/synclib/connectors/TargetConnector.php');
|
|
require_once 'vtlib/Vtiger/Net/Client.php';
|
|
|
|
Class Google_Contacts_Connector extends WSAPP_TargetConnector {
|
|
|
|
protected $apiConnection;
|
|
protected $totalRecords;
|
|
protected $createdRecords;
|
|
protected $maxResults = 100;
|
|
|
|
const CONTACTS_URI = 'https://www.google.com/m8/feeds/contacts/default/full';
|
|
|
|
const CONTACTS_GROUP_URI = 'https://www.google.com/m8/feeds/groups/default/full';
|
|
|
|
const CONTACTS_BATCH_URI = 'https://www.google.com/m8/feeds/contacts/default/full/batch';
|
|
|
|
const USER_PROFILE_INFO = 'https://www.googleapis.com/oauth2/v1/userinfo';
|
|
|
|
protected $NS = array(
|
|
'gd' => 'http://schemas.google.com/g/2005',
|
|
'gContact' => 'http://schemas.google.com/contact/2008',
|
|
'batch' => 'http://schemas.google.com/gdata/batch'
|
|
);
|
|
|
|
protected $apiVersion = '3.0';
|
|
|
|
private $groups = null;
|
|
|
|
private $selectedGroup = null;
|
|
|
|
private $fieldMapping = null;
|
|
|
|
private $maxBatchSize = 100;
|
|
|
|
protected $fields = array(
|
|
'salutationtype' => array(
|
|
'name' => 'gd:namePrefix'
|
|
),
|
|
'firstname' => array(
|
|
'name' => 'gd:givenName'
|
|
),
|
|
'lastname' => array(
|
|
'name' => 'gd:familyName'
|
|
),
|
|
'title' => array(
|
|
'name' => 'gd:orgTitle'
|
|
),
|
|
'organizationname' => array(
|
|
'name' => 'gd:orgName'
|
|
),
|
|
'birthday' => array(
|
|
'name' => 'gContact:birthday'
|
|
),
|
|
'email' => array(
|
|
'name' => 'gd:email',
|
|
'types' => array('home','work','custom')
|
|
),
|
|
'phone' => array(
|
|
'name' => 'gd:phoneNumber',
|
|
'types' => array('mobile','home','work','main','work_fax','home_fax','pager','custom')
|
|
),
|
|
'address' => array(
|
|
'name' => 'gd:structuredPostalAddress',
|
|
'types' => array('home','work','custom')
|
|
),
|
|
'date' => array(
|
|
'name' => 'gContact:event',
|
|
'types' => array('anniversary','custom')
|
|
),
|
|
'description' => array(
|
|
'name' => 'content'
|
|
),
|
|
'custom' => array(
|
|
'name' => 'gContact:userDefinedField'
|
|
),
|
|
'url' => array(
|
|
'name' => 'gContact:website',
|
|
'types' => array('profile','blog','home-page','work','custom')
|
|
)
|
|
);
|
|
|
|
public function __construct($oauth2Connection) {
|
|
$this->apiConnection = $oauth2Connection;
|
|
}
|
|
|
|
/**
|
|
* Get the name of the Google Connector
|
|
* @return string
|
|
*/
|
|
public function getName() {
|
|
return 'GoogleContacts';
|
|
}
|
|
|
|
/**
|
|
* Function to get Fields
|
|
* @return <Array>
|
|
*/
|
|
public function getFields() {
|
|
return $this->fields;
|
|
}
|
|
|
|
/**
|
|
* Function to get the mapped value
|
|
* @param <Array> $valueSet
|
|
* @param <Array> $mapping
|
|
* @return <Mixed>
|
|
*/
|
|
public function getMappedValue($valueSet,$mapping) {
|
|
$key = $mapping['google_field_type'];
|
|
if($key == 'custom')
|
|
$key = $mapping['google_custom_label'];
|
|
return $valueSet[decode_html($key)];
|
|
}
|
|
|
|
/**
|
|
* Function to get field value of google field
|
|
* @param <Array> $googleFieldDetails
|
|
* @param <Google_Contacts_Model> $user
|
|
* @return <Mixed>
|
|
*/
|
|
public function getGoogleFieldValue($googleFieldDetails, $googleRecord, $user) {
|
|
$googleFieldValue = '';
|
|
switch ($googleFieldDetails['google_field_name']) {
|
|
case 'gd:namePrefix' :
|
|
$googleFieldValue = $googleRecord->getNamePrefix();
|
|
break;
|
|
case 'gd:givenName' :
|
|
$googleFieldValue = $googleRecord->getFirstName();
|
|
break;
|
|
case 'gd:familyName' :
|
|
$googleFieldValue = $googleRecord->getLastName();
|
|
break;
|
|
case 'gd:orgTitle' :
|
|
$googleFieldValue = $googleRecord->getTitle();
|
|
break;
|
|
case 'gd:orgName' :
|
|
$googleFieldValue = $googleRecord->getAccountName($user->id);
|
|
break;
|
|
case 'gContact:birthday' :
|
|
$googleFieldValue = $googleRecord->getBirthday();
|
|
break;
|
|
case 'gd:email' :
|
|
$emails = $googleRecord->getEmails();
|
|
$googleFieldValue = $this->getMappedValue($emails, $googleFieldDetails);
|
|
break;
|
|
case 'gd:phoneNumber' :
|
|
$phones = $googleRecord->getPhones();
|
|
$googleFieldValue = $this->getMappedValue($phones, $googleFieldDetails);
|
|
break;
|
|
case 'gd:structuredPostalAddress' :
|
|
$addresses = $googleRecord->getAddresses();
|
|
$googleFieldValue = $this->getMappedValue($addresses, $googleFieldDetails);
|
|
break;
|
|
case 'content' :
|
|
$googleFieldValue = $googleRecord->getDescription();
|
|
break;
|
|
case 'gContact:userDefinedField' :
|
|
$userDefinedFields = $googleRecord->getUserDefineFieldsValues();
|
|
$googleFieldValue = $this->getMappedValue($userDefinedFields, $googleFieldDetails);
|
|
break;
|
|
case 'gContact:website' :
|
|
$websites = $googleRecord->getUrlFields();
|
|
$googleFieldValue = $this->getMappedValue($websites, $googleFieldDetails);
|
|
break;
|
|
}
|
|
return $googleFieldValue;
|
|
}
|
|
|
|
/**
|
|
* Tarsform Google Records to Vtiger Records
|
|
* @param <array> $targetRecords
|
|
* @return <array> tranformed Google Records
|
|
*/
|
|
public function transformToSourceRecord($targetRecords, $user = false) {
|
|
$entity = array();
|
|
$contacts = array();
|
|
|
|
if(!isset($this->fieldMapping)) {
|
|
$this->fieldMapping = Google_Utils_Helper::getFieldMappingForUser($user);
|
|
}
|
|
|
|
foreach ($targetRecords as $googleRecord) {
|
|
if ($googleRecord->getMode() != WSAPP_SyncRecordModel::WSAPP_DELETE_MODE) {
|
|
if(!$user) $user = Users_Record_Model::getCurrentUserModel();
|
|
|
|
$entity = Vtiger_Functions::getMandatoryReferenceFields('Contacts');
|
|
$entity['assigned_user_id'] = vtws_getWebserviceEntityId('Users', $user->id);
|
|
|
|
foreach($this->fieldMapping as $vtFieldName => $googleFieldDetails) {
|
|
$googleFieldValue = $this->getGoogleFieldValue($googleFieldDetails, $googleRecord, $user);
|
|
if($vtFieldName == 'mailingaddress') {
|
|
$address = $googleFieldValue;
|
|
$entity['mailingstreet'] = $address['street'];
|
|
$entity['mailingpobox'] = $address['pobox'];
|
|
$entity['mailingcity'] = $address['city'];
|
|
$entity['mailingstate'] = $address['region'];
|
|
$entity['mailingzip'] = $address['postcode'];
|
|
$entity['mailingcountry'] = $address['country'];
|
|
if(empty($entity['mailingstreet'])) {
|
|
$entity['mailingstreet'] = $address['formattedAddress'];
|
|
}
|
|
} else if($vtFieldName == 'otheraddress') {
|
|
$address = $googleFieldValue;
|
|
$entity['otherstreet'] = $address['street'];
|
|
$entity['otherpobox'] = $address['pobox'];
|
|
$entity['othercity'] = $address['city'];
|
|
$entity['otherstate'] = $address['region'];
|
|
$entity['otherzip'] = $address['postcode'];
|
|
$entity['othercountry'] = $address['country'];
|
|
if(empty($entity['otherstreet'])) {
|
|
$entity['otherstreet'] = $address['formattedAddress'];
|
|
}
|
|
} else {
|
|
$entity[$vtFieldName] = $googleFieldValue;
|
|
}
|
|
}
|
|
|
|
if (empty($entity['lastname'])) {
|
|
if (!empty($entity['firstname'])) {
|
|
$entity['lastname'] = $entity['firstname'];
|
|
} else if(empty($entity['firstname']) && !empty($entity['email'])) {
|
|
$entity['lastname'] = $entity['email'];
|
|
} else if( !empty($entity['mobile']) || !empty($entity['mailingstreet'])) {
|
|
$entity['lastname'] = 'Google Contact';
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
$contact = $this->getSynchronizeController()->getSourceRecordModel($entity);
|
|
|
|
$contact = $this->performBasicTransformations($googleRecord, $contact);
|
|
$contact = $this->performBasicTransformationsToSourceRecords($contact, $googleRecord);
|
|
$contacts[] = $contact;
|
|
}
|
|
return $contacts;
|
|
}
|
|
|
|
/**
|
|
* Pull the contacts from google
|
|
* @param <object> $SyncState
|
|
* @return <array> google Records
|
|
*/
|
|
public function pull($SyncState, $user = false) {
|
|
return $this->getContacts($SyncState, $user);
|
|
}
|
|
|
|
/**
|
|
* Helper to send http request using NetClient
|
|
* @param <String> $url
|
|
* @param <Array> $headers
|
|
* @param <Array> $params
|
|
* @param <String> $method
|
|
* @return <Mixed>
|
|
*/
|
|
protected function fireRequest($url,$headers,$params=array(),$method='POST') {
|
|
$httpClient = new Vtiger_Net_Client($url);
|
|
if(count($headers)) $httpClient->setHeaders($headers);
|
|
switch ($method) {
|
|
case 'POST':
|
|
$response = $httpClient->doPost($params);
|
|
break;
|
|
case 'GET':
|
|
$response = $httpClient->doGet($params);
|
|
break;
|
|
}
|
|
return $response;
|
|
}
|
|
|
|
function fetchContactsFeed($query) {
|
|
$query['alt'] = 'json';
|
|
if($this->apiConnection->isTokenExpired()) $this->apiConnection->refreshToken();
|
|
$headers = array(
|
|
'GData-Version' => $this->apiVersion,
|
|
'Authorization' => $this->apiConnection->token['access_token']['token_type'] . ' ' .
|
|
$this->apiConnection->token['access_token']['access_token'],
|
|
);
|
|
$response = $this->fireRequest(self::CONTACTS_URI, $headers, $query, 'GET');
|
|
return $response;
|
|
}
|
|
|
|
function getContactListFeed($query) {
|
|
$feed = $this->fetchContactsFeed($query);
|
|
$decoded_feed = json_decode($feed,true);
|
|
return $decoded_feed['feed'];
|
|
}
|
|
|
|
function googleFormat($date) {
|
|
return str_replace(' ', 'T', $date);
|
|
}
|
|
|
|
/**
|
|
* Pull the contacts from google
|
|
* @param <object> $SyncState
|
|
* @return <array> google Records
|
|
*/
|
|
public function getContacts($SyncState, $user = false) {
|
|
if(!$user) $user = Users_Record_Model::getCurrentUserModel();
|
|
$query = array(
|
|
'max-results' => $this->maxResults,
|
|
'start-index' => 1,
|
|
'orderby' => 'lastmodified',
|
|
'sortorder' => 'ascending',
|
|
);
|
|
if(!isset($this->selectedGroup))
|
|
$this->selectedGroup = Google_Utils_Helper::getSelectedContactGroupForUser($user);
|
|
|
|
if($this->selectedGroup != '' && $this->selectedGroup != 'all') {
|
|
if($this->selectedGroup == 'none') return array();
|
|
if(!isset($this->groups)) {
|
|
$this->groups = $this->pullGroups(TRUE);
|
|
}
|
|
if(in_array($this->selectedGroup, $this->groups['entry']))
|
|
$query['group'] = $this->selectedGroup;
|
|
else
|
|
return array();
|
|
}
|
|
|
|
if (Google_Utils_Helper::getSyncTime('Contacts', $user)) {
|
|
$query['updated-min'] = $this->googleFormat(Google_Utils_Helper::getSyncTime('Contacts', $user));
|
|
$query['showdeleted'] = 'true';
|
|
}
|
|
|
|
$feed = $this->getContactListFeed($query);
|
|
|
|
$this->totalRecords = $feed['openSearch$totalResults']['$t'];
|
|
$contactRecords = array();
|
|
if (count($feed['entry']) > 0) {
|
|
$lastEntry = end($feed['entry']);
|
|
$maxModifiedTime = date('Y-m-d H:i:s', strtotime(Google_Contacts_Model::vtigerFormat($lastEntry['updated']['$t'])) + 1);
|
|
if ($this->totalRecords > $this->maxResults) {
|
|
if (!Google_Utils_Helper::getSyncTime('Contacts', $user)) {
|
|
$query['updated-min'] = $this->googleFormat(date('Y-m-d H:i:s', strtotime(Google_Contacts_Model::vtigerFormat($lastEntry['updated']['$t']))));
|
|
$query['start-index'] = $this->maxResults;
|
|
}
|
|
if($this->selectedGroup != '' && $this->selectedGroup != 'all') {
|
|
$query['group'] = $this->selectedGroup;
|
|
}
|
|
$query['max-results'] = (5000);
|
|
$query['updated-max'] = $this->googleFormat($maxModifiedTime);
|
|
$extendedFeed = $this->getContactListFeed($query);
|
|
if(is_array($extendedFeed['entry'])) {
|
|
$contactRecords = array_merge($feed['entry'], $extendedFeed['entry']);
|
|
} else {
|
|
$contactRecords = $feed['entry'];
|
|
}
|
|
} else {
|
|
$contactRecords = $feed['entry'];
|
|
}
|
|
}
|
|
|
|
$googleRecords = array();
|
|
foreach ($contactRecords as $i => $contact) {
|
|
$recordModel = Google_Contacts_Model::getInstanceFromValues(array('entity' => $contact));
|
|
$deleted = false;
|
|
if(array_key_exists('gd$deleted', $contact)) {
|
|
$deleted = true;
|
|
}
|
|
if (!$deleted) {
|
|
$recordModel->setType($this->getSynchronizeController()->getSourceType())->setMode(WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE);
|
|
} else {
|
|
$recordModel->setType($this->getSynchronizeController()->getSourceType())->setMode(WSAPP_SyncRecordModel::WSAPP_DELETE_MODE);
|
|
}
|
|
$googleRecords[$contact['id']['$t']] = $recordModel;
|
|
}
|
|
$this->createdRecords = count($googleRecords);
|
|
if (isset($maxModifiedTime)) {
|
|
Google_Utils_Helper::updateSyncTime('Contacts', $maxModifiedTime, $user);
|
|
} else {
|
|
Google_Utils_Helper::updateSyncTime('Contacts', false, $user);
|
|
}
|
|
return $googleRecords;
|
|
}
|
|
|
|
/**
|
|
* Function to send a batch request
|
|
* @param <String> <Xml> $batchFeed
|
|
* @return <Mixed>
|
|
*/
|
|
protected function sendBatchRequest($batchFeed) {
|
|
if($this->apiConnection->isTokenExpired()) $this->apiConnection->refreshToken();
|
|
$headers = array(
|
|
'GData-Version' => $this->apiVersion,
|
|
'Authorization' => $this->apiConnection->token['access_token']['token_type'] . ' ' .
|
|
$this->apiConnection->token['access_token']['access_token'],
|
|
'If-Match' => '*',
|
|
'Content-Type' => 'application/atom+xml',
|
|
);
|
|
$response = $this->fireRequest(self::CONTACTS_BATCH_URI, $headers, $batchFeed);
|
|
return $response;
|
|
}
|
|
|
|
public function mbEncode($str) {
|
|
global $default_charset;
|
|
$convmap = array(0x080, 0xFFFF, 0, 0xFFFF);
|
|
return mb_encode_numericentity(htmlspecialchars($str), $convmap, $default_charset);
|
|
}
|
|
|
|
/**
|
|
* Function to add detail to entry element
|
|
* @param <SimpleXMLElement> $entry
|
|
* @param <Google_Contacts_Model> $entity
|
|
* @param <Users_Record_Model> $user
|
|
*/
|
|
protected function addEntityDetailsToAtomEntry(&$entry,$entity,$user) {
|
|
$gdNS = $this->NS['gd'];
|
|
$gdName = $entry->addChild("name",'',$gdNS);
|
|
if($entity->get('salutationtype')) $gdName->addChild("namePrefix", Google_Utils_Helper::toGoogleXml($entity->get('salutationtype')),$gdNS);
|
|
if($entity->get('firstname')) $gdName->addChild("givenName", Google_Utils_Helper::toGoogleXml($entity->get('firstname')),$gdNS);
|
|
if($entity->get('lastname')) $gdName->addChild("familyName", Google_Utils_Helper::toGoogleXml($entity->get('lastname')),$gdNS);
|
|
$gdRel = $gdNS . '#';
|
|
|
|
if($entity->get('account_id') || $entity->get('title')) {
|
|
$gdOrganization = $entry->addChild("organization",null,$gdNS);
|
|
$gdOrganization->addAttribute("rel","http://schemas.google.com/g/2005#other");
|
|
if($entity->get('account_id')) $gdOrganization->addChild("orgName", Google_Utils_Helper::toGoogleXml($entity->get('account_id')),$gdNS);
|
|
if($entity->get('title')) $gdOrganization->addChild("orgTitle", Google_Utils_Helper::toGoogleXml($entity->get('title')),$gdNS);
|
|
}
|
|
|
|
if(!isset($this->fieldMapping)) {
|
|
$this->fieldMapping = Google_Utils_Helper::getFieldMappingForUser($user);
|
|
}
|
|
|
|
foreach($this->fieldMapping as $vtFieldName => $googleFieldDetails) {
|
|
if(in_array($googleFieldDetails['google_field_name'],array('gd:givenName','gd:familyName','gd:orgTitle','gd:orgName','gd:namePrefix')))
|
|
continue;
|
|
|
|
switch ($googleFieldDetails['google_field_name']) {
|
|
case 'gd:email' :
|
|
if($entity->get($vtFieldName)) {
|
|
$gdEmail = $entry->addChild("email",'',$gdNS);
|
|
if($googleFieldDetails['google_field_type'] == 'custom')
|
|
$gdEmail->addAttribute("label",$this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])));
|
|
else
|
|
$gdEmail->addAttribute("rel",$gdRel . $googleFieldDetails['google_field_type']);
|
|
$gdEmail->addAttribute("address", Google_Utils_Helper::toGoogleXml($entity->get($vtFieldName)));
|
|
if($vtFieldName == 'email')
|
|
$gdEmail->addAttribute("primary",'true');
|
|
}
|
|
break;
|
|
case 'gContact:birthday' :
|
|
if($entity->get('birthday')) {
|
|
$gContactNS = $this->NS['gContact'];
|
|
$gContactBirthday = $entry->addChild("birthday",'',$gContactNS);
|
|
$gContactBirthday->addAttribute("when",$entity->get('birthday'));
|
|
}
|
|
break;
|
|
case 'gd:phoneNumber' :
|
|
if($entity->get($vtFieldName)) {
|
|
$gdPhoneMobile = $entry->addChild("phoneNumber",Google_Utils_Helper::toGoogleXml($entity->get($vtFieldName)),$gdNS);
|
|
if($googleFieldDetails['google_field_type'] == 'custom')
|
|
$gdPhoneMobile->addAttribute("label",$this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])));
|
|
else
|
|
$gdPhoneMobile->addAttribute("rel",$gdRel . $googleFieldDetails['google_field_type']);
|
|
}
|
|
break;
|
|
case 'gd:structuredPostalAddress' :
|
|
if($vtFieldName == 'mailingaddress') {
|
|
if($entity->get('mailingstreet') || $entity->get('mailingpobox') || $entity->get('mailingzip') ||
|
|
$entity->get('mailingcity') || $entity->get('mailingstate') || $entity->get('mailingcountry')) {
|
|
$gdAddressHome = $entry->addChild("structuredPostalAddress",null,$gdNS);
|
|
if($googleFieldDetails['google_field_type'] == 'custom')
|
|
$gdAddressHome->addAttribute("label",$this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])));
|
|
else
|
|
$gdAddressHome->addAttribute("rel",$gdRel . $googleFieldDetails['google_field_type']);
|
|
if($entity->get('mailingstreet')) $gdAddressHome->addChild("street", Google_Utils_Helper::toGoogleXml($entity->get('mailingstreet')),$gdNS);
|
|
if($entity->get('mailingpobox')) $gdAddressHome->addChild("pobox", Google_Utils_Helper::toGoogleXml($entity->get('mailingpobox')),$gdNS);
|
|
if($entity->get('mailingzip')) $gdAddressHome->addChild("postcode", Google_Utils_Helper::toGoogleXml($entity->get('mailingzip')),$gdNS);
|
|
if($entity->get('mailingcity')) $gdAddressHome->addChild("city", Google_Utils_Helper::toGoogleXml($entity->get('mailingcity')),$gdNS);
|
|
if($entity->get('mailingstate')) $gdAddressHome->addChild("region", Google_Utils_Helper::toGoogleXml($entity->get('mailingstate')),$gdNS);
|
|
if($entity->get('mailingcountry')) $gdAddressHome->addChild("country", Google_Utils_Helper::toGoogleXml($entity->get('mailingcountry')),$gdNS);
|
|
}
|
|
} else {
|
|
if($entity->get('otherstreet') || $entity->get('otherpobox') || $entity->get('otherzip') ||
|
|
$entity->get('othercity') || $entity->get('otherstate') || $entity->get('othercountry')) {
|
|
$gdAddressWork = $entry->addChild("structuredPostalAddress",null,$gdNS);
|
|
if($googleFieldDetails['google_field_type'] == 'custom')
|
|
$gdAddressWork->addAttribute("label",$this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])));
|
|
else
|
|
$gdAddressWork->addAttribute("rel",$gdRel . $googleFieldDetails['google_field_type']);
|
|
if($entity->get('otherstreet')) $gdAddressWork->addChild("street", Google_Utils_Helper::toGoogleXml($entity->get('otherstreet')),$gdNS);
|
|
if($entity->get('otherpobox')) $gdAddressWork->addChild("pobox", Google_Utils_Helper::toGoogleXml($entity->get('otherpobox')),$gdNS);
|
|
if($entity->get('otherzip')) $gdAddressWork->addChild("postcode", Google_Utils_Helper::toGoogleXml($entity->get('otherzip')),$gdNS);
|
|
if($entity->get('othercity')) $gdAddressWork->addChild("city", Google_Utils_Helper::toGoogleXml($entity->get('othercity')),$gdNS);
|
|
if($entity->get('otherstate')) $gdAddressWork->addChild("region", Google_Utils_Helper::toGoogleXml($entity->get('otherstate')),$gdNS);
|
|
if($entity->get('othercountry')) $gdAddressWork->addChild("country", Google_Utils_Helper::toGoogleXml($entity->get('othercountry')),$gdNS);
|
|
}
|
|
}
|
|
break;
|
|
case 'content' :
|
|
if($entity->get($vtFieldName)) $entry->addChild('content', Google_Utils_Helper::toGoogleXml($entity->get($vtFieldName)));
|
|
break;
|
|
case 'gContact:userDefinedField' :
|
|
if($entity->get($vtFieldName) && $googleFieldDetails['google_custom_label']) {
|
|
$userDefinedField = $entry->addChild('userDefinedField','',$this->NS['gContact']);
|
|
$userDefinedField->addAttribute('key', $this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])));
|
|
$userDefinedField->addAttribute('value', Google_Utils_Helper::toGoogleXml($this->mbEncode($entity->get($vtFieldName))));
|
|
}
|
|
break;
|
|
case 'gContact:website' :
|
|
if($entity->get($vtFieldName)) {
|
|
$websiteField = $entry->addChild('website','',$this->NS['gContact']);
|
|
if($googleFieldDetails['google_field_type'] == 'custom')
|
|
$websiteField->addAttribute('label',$this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])));
|
|
else
|
|
$websiteField->addAttribute('rel',$googleFieldDetails['google_field_type']);
|
|
$websiteField->addAttribute('href', Google_Utils_Helper::toGoogleXml($this->mbEncode($entity->get($vtFieldName))));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Function to add update entry to the atomfeed
|
|
* @param <SimpleXMLElement> $feed
|
|
* @param <Google_Contacts_Model> $entity
|
|
* @param <Users_Record_Model> $user
|
|
*/
|
|
protected function addUpdateContactEntry(&$feed,$entity,$user,$contactsGroupMap=array()) {
|
|
$batchNS = $this->NS['batch'];
|
|
$baseEntryId = $entryId = $entity->get('_id');
|
|
$entryId = str_replace('/base/','/full/',$entryId);
|
|
//fix for issue https://code.google.com/p/gdata-issues/issues/detail?id=2129
|
|
$entry = $feed->addChild('entry');
|
|
$entry->addChild('id','update',$batchNS);
|
|
$batchOperation = $entry->addChild('operation','',$batchNS);
|
|
$batchOperation->addAttribute('type','update');
|
|
$category = $entry->addChild("category");
|
|
$category->addAttribute("scheme","http://schemas.google.com/g/2005#kind");
|
|
$category->addAttribute("term","http://schemas.google.com/g/2008#contact");
|
|
$entry->addChild('id',$entryId);
|
|
|
|
if(!$user) $user = Users_Record_Model::getCurrentUserModel ();
|
|
|
|
if(!isset($this->selectedGroup))
|
|
$this->selectedGroup = Google_Utils_Helper::getSelectedContactGroupForUser($user);
|
|
|
|
if(array_key_exists($baseEntryId, $contactsGroupMap)) {
|
|
$groupMemberShip = $entry->addChild('groupMembershipInfo','',$this->NS['gContact']);
|
|
$groupMemberShip->addAttribute('href',$contactsGroupMap[$baseEntryId]);
|
|
} elseif($this->selectedGroup != '' && $this->selectedGroup != 'all') {
|
|
$groupMemberShip = $entry->addChild('groupMembershipInfo','',$this->NS['gContact']);
|
|
$groupMemberShip->addAttribute('href',$this->selectedGroup);
|
|
}
|
|
|
|
$this->addEntityDetailsToAtomEntry($entry, $entity, $user);
|
|
}
|
|
|
|
/**
|
|
* Function to add delete contact entry to atom feed
|
|
* @param <SimpleXMLElement> $feed
|
|
* @param <Google_Contacts_Model> $entity
|
|
*/
|
|
protected function addDeleteContactEntry(&$feed,$entity) {
|
|
$batchNS = $this->NS['batch'];
|
|
$entryId = $entity->get('_id');
|
|
$entryId = str_replace('/base/','/full/',$entryId);
|
|
//fix for issue https://code.google.com/p/gdata-issues/issues/detail?id=2129
|
|
$entry = $feed->addChild('entry');
|
|
$entry->addChild('id','delete',$batchNS);
|
|
$batchOperation = $entry->addChild('operation','',$batchNS);
|
|
$batchOperation->addAttribute('type','delete');
|
|
$entry->addChild('id',$entryId);
|
|
}
|
|
|
|
/**
|
|
* Function to add create entry to the atomfeed
|
|
* @param <SimpleXMLElement> $feed
|
|
* @param <Google_Contacts_Model> $entity
|
|
* @param <Users_Record_Model> $user
|
|
*/
|
|
protected function addCreateContactEntry(&$feed,$entity,$user) {
|
|
$entry = $feed->addChild("entry");
|
|
$batchNS = $this->NS['batch'];
|
|
$entry->addChild("id","create",$batchNS);
|
|
$batchOperation = $entry->addChild("operation",'',$batchNS);
|
|
$batchOperation->addAttribute("type","insert");
|
|
$category = $entry->addChild("category");
|
|
$category->addAttribute("scheme","http://schemas.google.com/g/2005#kind");
|
|
$category->addAttribute("term","http://schemas.google.com/g/2008#contact");
|
|
|
|
if(!$user) $user = Users_Record_Model::getCurrentUserModel ();
|
|
|
|
if(!isset($this->selectedGroup))
|
|
$this->selectedGroup = Google_Utils_Helper::getSelectedContactGroupForUser($user);
|
|
|
|
if($this->selectedGroup != '' && $this->selectedGroup != 'all') {
|
|
$groupMemberShip = $entry->addChild('groupMembershipInfo','',$this->NS['gContact']);
|
|
$groupMemberShip->addAttribute('href',$this->selectedGroup);
|
|
}
|
|
|
|
$this->addEntityDetailsToAtomEntry($entry, $entity, $user);
|
|
}
|
|
|
|
/**
|
|
* Function to add Retreive entry to atomfeed
|
|
* @param <SimpleXMLElement> $feed
|
|
* @param <Google_Contacts_Model> $entity
|
|
* @param <Users_Record_Model> $user
|
|
*/
|
|
protected function addRetrieveContactEntry(&$feed, $entity, $user) {
|
|
$entryId = $entity->get('_id');
|
|
$entryId = str_replace('/base/','/full/',$entryId);
|
|
$entry = $feed->addChild("entry");
|
|
$batchNS = $this->NS['batch'];
|
|
$entry->addChild("id","retrieve",$batchNS);
|
|
$batchOperation = $entry->addChild("operation",'',$batchNS);
|
|
$batchOperation->addAttribute("type","query");
|
|
$entry->addChild('id',$entryId);
|
|
}
|
|
|
|
/**
|
|
* Function to get GoogleContacts-ContactsGroup map for the supplied records
|
|
* @global $default_charset
|
|
* @param <Array> $records
|
|
* @param <Users_Record_Model> $user
|
|
* @return <Array>
|
|
*/
|
|
protected function googleContactsGroupMap($records,$user) {
|
|
global $default_charset;
|
|
$contactsGroupMap = array();
|
|
|
|
$atom = new SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?>
|
|
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008'
|
|
xmlns:gd='http://schemas.google.com/g/2005' xmlns:batch='http://schemas.google.com/gdata/batch' />");
|
|
|
|
foreach($records as $record) {
|
|
$entity = $record->get('entity');
|
|
$this->addRetrieveContactEntry($atom, $entity, $user);
|
|
}
|
|
|
|
$payLoad = html_entity_decode($atom->asXML(), ENT_QUOTES, $default_charset);
|
|
$response = $this->sendBatchRequest($payLoad);
|
|
if($response) {
|
|
$responseXml = simplexml_load_string($response);
|
|
$responseXml->registerXPathNamespace('gd', $this->NS['gd']);
|
|
$responseXml->registerXPathNamespace('gContact', $this->NS['gContact']);
|
|
$responseXml->registerXPathNamespace('batch', $this->NS['batch']);
|
|
|
|
foreach($responseXml->entry as $entry) {
|
|
$entryXML = $entry->asXML();
|
|
$p = xml_parser_create();
|
|
xml_parse_into_struct($p, $entryXML, $xmlList, $index);
|
|
xml_parser_free($p);
|
|
|
|
if(count($xmlList)) {
|
|
foreach($xmlList as $tagDetails) {
|
|
|
|
if($tagDetails['tag'] == 'ID') {
|
|
$googleContactId = $tagDetails['value'];
|
|
}
|
|
|
|
if($tagDetails['tag'] == 'GCONTACT:GROUPMEMBERSHIPINFO') {
|
|
$attribs = $tagDetails['attributes'];
|
|
$googleContactGroupId = $attribs['HREF'];
|
|
}
|
|
|
|
if(isset($googleContactId) && isset($googleContactGroupId)) {
|
|
$contactsGroupMap[$googleContactId] = $googleContactGroupId;
|
|
unset($googleContactId);unset($googleContactGroupId);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $contactsGroupMap;
|
|
}
|
|
|
|
/**
|
|
* Function to push records in a batch
|
|
* https://developers.google.com/google-apps/contacts/v3/index#batch_operations
|
|
* @global <String> $default_charset
|
|
* @param <Array> $records
|
|
* @param <Users_Record_Model> $user
|
|
* @return <Array> - pushedRecords
|
|
*/
|
|
protected function pushChunk($records,$user) {
|
|
global $default_charset;
|
|
$atom = new SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?>
|
|
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008'
|
|
xmlns:gd='http://schemas.google.com/g/2005' xmlns:batch='http://schemas.google.com/gdata/batch' />");
|
|
|
|
if($records[0]->getMode() == WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE) {
|
|
$contactsGroupMap = $this->googleContactsGroupMap($records,$user);
|
|
}
|
|
foreach ($records as $record) {
|
|
$entity = $record->get('entity');
|
|
try {
|
|
if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE) {
|
|
$this->addUpdateContactEntry($atom,$entity,$user, $contactsGroupMap);
|
|
} else if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_DELETE_MODE) {
|
|
$this->addDeleteContactEntry($atom,$entity);
|
|
} else {
|
|
$this->addCreateContactEntry($atom,$entity,$user);
|
|
}
|
|
} catch (Exception $e) {
|
|
continue;
|
|
}
|
|
}
|
|
$payLoad = html_entity_decode($atom->asXML(), ENT_QUOTES, $default_charset);
|
|
$response = $this->sendBatchRequest($payLoad);
|
|
$responseXml = simplexml_load_string($response);
|
|
if($responseXml) {
|
|
$responseXml->registerXPathNamespace('gd', $this->NS['gd']);
|
|
$responseXml->registerXPathNamespace('gContact', $this->NS['gContact']);
|
|
$responseXml->registerXPathNamespace('batch', $this->NS['batch']);
|
|
foreach ($records as $index => $record) {
|
|
$newEntity = array();
|
|
$entry = $responseXml->entry[$index];
|
|
$newEntityId = (string)$entry->id;
|
|
$newEntity['id']['$t'] = str_replace('/full/', '/base/', $newEntityId);
|
|
$newEntity['updated']['$t'] = (string)$entry->updated[0];
|
|
$record->set('entity', $newEntity);
|
|
}
|
|
}
|
|
return $records;
|
|
}
|
|
|
|
/**
|
|
* Function to push records in batch of maxBatchSize
|
|
* @param <Array Google_Contacts_Model> $records
|
|
* @param <Users_Record_Model> $user
|
|
* @return <Array> - pushed records
|
|
*/
|
|
protected function batchPush($records,$user) {
|
|
$chunks = array_chunk($records, $this->maxBatchSize);
|
|
$mergedRecords = array();
|
|
foreach($chunks as $chunk) {
|
|
$pushedRecords = $this->pushChunk($chunk, $user);
|
|
$mergedRecords = array_merge($mergedRecords,$pushedRecords);
|
|
}
|
|
return $mergedRecords;
|
|
}
|
|
|
|
/**
|
|
* Push the vtiger records to google
|
|
* @param <array> $records vtiger records to be pushed to google
|
|
* @return <array> pushed records
|
|
*/
|
|
public function push($records, $user = false) {
|
|
if(!$user) $user = Users_Record_Model::getCurrentUserModel();
|
|
|
|
if(!isset($this->selectedGroup))
|
|
$this->selectedGroup = Google_Utils_Helper::getSelectedContactGroupForUser($user);
|
|
|
|
if($this->selectedGroup != '' && $this->selectedGroup != 'all') {
|
|
if($this->selectedGroup == 'none') return array();
|
|
if(!isset($this->groups)) {
|
|
$this->groups = $this->pullGroups(TRUE);
|
|
}
|
|
if(!in_array($this->selectedGroup, $this->groups['entry']))
|
|
return array();
|
|
}
|
|
|
|
$updateRecords = $deleteRecords = $addRecords = array();
|
|
foreach($records as $record) {
|
|
if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE) {
|
|
$updateRecords[] = $record;
|
|
} else if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_DELETE_MODE) {
|
|
$deleteRecords[] = $record;
|
|
} else {
|
|
$addRecords[] = $record;
|
|
}
|
|
}
|
|
|
|
if(count($deleteRecords)) {
|
|
$deletedRecords = $this->batchPush($deleteRecords, $user);
|
|
}
|
|
|
|
if(count($updateRecords)) {
|
|
$updatedRecords = $this->batchPush($updateRecords, $user);
|
|
}
|
|
|
|
if(count($addRecords)) {
|
|
$addedRecords = $this->batchPush($addRecords, $user);
|
|
}
|
|
|
|
$i = $j = $k = 0;
|
|
foreach($records as $record) {
|
|
if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE) {
|
|
$uprecord = $updatedRecords[$i++];
|
|
$newEntity = $uprecord->get('entity');
|
|
$record->set('entity',$newEntity);
|
|
} else if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_DELETE_MODE) {
|
|
$delrecord = $deletedRecords[$j++];
|
|
$newEntity = $delrecord->get('entity');
|
|
$record->set('entity',$newEntity);
|
|
} else {
|
|
$adrecord = $addedRecords[$k++];
|
|
$newEntity = $adrecord->get('entity');
|
|
$record->set('entity',$newEntity);
|
|
}
|
|
}
|
|
return $records;
|
|
}
|
|
|
|
/**
|
|
* Tarsform Vtiger Records to Google Records
|
|
* @param <array> $vtContacts
|
|
* @return <array> tranformed vtiger Records
|
|
*/
|
|
public function transformToTargetRecord($vtContacts) {
|
|
$records = array();
|
|
foreach ($vtContacts as $vtContact) {
|
|
$recordModel = Google_Contacts_Model::getInstanceFromValues(array('entity' => $vtContact));
|
|
$recordModel->setType($this->getSynchronizeController()->getSourceType())->setMode($vtContact->getMode())->setSyncIdentificationKey($vtContact->get('_syncidentificationkey'));
|
|
$recordModel = $this->performBasicTransformations($vtContact, $recordModel);
|
|
$recordModel = $this->performBasicTransformationsToTargetRecords($recordModel, $vtContact);
|
|
$records[] = $recordModel;
|
|
}
|
|
return $records;
|
|
}
|
|
|
|
/**
|
|
* returns if more records exits or not
|
|
* @return <boolean> true or false
|
|
*/
|
|
public function moreRecordsExits() {
|
|
return ($this->totalRecords - $this->createdRecords > 0) ? true : false;
|
|
}
|
|
|
|
/**
|
|
* Function to pull contact groups for user
|
|
* @param <Boolean> $onlyIds
|
|
* @return <Array>
|
|
*/
|
|
public function pullGroups($onlyIds = FALSE) {
|
|
//max-results: If you want to receive all of the groups, rather than only the default maximum.
|
|
$query = array(
|
|
'alt' => 'json',
|
|
'max-results' => 1000,
|
|
);
|
|
if($this->apiConnection->isTokenExpired()) $this->apiConnection->refreshToken();
|
|
$headers = array(
|
|
'GData-Version' => $this->apiVersion,
|
|
'Authorization' => $this->apiConnection->token['access_token']['token_type'] . ' ' .
|
|
$this->apiConnection->token['access_token']['access_token']
|
|
);
|
|
$response = $this->fireRequest(self::CONTACTS_GROUP_URI, $headers,$query,'GET');
|
|
$decoded_resp = json_decode($response,true);
|
|
$feed = $decoded_resp['feed'];
|
|
$entries = $feed['entry'];
|
|
$groups = array(
|
|
'title' => $feed['title']['$t']
|
|
);
|
|
if(is_array($entries)) {
|
|
foreach($entries as $entry) {
|
|
$group = array(
|
|
'id' => $entry['id']['$t'],
|
|
'title' => $entry['title']['$t']
|
|
);
|
|
if($onlyIds) $group = $group['id'];
|
|
$groups['entry'][] = $group;
|
|
}
|
|
}
|
|
return $groups;
|
|
}
|
|
|
|
/**
|
|
* Function to get user profile info
|
|
* @return <Mixed>
|
|
*/
|
|
public function getUserProfileInfo() {
|
|
if($this->apiConnection->isTokenExpired()) $this->apiConnection->refreshToken();
|
|
$headers = array(
|
|
'GData-Version' => $this->apiVersion,
|
|
'Authorization' => $this->apiConnection->token['access_token']['token_type'] . ' ' .
|
|
$this->apiConnection->token['access_token']['access_token'],
|
|
'If-Match' => '*',
|
|
'Content-Type' => 'application/json',
|
|
);
|
|
$response = $this->fireRequest(self::USER_PROFILE_INFO, $headers, array(), 'GET');
|
|
return $response;
|
|
}
|
|
}
|