- 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.
427 lines
18 KiB
PHP
427 lines
18 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');
|
|
vimport('~~/libraries/google-api-php-client/src/Google/Client.php');
|
|
vimport('~~/libraries/google-api-php-client/src/Google/Service/Calendar.php');
|
|
|
|
Class Google_Calendar_Connector extends WSAPP_TargetConnector {
|
|
|
|
const maxBatchRequestCount = 500;
|
|
|
|
protected $apiConnection;
|
|
protected $totalRecords;
|
|
protected $maxResults = 1000;
|
|
protected $createdRecords;
|
|
|
|
protected $client;
|
|
protected $service;
|
|
|
|
protected $eventCalendarFieldMappingTableName = 'vtiger_google_event_calendar_mapping';
|
|
protected $calendars;
|
|
|
|
public function __construct($oauth2Connection) {
|
|
$this->apiConnection = $oauth2Connection;
|
|
$this->client = new Google_Client();
|
|
$this->client->setClientId($oauth2Connection->getClientId());
|
|
$this->client->setClientSecret($oauth2Connection->getClientSecret());
|
|
$this->client->setRedirectUri($oauth2Connection->getRedirectUri());
|
|
$this->client->setScopes($oauth2Connection->getScope());
|
|
$this->client->setAccessType($oauth2Connection->getAccessType());
|
|
$this->client->setApprovalPrompt($oauth2Connection->getApprovalPrompt());
|
|
try {
|
|
$this->client->setAccesstoken($oauth2Connection->getAccessToken());
|
|
} catch(Exception $e) {} //suppressing invalid access-token exception
|
|
$this->service = new Google_Service_Calendar($this->client);
|
|
}
|
|
|
|
public function getName() {
|
|
return 'GoogleCalendar';
|
|
}
|
|
|
|
public function emailLookUp($emailIds) {
|
|
$db = PearDatabase::getInstance();
|
|
$sql = 'SELECT crmid FROM vtiger_emailslookup WHERE setype = "Contacts" AND value IN (' . generateQuestionMarks($emailIds) . ')';
|
|
$result = $db->pquery($sql,$emailIds);
|
|
$crmIds = array();
|
|
for($i=0;$i<$db->num_rows($result);$i++) {
|
|
$crmIds[] = $db->query_result($result,$i,'crmid');
|
|
}
|
|
return $crmIds;
|
|
}
|
|
|
|
/**
|
|
* Tarsform Google Records to Vtiger Records
|
|
* @param <array> $targetRecords
|
|
* @return <array> tranformed Google Records
|
|
*/
|
|
public function transformToSourceRecord($targetRecords, $user = false) {
|
|
$entity = array();
|
|
$calendarArray = array();
|
|
foreach ($targetRecords as $googleRecord) {
|
|
if ($googleRecord->getMode() != WSAPP_SyncRecordModel::WSAPP_DELETE_MODE) {
|
|
if(!$user)
|
|
$user = Users_Record_Model::getCurrentUserModel();
|
|
$entity = Vtiger_Functions::getMandatoryReferenceFields('Events');
|
|
$entity['assigned_user_id'] = vtws_getWebserviceEntityId('Users', $user->id);
|
|
$entity['subject'] = $googleRecord->getSubject();
|
|
$entity['date_start'] = $googleRecord->getStartDate($user);
|
|
$entity['location'] = $googleRecord->getWhere();
|
|
$entity['time_start'] = $googleRecord->getStartTimeUTC($user);
|
|
$entity['due_date'] = $googleRecord->getEndDate($user);
|
|
$entity['time_end'] = $googleRecord->getEndTimeUTC($user);
|
|
$entity['eventstatus'] = "Planned";
|
|
$entity['activitytype'] = "Meeting";
|
|
$entity['description'] = $googleRecord->getDescription();
|
|
$entity['duration_hours'] = '00:00';
|
|
$entity['visibility'] = $googleRecord->getVisibility($user);
|
|
if (empty($entity['subject'])) {
|
|
$entity['subject'] = 'Google Event';
|
|
}
|
|
$attendees = $googleRecord->getAttendees();
|
|
$entity['contactidlist'] = '';
|
|
if(count($attendees)) {
|
|
$contactIds = $this->emailLookUp($attendees);
|
|
if(count($contactIds)) {
|
|
$entity['contactidlist'] = implode(';', $contactIds);
|
|
}
|
|
}
|
|
}
|
|
|
|
$calendar = $this->getSynchronizeController()->getSourceRecordModel($entity);
|
|
|
|
$calendar = $this->performBasicTransformations($googleRecord, $calendar);
|
|
$calendar = $this->performBasicTransformationsToSourceRecords($calendar, $googleRecord);
|
|
$calendarArray[] = $calendar;
|
|
}
|
|
|
|
return $calendarArray;
|
|
}
|
|
|
|
/**
|
|
* Pull the events from google
|
|
* @param <object> $SyncState
|
|
* @return <array> google Records
|
|
*/
|
|
public function pull($SyncState, $user = false) {
|
|
try {
|
|
return $this->getCalendar($SyncState, $user);
|
|
} catch (Exception $e) {
|
|
return array();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function to convert datetime to RFC 3339 timestamp
|
|
* @param <String> $date
|
|
* @return <DateTime>
|
|
*/
|
|
function googleFormat($date) {
|
|
$datTime = new DateTime($date);
|
|
$timeZone = new DateTimeZone('UTC');
|
|
$datTime->setTimezone($timeZone);
|
|
$googleFormat = $datTime->format('Y-m-d\TH:i:s\Z');
|
|
return $googleFormat;
|
|
}
|
|
|
|
/**
|
|
* Pull the events from google
|
|
* @param <object> $SyncState
|
|
* @return <array> google Records
|
|
*/
|
|
public function getCalendar($SyncState, $user = false) {
|
|
if($this->apiConnection->isTokenExpired()) {
|
|
$this->apiConnection->refreshToken();
|
|
$this->client->setAccessToken($this->apiConnection->getAccessToken());
|
|
$this->service = new Google_Service_Calendar($this->client);
|
|
}
|
|
$query = array(
|
|
'maxResults' => $this->maxResults,
|
|
'orderBy' => 'updated',
|
|
'singleEvents' => true,
|
|
);
|
|
|
|
if (Google_Utils_Helper::getSyncTime('Calendar', $user)) {
|
|
$query['updatedMin'] = $this->googleFormat(Google_Utils_Helper::getSyncTime('Calendar', $user));
|
|
//shows deleted by default
|
|
}
|
|
|
|
$calendarId = Google_Utils_Helper::getSelectedCalendarForUser($user);
|
|
if(!isset($this->calendars)) {
|
|
$this->calendars = $this->pullCalendars(true);
|
|
}
|
|
if(!in_array($calendarId, $this->calendars)) {
|
|
$calendarId = 'primary';
|
|
}
|
|
|
|
try {
|
|
$feed = $this->service->events->listEvents($calendarId,$query);
|
|
} catch (Exception $e) {
|
|
if($e->getCode() == 410) {
|
|
$query['showDeleted'] = false;
|
|
$feed = $this->service->events->listEvents($calendarId,$query);
|
|
}
|
|
}
|
|
|
|
$calendarRecords = array();
|
|
if($feed) {
|
|
$calendarRecords = $feed->getItems();
|
|
if($feed->getNextPageToken()) $this->totalRecords = $this->maxResults + 1;
|
|
}
|
|
|
|
if (count($calendarRecords) > 0) {
|
|
$maxModifiedTime = date('Y-m-d H:i:s', strtotime(Google_Contacts_Model::vtigerFormat(end($calendarRecords)->getUpdated())) + 1);
|
|
}
|
|
|
|
$googleRecords = array();
|
|
$googleEventIds = array();
|
|
foreach ($calendarRecords as $i => $calendar) {
|
|
$recordModel = Google_Calendar_Model::getInstanceFromValues(array('entity' => $calendar));
|
|
$deleted = false;
|
|
if ($calendar->getStatus() == 'cancelled') {
|
|
$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[$calendar->getId()] = $recordModel;
|
|
$googleEventIds[] = $calendar->getId();
|
|
}
|
|
$this->createdRecords = count($googleRecords);
|
|
if (isset($maxModifiedTime)) {
|
|
Google_Utils_Helper::updateSyncTime('Calendar', $maxModifiedTime, $user);
|
|
} else {
|
|
Google_Utils_Helper::updateSyncTime('Calendar', false, $user);
|
|
}
|
|
if(count($googleEventIds)) {
|
|
$this->putGoogleEventCalendarMap($googleEventIds, $calendarId, $user);
|
|
}
|
|
return $googleRecords;
|
|
}
|
|
|
|
protected function putGoogleEventCalendarMap($event_ids, $calendar_id, $user) {
|
|
if(is_array($event_ids) && count($event_ids)) {
|
|
$db = PearDatabase::getInstance();
|
|
$user_id = $user->getId();
|
|
$sql = 'INSERT INTO vtiger_google_event_calendar_mapping (event_id, calendar_id, user_id) VALUES ';
|
|
$sqlParams = array();
|
|
foreach($event_ids as $event_id) {
|
|
$sql .= '(?, ?, ?),';
|
|
$sqlParams[] = $event_id;
|
|
$sqlParams[] = $calendar_id;
|
|
$sqlParams[] = $user_id;
|
|
}
|
|
$sql = substr_replace($sql, "", -1);
|
|
$db->pquery('DELETE FROM vtiger_google_event_calendar_mapping WHERE event_id IN ('.generateQuestionMarks($event_ids).')',$event_ids);
|
|
$db->pquery($sql,$sqlParams);
|
|
}
|
|
}
|
|
|
|
protected function getGoogleEventCalendarMap($user) {
|
|
$db = PearDatabase::getInstance();
|
|
$map = array();
|
|
$sql = 'SELECT event_id, calendar_id FROM vtiger_google_event_calendar_mapping WHERE user_id = ?';
|
|
$res = $db->pquery($sql, array($user->getId()));
|
|
$num_of_rows = $db->num_rows($res);
|
|
for($i=0;$i<$num_of_rows;$i++) {
|
|
$event_id = $db->query_result($res, $i, 'event_id');
|
|
$calendar_id = $db->query_result($res, $i, 'calendar_id');
|
|
$map[$event_id] = $calendar_id;
|
|
}
|
|
return $map;
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
//TODO : use batch requests
|
|
$calendarId = Google_Utils_Helper::getSelectedCalendarForUser($user);
|
|
if(!isset($this->calendars)) {
|
|
try {
|
|
$this->calendars = $this->pullCalendars(true);
|
|
} catch (Exception $e) {
|
|
return $records;
|
|
}
|
|
}
|
|
if(!in_array($calendarId, $this->calendars)) {
|
|
$calendarId = 'primary';
|
|
}
|
|
|
|
$eventCalendarMap = $this->getGoogleEventCalendarMap($user);
|
|
$newEventIds = array();
|
|
foreach ($records as $record) {
|
|
$entity = $record->get('entity');
|
|
$eventCalendarId = 'primary';
|
|
if($this->apiConnection->isTokenExpired()) {
|
|
$this->apiConnection->refreshToken();
|
|
$this->client->setAccessToken($this->apiConnection->getAccessToken());
|
|
$this->service = new Google_Service_Calendar($this->client);
|
|
}
|
|
try {
|
|
if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE) {
|
|
if(array_key_exists($entity->getId(), $eventCalendarMap)) {
|
|
$eventCalendarId = $eventCalendarMap[$entity->getId()];
|
|
}
|
|
$newEntity = $this->service->events->update($eventCalendarId,$entity->getId(),$entity);
|
|
$record->set('entity', $newEntity);
|
|
} else if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_DELETE_MODE) {
|
|
$record->set('entity', $entity);
|
|
if(array_key_exists($entity->getId(), $eventCalendarMap)) {
|
|
$eventCalendarId = $eventCalendarMap[$entity->getId()];
|
|
}
|
|
$newEntity = $this->service->events->delete($eventCalendarId,$entity->getId());
|
|
} else {
|
|
$newEntity = $this->service->events->insert($calendarId,$entity);
|
|
$newEventIds[] = $newEntity->getId();
|
|
$record->set('entity', $newEntity);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
continue;
|
|
}
|
|
}
|
|
if(count($newEventIds)) {
|
|
$this->putGoogleEventCalendarMap($newEventIds, $calendarId, $user);
|
|
}
|
|
return $records;
|
|
}
|
|
|
|
/**
|
|
* Tarsform Vtiger Records to Google Records
|
|
* @param <array> $vtEvents
|
|
* @return <array> tranformed vtiger Records
|
|
*/
|
|
public function transformToTargetRecord($vtEvents, $user) {
|
|
$records = array();
|
|
foreach ($vtEvents as $vtEvent) {
|
|
$newEvent = new Google_Service_Calendar_Event();
|
|
|
|
if ($vtEvent->getMode() == WSAPP_SyncRecordModel::WSAPP_DELETE_MODE) {
|
|
$newEvent->setId($vtEvent->get('_id'));
|
|
} elseif($vtEvent->getMode() == WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE && $vtEvent->get('_id')) {
|
|
if($this->apiConnection->isTokenExpired()) {
|
|
$this->apiConnection->refreshToken();
|
|
try {
|
|
$this->client->setAccessToken($this->apiConnection->getAccessToken());
|
|
} catch(Exception $e) {}//suppressing invalid access-token exception if access revoked
|
|
$this->service = new Google_Service_Calendar($this->client);
|
|
}
|
|
try {
|
|
$calendarId = 'primary';
|
|
$eventCalendarMap = $this->getGoogleEventCalendarMap($user);
|
|
if(array_key_exists($vtEvent->get('_id'), $eventCalendarMap)) {
|
|
$calendarId = $eventCalendarMap[$vtEvent->get('_id')];
|
|
}
|
|
$newEvent = $this->service->events->get($calendarId, $vtEvent->get('_id'));
|
|
} catch (Exception $e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$newEvent->setSummary($vtEvent->get('subject'));
|
|
$newEvent->setLocation($vtEvent->get('location'));
|
|
$newEvent->setDescription($vtEvent->get('description'));
|
|
$newEvent->setVisibility(strtolower($vtEvent->get('visibility')));
|
|
|
|
$startDate = $vtEvent->get('date_start');
|
|
$startTime = $vtEvent->get('time_start');
|
|
$endDate = $vtEvent->get('due_date');
|
|
$endTime = $vtEvent->get('time_end');
|
|
if (empty($endTime)) {
|
|
$endTime = "00:00";
|
|
}
|
|
$start = new Google_Service_Calendar_EventDateTime();
|
|
$start->setDateTime($this->googleFormat($startDate . ' ' . $startTime));
|
|
$newEvent->setStart($start);
|
|
|
|
$end = new Google_Service_Calendar_EventDateTime();
|
|
$end->setDateTime($this->googleFormat($endDate. ' ' .$endTime));
|
|
$newEvent->setEnd($end);
|
|
|
|
/**
|
|
* Commenting out adding attendees in google
|
|
//attendees
|
|
$googleAttendees = array();
|
|
$newEvent->setAttendees($googleAttendees);
|
|
$attendees = $vtEvent->get('attendees');
|
|
if(isset($attendees)) {
|
|
foreach($attendees as $attendee) {
|
|
if(!empty($attendee['email'])) {
|
|
$eventAttendee = new Google_Service_Calendar_EventAttendee();
|
|
$eventAttendee->setEmail($attendee['email']);
|
|
$googleAttendees[] = $eventAttendee;
|
|
}
|
|
}
|
|
if(count($googleAttendees)) $newEvent->setAttendees($googleAttendees);
|
|
}
|
|
*/
|
|
|
|
$recordModel = Google_Calendar_Model::getInstanceFromValues(array('entity' => $newEvent));
|
|
$recordModel->setType($this->getSynchronizeController()->getSourceType())->setMode($vtEvent->getMode())->setSyncIdentificationKey($vtEvent->get('_syncidentificationkey'));
|
|
$recordModel = $this->performBasicTransformations($vtEvent, $recordModel);
|
|
$recordModel = $this->performBasicTransformationsToTargetRecords($recordModel, $vtEvent);
|
|
$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;
|
|
}
|
|
|
|
public function pullCalendars($list=false) {
|
|
$calendarList = $this->service->calendarList->listCalendarList();
|
|
$allCalendarsItems = array();
|
|
while(true) {
|
|
$calendarItems = $calendarList->getItems();
|
|
if(is_array($calendarItems))
|
|
$allCalendarsItems = array_merge($allCalendarsItems, $calendarItems);
|
|
|
|
$pageToken = $calendarList->getNextPageToken();
|
|
if ($pageToken) {
|
|
$optParams = array('pageToken' => $pageToken);
|
|
$calendarList = $this->service->calendarList->listCalendarList($optParams);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
$calendars = array();
|
|
if($list) {
|
|
foreach($allCalendarsItems as $calendarItem) {
|
|
if(!$calendarItem->getPrimary())
|
|
$calendars[] = $calendarItem->getId();
|
|
else
|
|
$calendars[] = 'primary';
|
|
}
|
|
return $calendars;
|
|
}
|
|
foreach($allCalendarsItems as $calendarItem) {
|
|
$calendars[] = array(
|
|
'id' => $calendarItem->getId(),
|
|
'summary' => $calendarItem->getSummary(),
|
|
'primary' => $calendarItem->getPrimary()
|
|
);
|
|
}
|
|
return $calendars;
|
|
}
|
|
|
|
}
|
|
?>
|
|
|