495 lines
20 KiB
PHP
495 lines
20 KiB
PHP
<?php
|
|
/*+**********************************************************************************
|
|
* The contents of this file are subject to the vtiger CRM Public License Version 1.1
|
|
* ("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.
|
|
************************************************************************************/
|
|
|
|
require_once 'modules/WSAPP/Utils.php';
|
|
require_once 'include/database/PearDatabase.php';
|
|
require_once 'include/Zend/Json.php';
|
|
require_once 'include/utils/utils.php';
|
|
|
|
class SyncServer {
|
|
private $appkey;
|
|
private $syncModule;
|
|
private $destHandler;
|
|
private $create = "create";
|
|
private $update = "update";
|
|
private $delete = "delete";
|
|
private $save = "save";
|
|
private $syncTypes = array("user","app");
|
|
|
|
|
|
/**
|
|
* Lookup application id using the key provided.
|
|
*/
|
|
function appid_with_key($key) {
|
|
$db = PearDatabase::getInstance();
|
|
$appidresult = $db->pquery("SELECT appid FROM vtiger_wsapp WHERE appkey=?", array($key));
|
|
if ($db->num_rows($appidresult)) return $db->query_result($appidresult, 0, 'appid');
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Retrieve serverid-clientid record map information for the given
|
|
* application and serverid
|
|
*/
|
|
function idmap_get_clientmap($appid, $serverids) {
|
|
if (!is_array($serverids)) $serverids = array($serverids);
|
|
$db = PearDatabase::getInstance(); ;
|
|
$result = $db->pquery(sprintf(
|
|
"SELECT serverid, clientid,clientmodifiedtime,servermodifiedtime,id FROM vtiger_wsapp_recordmapping WHERE appid=? AND serverid IN ('%s')",
|
|
implode("','", $serverids)), array($appid));
|
|
|
|
$mapping = array();
|
|
if ($db->num_rows($result)) {
|
|
while ($row = $db->fetch_array($result)) {
|
|
$mapping[$row['serverid']] = array("clientid"=>$row['clientid'],"clientmodifiedtime"=>$row['clientmodifiedtime'],
|
|
"servermodifiedtime"=>$row['servermodifiedtime'],"id"=>$row['id']);
|
|
}
|
|
}
|
|
return $mapping;
|
|
}
|
|
|
|
/**
|
|
* Retrieve serverid-clientid record map information for the given
|
|
* application and client
|
|
*/
|
|
function idmap_get_clientservermap($appid,$clientids){
|
|
if(!is_array($clientids)) $clientids = array($clientids);
|
|
|
|
$db = PearDatabase::getInstance();
|
|
|
|
$result = $db->pquery(sprintf(
|
|
"SELECT serverid, clientid FROM vtiger_wsapp_recordmapping WHERE appid=? AND clientid IN ('%s')",
|
|
implode("','", $clientids)), array($appid));
|
|
$mapping = array();
|
|
if($db->num_rows($result)){
|
|
while($row = $db->fetch_array($result)){
|
|
$mapping[$row['clientid']] = $row['serverid'];
|
|
}
|
|
}
|
|
return $mapping;
|
|
}
|
|
|
|
function idmap_storeRecordsInQueue($syncServerId,$recordDetails,$flag,$appid){
|
|
if(!is_array($recordDetails))
|
|
$recordDetails = array($recordDetails);
|
|
$db = PearDatabase::getInstance();
|
|
$params = array();
|
|
$params[] = $syncServerId;
|
|
$params[] = Zend_Json::encode($recordDetails);
|
|
$params[] = $flag;
|
|
$params[] = $appid;
|
|
$db->pquery("INSERT INTO vtiger_wsapp_queuerecords(syncserverid,details,flag,appid) VALUES(?,?,?,?)",array($params));
|
|
}
|
|
|
|
function checkIdExistInQueue($syncServerId){
|
|
$db = PearDatabase::getInstance();
|
|
$checkQuery = "SELECT syncserverid FROM vtiger_wsapp_queuerecords WHERE syncserverid=?";
|
|
$result = $db->pquery($checkQuery,array($syncServerId));
|
|
if($db->num_rows($result)>0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
function markRecordAsDeleteForAllCleints($recordValues){
|
|
$recordWsId = $recordValues['id'];
|
|
$modifiedTime = $recordValues['modifiedtime'];
|
|
$db = PearDatabase::getInstance();
|
|
$query = "SELECT * FROM vtiger_wsapp_recordmapping WHERE serverid=? and servermodifiedtime < ?";
|
|
$params = array($recordWsId,$modifiedTime);
|
|
$result = $db->pquery($query,$params);
|
|
while($arre = $db->fetchByAssoc($result)){
|
|
$syncServerId = $arre["id"];
|
|
$clientId = $arre["clientid"];
|
|
$clientMappedId = $arre["appid"];
|
|
if(!$this->checkIdExistInQueue($syncServerId)){
|
|
$this->idmap_storeRecordsInQueue($syncServerId,$recordValues,$this->delete,$clientMappedId);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getSyncServerId($clientId,$serverId,$clientAppId){
|
|
$db = PearDatabase::getInstance();
|
|
$syncServerId = NULL;
|
|
$query = "SELECT id FROM vtiger_wsapp_recordmapping WHERE clientid=? and serverid=? and appid=?";
|
|
$result = $db->pquery($query,array($clientId,$serverId,$clientAppId));
|
|
if($db->num_rows($result)>0){
|
|
$syncServerId = $db->query_result($result,0,'id');
|
|
}
|
|
return $syncServerId;
|
|
}
|
|
|
|
function deleteQueueRecords($syncServerIdList){
|
|
$db= PearDatabase::getInstance();
|
|
$deleteQuery = "DELETE FROM vtiger_wsapp_queuerecords WHERE syncserverid IN (".generateQuestionMarks($syncServerIdList).")";
|
|
$result = $db->pquery($deleteQuery,$syncServerIdList);
|
|
}
|
|
|
|
/**
|
|
* Create serverid-clientid record map for the application
|
|
*/
|
|
function idmap_put($appid, $serverid, $clientid,$clientModifiedTime,$serverModifiedTime,$serverAppId,$mode="save") {
|
|
$db = PearDatabase::getInstance();
|
|
if($mode == $this->create)
|
|
$this->idmap_create($appid, $serverid, $clientid,$clientModifiedTime,$serverModifiedTime,$serverAppId);
|
|
else if ($mode == $this->update)
|
|
$this->idmap_update($appid, $serverid, $clientid, $clientModifiedTime,$serverModifiedTime,$serverAppId);
|
|
else if($mode==$this->save){
|
|
$result = $db->pquery("SELECT * FROM vtiger_wsapp_recordmapping WHERE appid=? and serverid=? and clientid=?",array($appid,$serverid,$clientid));
|
|
if($db->num_rows($result)<=0)
|
|
$this->idmap_create($appid, $serverid, $clientid, $clientModifiedTime,$serverModifiedTime,$serverAppId);
|
|
else
|
|
$this->idmap_update($appid, $serverid, $clientid, $clientModifiedTime,$serverModifiedTime,$serverAppId);
|
|
}
|
|
else if ($mode == $this->delete)
|
|
$this->idmap_delete($appid, $serverid, $clientid,$serverAppId);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param $appid
|
|
* @param $serverid
|
|
* @param $clientid
|
|
* @param $modifiedTime
|
|
* create mapping for server and client id
|
|
*/
|
|
function idmap_create($appid, $serverid, $clientid,$clientModifiedTime,$serverModifiedTime,$serverAppId){
|
|
$db = PearDatabase::getInstance();
|
|
$db->pquery("INSERT INTO vtiger_wsapp_recordmapping (appid, serverid, clientid,clientmodifiedtime,servermodifiedtime,serverappid) VALUES (?,?,?,?,?,?)",
|
|
array($appid, $serverid, $clientid,$clientModifiedTime,$serverModifiedTime,$serverAppId));
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param <type> $appid
|
|
* @param <type> $serverid
|
|
* @param <type> $clientid
|
|
* @param <type> $modifiedTime
|
|
* update the mapping of server and client id
|
|
*/
|
|
function idmap_update($appid, $serverid, $clientid,$clientModifiedTime,$serverModifiedTime,$serverAppId){
|
|
$db = PearDatabase::getInstance();
|
|
$db->pquery("UPDATE vtiger_wsapp_recordmapping SET clientmodifiedtime=?,servermodifiedtime=? WHERE appid=? and serverid=? and clientid=? and serverappid=?",
|
|
array($clientModifiedTime,$serverModifiedTime,$appid, $serverid, $clientid,$serverAppId));
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param <type> $appid
|
|
* @param <type> $serverid
|
|
* @param <type> $clientid
|
|
* delete the mapping for client and server id
|
|
*/
|
|
function idmap_delete($appid, $serverid, $clientid,$serverAppId){
|
|
$db = PearDatabase::getInstance();
|
|
$db->pquery("DELETE FROM vtiger_wsapp_recordmapping WHERE appid=? and serverid=? and clientid=? and serverappid=?",
|
|
array($appid, $serverid, $clientid,$serverAppId));
|
|
}
|
|
|
|
|
|
function idmap_updateMapDetails($appid,$clientid,$clientModifiedTime,$serverModifiedTime){
|
|
$db = PearDatabase::getInstance();
|
|
$db->pquery("UPDATE vtiger_wsapp_recordmapping SET clientmodifiedtime=?,servermodifiedtime=? WHERE appid=? and clientid=?",
|
|
array($clientModifiedTime,$serverModifiedTime,$appid, $clientid));
|
|
}
|
|
|
|
function getDestinationHandleDetails(){
|
|
return wsapp_getHandler('vtigerCRM');
|
|
}
|
|
|
|
|
|
/*****************
|
|
* Web services
|
|
*****************/
|
|
|
|
/**
|
|
* Register the application
|
|
*/
|
|
function register($name,$type) {
|
|
if (empty($name)) {
|
|
throw new WebServiceException('WSAPP01',"No type specified");
|
|
}
|
|
if(empty($type)){
|
|
throw new WebServiceException('WSAPP06',"No sync type specified");
|
|
}
|
|
if(is_array($name))
|
|
throw new WebServiceException("WSAPP07","type is in the wrong format");
|
|
$type = strtolower($type);
|
|
if(!in_array($type, $this->syncTypes))
|
|
throw new WebServiceException('WSAPP05',"Wrong sync type specified");
|
|
$db = PearDatabase::getInstance();
|
|
$uid = uniqid();
|
|
$db->pquery("INSERT INTO vtiger_wsapp (name, appkey,type) VALUES(?,?,?)", array($name, $uid,$type));
|
|
|
|
return array('key' => $uid);
|
|
}
|
|
|
|
/**
|
|
* Deregister the application
|
|
*/
|
|
function deregister($name, $key, $user) {
|
|
if (!empty($name) && !empty($key)) {
|
|
$db = PearDatabase::getInstance();
|
|
$uid = uniqid();
|
|
$db->pquery("DELETE FROM vtiger_wsapp_recordmapping WHERE appid=(SELECT appid FROM vtiger_wsapp WHERE name=? AND appkey=?)",
|
|
array($name, $key));
|
|
$db->pquery("DELETE FROM vtiger_wsapp WHERE name=? AND appkey=?", array($name, $key));
|
|
}
|
|
return array ($name, $key);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handles Create/Update/Delete operations on record
|
|
*/
|
|
function put($key, $element, $user) {
|
|
$db = PearDatabase::getInstance();
|
|
$appid = $this->appid_with_key($key);
|
|
|
|
if (empty($appid)) {
|
|
throw new WebServiceException('WSAPP04',"Access restricted to app");
|
|
}
|
|
|
|
if (!is_array($element)) $records = array($element);
|
|
else $records = $element;
|
|
|
|
//hardcoded since the destination handler will be vtigerCRM
|
|
$serverKey = wsapp_getAppKey("vtigerCRM");
|
|
$serverAppId = $this->appid_with_key($serverKey);
|
|
$handlerDetails = $this->getDestinationHandleDetails();
|
|
require_once $handlerDetails['handlerpath'];
|
|
$this->destHandler = new $handlerDetails['handlerclass']($serverKey);
|
|
|
|
$recordDetails = array();
|
|
|
|
$createRecords = array();
|
|
$updateRecords = array();
|
|
$deleteRecords = array();
|
|
|
|
$clientModifiedTimeList = array();
|
|
foreach ($records as $record) {
|
|
$recordDetails = array();
|
|
$clientRecordId = $record['id'];
|
|
|
|
// Missing client record id?
|
|
if (empty($clientRecordId)) continue;
|
|
|
|
$lookupRecordId = false;
|
|
$lookupResult = $db->pquery("SELECT serverid,clientmodifiedtime FROM vtiger_wsapp_recordmapping WHERE appid=? AND clientid=?", array($appid, $clientRecordId));
|
|
if ($db->num_rows($lookupResult)) $lookupRecordId = $db->query_result($lookupResult, 0, 'serverid');
|
|
if (empty($lookupRecordId) && $record['mode'] !="delete") {
|
|
$createRecords[$clientRecordId] = $record['values'];
|
|
$createRecords[$clientRecordId]['module'] = $record['module'];
|
|
$clientModifiedTimeList[$clientRecordId] = $record['values']['modifiedtime'];
|
|
} else {
|
|
if (empty($record['values']) && !(empty($lookupRecordId)) ) {
|
|
$deleteRecords[$clientRecordId] = $lookupRecordId;
|
|
}
|
|
else if (!(empty($lookupRecordId))) {
|
|
$clientLastModifiedTime = $db->query_result($lookupResult,0,'clientmodifiedtime');
|
|
if($clientLastModifiedTime >= $record['values']['modifiedtime'])
|
|
continue;
|
|
$record['values']['id'] = $lookupRecordId;
|
|
$updateRecords[$clientRecordId] = $record['values'];
|
|
$updateRecords[$clientRecordId]['module'] = $record['module'];
|
|
$clientModifiedTimeList[$clientRecordId] = $record['values']['modifiedtime'];
|
|
}
|
|
}
|
|
}
|
|
|
|
$recordDetails['created'] = $createRecords;
|
|
$recordDetails['updated'] = $updateRecords;
|
|
$recordDetails['deleted'] = $deleteRecords;
|
|
|
|
$result = $this->destHandler->put($recordDetails,$user);
|
|
|
|
$response= array();
|
|
$response['created'] = array();
|
|
$response['updated'] = array();
|
|
$response['deleted'] = array();
|
|
|
|
$nextSyncDeleteRecords = $this->destHandler->getAssignToChangedRecords();
|
|
foreach($result['created'] as $clientRecordId=>$record){
|
|
$this->idmap_put($appid, $record['id'], $clientRecordId,$clientModifiedTimeList[$clientRecordId],$record['modifiedtime'],$serverAppId,$this->create);
|
|
$responseRecord = $record;
|
|
$responseRecord['_id'] = $record['id'];
|
|
$responseRecord['id'] = $clientRecordId;
|
|
$responseRecord['_modifiedtime'] = $record['modifiedtime'];
|
|
$responseRecord['modifiedtime'] = $clientModifiedTimeList[$clientRecordId];
|
|
$response['created'][] = $responseRecord;
|
|
}
|
|
foreach($result['updated'] as $clientRecordId=>$record){
|
|
$this->idmap_put($appid, $record['id'], $clientRecordId,$clientModifiedTimeList[$clientRecordId],$record['modifiedtime'],$serverAppId,$this->update);
|
|
$responseRecord = $record;
|
|
$responseRecord['_id'] = $record['id'];
|
|
$responseRecord['id'] = $clientRecordId;
|
|
$responseRecord['_modifiedtime'] = $record['modifiedtime'];
|
|
$responseRecord['modifiedtime'] = $clientModifiedTimeList[$clientRecordId];
|
|
$response['updated'][] = $responseRecord;
|
|
}
|
|
foreach($result['deleted'] as $clientRecordId=>$record){
|
|
$this->idmap_put($appid, $record, $clientRecordId,"","",$serverAppId,$this->delete);
|
|
$response['deleted'][] = $clientRecordId;
|
|
}
|
|
$queueRecordIds = array();
|
|
$queueRecordDetails = array();
|
|
foreach($nextSyncDeleteRecords as $clientRecordId=>$record){
|
|
$queueRecordIds[] = $record['id'];
|
|
$queueRecordDetails[$record['id']] = $this->convertToQueueRecordFormat($record, $this->delete);
|
|
}
|
|
if(count($queueRecordIds > 0)){
|
|
$syncServerDetails = $this->idmap_get_clientmap($appid,$queueRecordIds);
|
|
foreach($queueRecordIds as $serverId){
|
|
$syncServerId = $syncServerDetails[$serverId]['id'];
|
|
$recordValues = $queueRecordDetails[$serverId];
|
|
if(!$this->checkIdExistInQueue($syncServerId)){
|
|
$this->idmap_storeRecordsInQueue($syncServerId, $recordValues, $this->delete,$appid);
|
|
}
|
|
}
|
|
}
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Share the Create/Update/Delete state information
|
|
*/
|
|
function get($key, $module, $token, $user) {
|
|
$db = PearDatabase::getInstance();
|
|
$appid = $this->appid_with_key($key);
|
|
if (empty($appid)) {
|
|
throw new WebServiceException('WSAPP04',"Access restricted to app");
|
|
}
|
|
$clientApplicationSyncType = wsapp_getAppSyncType($key);
|
|
//hardcoded since the destination handler will be vtigerCRM
|
|
$serverKey = wsapp_getAppKey("vtigerCRM");
|
|
$handlerDetails = wsapp_getHandler('vtigerCRM');
|
|
require_once $handlerDetails['handlerpath'];
|
|
$this->destHandler = new $handlerDetails['handlerclass']($serverKey);
|
|
$this->destHandler->setClientSyncType($clientApplicationSyncType);
|
|
$result = $this->destHandler->get($module, $token,$user);
|
|
// Lookup Ids
|
|
$updatedIds = array(); $deletedIds = array();
|
|
foreach($result['updated'] as $u){
|
|
$updatedIds[] = $u['id'];
|
|
}
|
|
foreach($result['deleted'] as $d){
|
|
$deletedIds[] = $d;
|
|
}
|
|
$syncServerDeleteIds = $this->getQueueDeleteRecord($appid);
|
|
foreach($syncServerDeleteIds as $deleteServerId){
|
|
$deletedIds[] = $deleteServerId;
|
|
}
|
|
|
|
$updateDeleteCommonIds = array_values(array_intersect($updatedIds,$deletedIds));
|
|
//if the record exist in both the update and delete , then send record as update
|
|
// and unset the id from deleted list
|
|
$deletedIds = array_diff($deletedIds,$updateDeleteCommonIds);
|
|
|
|
$updatedLookupIds = $this->idmap_get_clientmap($appid, $updatedIds);
|
|
$deletedLookupIds = $this->idmap_get_clientmap($appid, $deletedIds);
|
|
$filteredCreates = array(); $filteredUpdates = array();
|
|
foreach ($result['updated'] as $u) {
|
|
if(in_array($u['id'],$updatedIds)){
|
|
if (isset($updatedLookupIds[$u['id']]) && ($u['modifiedtime'] > $updatedLookupIds[$u['id']]['servermodifiedtime'])) {
|
|
$u['_id'] = $u['id'];
|
|
$u['id'] = $updatedLookupIds[$u['id']]['clientid']; // Replace serverid with clientid
|
|
$u['_modifiedtime'] = $u['modifiedtime'];
|
|
$filteredUpdates[] = $u;
|
|
} else if (empty($updatedLookupIds[$u['id']])){
|
|
$u['_id'] = $u['id'];// Rename the id key
|
|
$u['_modifiedtime'] = $u['modifiedtime'];
|
|
unset($u['id']);
|
|
$filteredCreates[] = $u;
|
|
}
|
|
}
|
|
}
|
|
|
|
$filteredDeletes = array();
|
|
foreach ($deletedIds as $d) {
|
|
if (isset($deletedLookupIds[$d])) {
|
|
$filteredDeletes[] = $deletedLookupIds[$d]['clientid']; // Replace serverid with clientid;
|
|
}
|
|
}
|
|
|
|
$result['created'] = $filteredCreates;
|
|
$result['updated'] = $filteredUpdates;
|
|
$result['deleted'] = $filteredDeletes;
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Update the missing serverid-clientid map as requested from application
|
|
*/
|
|
function map($key, $element, $user) {
|
|
if (empty($element)) return;
|
|
$db = PearDatabase::getInstance();
|
|
$appid = $this->appid_with_key($key);
|
|
$createDetails = $element["create"];
|
|
$deleteDetails = $element["delete"];
|
|
$updatedDetails = $element["update"];
|
|
$deleteQueueSyncServerIds = array();
|
|
$serverKey = wsapp_getAppKey("vtigerCRM");
|
|
$serverAppId = $this->appid_with_key($serverKey);
|
|
//$lookups = $this->idmap_get_clientmap($appid, array_values($createDetails));
|
|
foreach ($createDetails as $clientid => $serverDetails) {
|
|
$this->idmap_put( $appid, $serverDetails['serverid'], $clientid,$serverDetails['modifiedtime'],$serverDetails['_modifiedtime'],$serverAppId,$this->create);
|
|
}
|
|
foreach($updatedDetails as $clientid=>$serverDetails){
|
|
$this->idmap_updateMapDetails( $appid, $clientid,$serverDetails['modifiedtime'],$serverDetails['_modifiedtime'],$this->update);
|
|
$syncServerId = $this->getSyncServerId($clientid,$serverDetails['serverid'],$appid);
|
|
if(isset($syncServerId) && $syncServerId != NULL){
|
|
$deleteQueueSyncServerIds[] = $syncServerId;
|
|
}
|
|
}
|
|
if(count($deleteDetails)>0){
|
|
$deleteLookUps = $this->idmap_get_clientservermap($appid, array_values($deleteDetails));
|
|
foreach($deleteDetails as $clientid){
|
|
if(isset($deleteLookUps[$clientid])){
|
|
$serverId = $deleteLookUps[$clientid];
|
|
$syncServerId = $this->getSyncServerId($clientid,$serverId,$appid);
|
|
if(isset($syncServerId) && $syncServerId != NULL){
|
|
$deleteQueueSyncServerIds[] = $syncServerId;
|
|
}
|
|
$this->idmap_delete($appid, $serverId, $clientid,$serverAppId);
|
|
}
|
|
|
|
}
|
|
}
|
|
if(count($deleteQueueSyncServerIds)>0){
|
|
$this->deleteQueueRecords($deleteQueueSyncServerIds);
|
|
}
|
|
}
|
|
|
|
function getQueueDeleteRecord($appId){
|
|
$db = PearDatabase::getInstance();
|
|
$result = $db->pquery("SELECT * FROM vtiger_wsapp_queuerecords
|
|
INNER JOIN vtiger_wsapp_recordmapping ON (vtiger_wsapp_recordmapping.id=vtiger_wsapp_queuerecords.syncserverid)
|
|
WHERE vtiger_wsapp_recordmapping.appid=? ",array($appId));
|
|
$serverIds = array();
|
|
$num_rows = $db->num_rows($result);
|
|
for($i=0;$i<$num_rows;$i++){
|
|
$serverId = $db->query_result($result,$i,'serverid');
|
|
$serverIds[] = $serverId;
|
|
}
|
|
return $serverIds;
|
|
}
|
|
|
|
function convertToQueueRecordFormat($record,$flag){
|
|
if($flag != $this->delete)
|
|
return $record;
|
|
else{
|
|
$recordFormat = array();
|
|
$recordFormat['id'] = $record['id'];
|
|
return $recordFormat;
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|