mirror of
https://github.com/VTECRM/vtenext.git
synced 2026-02-26 16:18:47 +00:00
369 lines
14 KiB
PHP
369 lines
14 KiB
PHP
<?php
|
|
/*************************************
|
|
* SPDX-FileCopyrightText: 2009-2020 Vtenext S.r.l. <info@vtenext.com>
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
************************************/
|
|
/* crmv@170283 */
|
|
require_once('include/RestApi/Exceptions.php');
|
|
|
|
class VTERestApi extends SDKExtendableClass {
|
|
|
|
protected $user;
|
|
protected $operationManager;
|
|
protected $operationParameters = [];
|
|
protected $restOperations = array(
|
|
'retrieve' => 'retrieve',
|
|
'create' => 'create',
|
|
'delete' => 'delete',
|
|
'query' => 'query',
|
|
'listtypes' => 'listtypes',
|
|
'describe' => 'describe',
|
|
'convertlead' => 'convertlead',
|
|
'updateRecord' => 'update',
|
|
'retrieveInventory' => 'retrieveinventory',
|
|
'revise' => 'revise',
|
|
'get_labels' => 'getlabels',
|
|
'get_langs' => 'getlangs',
|
|
'login_pwd' => 'loginpwd',
|
|
'getmenulist' => 'getmenulist',
|
|
'retrieveExtra' => 'retrieveextra',
|
|
'queryExtra' => 'queryextra',
|
|
'describeExtra' => 'describeextra',
|
|
'listtypesExtra' => 'listtypesextra',
|
|
'getRelationsExtra' => 'getrelationsextra',
|
|
'ol_get_filters' => 'olgetfilters',
|
|
'ol_clientsearch' => 'olclientsearch',
|
|
'ol_is_sdk' => 'olissdk',
|
|
'ol_doquery' => 'oldoquery',
|
|
'describe_all' => 'describeall',
|
|
'dynaform_describe' => 'dynaformdescribe',
|
|
);
|
|
// crmv@171524
|
|
private $systemOperations = array( // system methods: written here and not in external files
|
|
'processhandler' => array('id'=>'string'),
|
|
);
|
|
// crmv@171524e
|
|
protected static $performanceLogger = null; //crmv@173186
|
|
|
|
function __construct() {
|
|
//crmv@173186
|
|
$this->log('URL',$_SERVER['REQUEST_URI'],true);
|
|
$this->log('METHOD',$_SERVER['REQUEST_METHOD']);
|
|
$this->log('REQUEST',Zend_Json::encode($_REQUEST));
|
|
//crmv@173186e
|
|
}
|
|
|
|
public function enableRestOperations() {
|
|
global $adb, $table_prefix;
|
|
foreach($this->restOperations as $name => $rest_name) {
|
|
$adb->pquery("update {$table_prefix}_ws_operation set rest_name = ? where name = ?", array($rest_name, $name));
|
|
}
|
|
}
|
|
|
|
protected function validateToken() {
|
|
if (!isset($_SERVER['PHP_AUTH_USER']) || trim($_SERVER['PHP_AUTH_USER']) == "") {
|
|
throw new InvalidLoginException('Authentication failed - reason: no username supplied');
|
|
} elseif (!isset($_SERVER['PHP_AUTH_PW']) || trim($_SERVER['PHP_AUTH_PW']) == "") {
|
|
throw new InvalidLoginException('Authentication failed - reason: no password supplied');
|
|
} else {
|
|
//crmv@186555
|
|
global $adb, $table_prefix, $current_user;
|
|
$user_name = vtlib_purify($_SERVER['PHP_AUTH_USER']);
|
|
$token = vtlib_purify($_SERVER['PHP_AUTH_PW']);
|
|
|
|
$userInstance = CRMEntity::getInstance('Users');
|
|
$userId = $userInstance->retrieve_user_id($user_name);
|
|
$userInstance->id = $userId;
|
|
|
|
$result = $adb->pquery("select id from {$table_prefix}_users where user_name = ? and accesskey = ? and status = ?", array($user_name, $token, 'Active'));
|
|
if (!($result && $adb->num_rows($result) > 0)) {
|
|
$userInstance->trackErrorLogin();
|
|
throw new InvalidLoginException('Authentication failed - reason: incorrect username and password combination');
|
|
} else {
|
|
$userInstance->trackSuccessLogin();
|
|
}
|
|
|
|
$success = true;
|
|
$userInstance->checkTrackingLogin($success);
|
|
if (!$success) {
|
|
throw new InvalidLoginException('Authentication failed - reason: too many unsuccessfully logins');
|
|
}
|
|
|
|
$current_user = CRMEntity::getInstance('Users');
|
|
$this->user = $current_user->retrieveCurrentUserInfoFromFile($userId);
|
|
//crmv@186555e
|
|
}
|
|
}
|
|
|
|
//crmv@173186
|
|
public function log($title,$str='',$new=false) {
|
|
VTESystemLogger::log('restapi', $title, $str, $new); // crmv@176614
|
|
}
|
|
//crmv@173186e
|
|
|
|
public function getMethods() {
|
|
global $adb, $table_prefix;
|
|
static $methods = array();
|
|
if (empty($methods)) {
|
|
$result = $adb->query("select operationid, name, type, rest_name, prelogin from {$table_prefix}_ws_operation where rest_name is not null and rest_name <> ''"); // crmv@179081
|
|
if ($result && $adb->num_rows($result) > 0) {
|
|
while($row=$adb->fetchByAssoc($result)) {
|
|
$methods[$row['rest_name']] = $row;
|
|
}
|
|
}
|
|
}
|
|
// crmv@171524
|
|
if (!empty($this->systemOperations)) {
|
|
foreach($this->systemOperations as $op => $params) {
|
|
$methods[$op] = array('name'=>$op,'rest_name'=>$op,'type'=>'POST','prelogin'=>0,'parameters'=>$params);
|
|
}
|
|
}
|
|
// crmv@171524e
|
|
return $methods;
|
|
}
|
|
|
|
public function getParameters($method) {
|
|
global $adb;
|
|
if (!isset($this->operationParameters[$method])) {
|
|
$this->operationParameters[$method] = [];
|
|
$methods = $this->getMethods();
|
|
// crmv@171524
|
|
if (isset($methods[$method]['parameters'])) {
|
|
$this->operationParameters[$method] = array_keys($methods[$method]['parameters']);
|
|
} else {
|
|
// crmv@171524e
|
|
if (isset($methods[$method])) {
|
|
$operation = strtolower($methods[$method]['name']);
|
|
}
|
|
require_once("include/Webservices/OperationManager.php");
|
|
require_once("include/Webservices/SessionManager.php");
|
|
$sessionManager = new SessionManager();
|
|
$format = vtws_getParameter($_REQUEST, "format", "json");
|
|
$this->operationManager = new OperationManager($adb,$operation,$format,$sessionManager);
|
|
$tmp_params = $this->operationManager->getOperationParams();
|
|
if (!empty($tmp_params)) {
|
|
foreach($tmp_params as $param) {
|
|
$name = array_keys($param); $name = $name[0];
|
|
$this->operationParameters[$method][] = $name;
|
|
}
|
|
}
|
|
} // crmv@171524
|
|
}
|
|
return $this->operationParameters[$method];
|
|
}
|
|
|
|
/*
|
|
* some examples:
|
|
* query query:"SELECT * FROM Accounts WHERE accountname like '%vte%';"
|
|
* listtypes fieldTypeList(optional):"[\"Accounts\",\"Contacts\"]"
|
|
* revise element:"{\"id\":\"9x222\",\"ticket_title\":\"aaa\",\"description\":\"aaa\"}"
|
|
*/
|
|
public function __call($method, $args) {
|
|
$methods = $this->getMethods();
|
|
// crmv@179081
|
|
if (!array_key_exists($method,$methods)) {
|
|
throw new MethodNotFoundException(sprintf('Method %s in class %s not found.', $method, get_class($this)));
|
|
} else {
|
|
$args = array_combine($this->getParameters($method), $args);
|
|
$validate = !$methods[$method]['prelogin'];
|
|
return $this->invoke($method, Zend_Json::encode($args), $validate);
|
|
}
|
|
// crmv@179081e
|
|
}
|
|
|
|
public function create($elementType, $element) {
|
|
$element = Zend_Json::decode($element);
|
|
if(!isset($element['assigned_user_id'])) {
|
|
$element['assigned_user_id'] = vtws_getWebserviceEntityId('Users',getUserId_Ol(vtlib_purify($_SERVER['PHP_AUTH_USER'])));
|
|
}
|
|
return $this->invoke(__FUNCTION__, Zend_Json::encode(array('elementType'=>$elementType,'element'=>Zend_Json::encode($element))));
|
|
}
|
|
|
|
private function invoke($method, $params='', $validate = true) { // crmv@179081
|
|
if ($validate) $this->validateToken(); // crmv@179081
|
|
|
|
$operationInput = $this->operationManager->sanitizeOperation(Zend_Json::decode($params));
|
|
$includes = $this->operationManager->getOperationIncludes();
|
|
foreach($includes as $ind=>$path){
|
|
require_once($path);
|
|
}
|
|
$rawOutput = $this->operationManager->runOperation($operationInput, $this->user);
|
|
return $rawOutput;
|
|
}
|
|
|
|
// crmv@171524 crmv@178291 crmv@199641
|
|
function processhandler($id) {
|
|
global $adb, $table_prefix;
|
|
|
|
$this->validateToken();
|
|
|
|
$start_time = microtime(true);
|
|
$allok = true;
|
|
|
|
require_once('include/utils/TriggerQueueManager.php');
|
|
$triggerQueueManager = \TriggerQueueManager::getInstance();
|
|
$triggerQueueManager->activateConsumer();
|
|
|
|
$VTEP = \VTEProperties::getInstance();
|
|
$rabbitmqConnection = $VTEP->getProperty('performance.mq_rabbitmq_connection');
|
|
$max_attempts_exhausted = false;
|
|
|
|
// catch fatal errors
|
|
$oldDieOnError = ini_get('display_errors');
|
|
ini_set('display_errors', 0);
|
|
register_shutdown_function(array($this, 'errorHandler'), $id);
|
|
|
|
// sleep(2);
|
|
|
|
// disable die on error, enable exception
|
|
$oldDieOnError = $adb->dieOnError;
|
|
$adb->setDieOnError(false);
|
|
$adb->setExceptOnError(true);
|
|
|
|
try {
|
|
$result = $adb->pquery("SELECT * FROM {$table_prefix}_trigger_queue WHERE id = ?", array($id));
|
|
if ($result && $adb->num_rows($result) > 0) {
|
|
|
|
$crmid = $adb->query_result($result, 0, 'crmid');
|
|
$logtitle = "crmid:{{$crmid}} queueid:{$id}";
|
|
$this->log("{$logtitle} info", "Start processing");
|
|
|
|
// check and lock working
|
|
$result_upd = $adb->pquery("UPDATE {$table_prefix}_trigger_queue SET working = ? WHERE id = ?", array(1,intval($id)));
|
|
if ($adb->getAffectedRowCount($result_upd) == 0) {
|
|
$this->log("{$logtitle} info", "End processing, already working");
|
|
throw new \Exception("Queue element $id (dependent_on: {$adb->query_result($result, 0, 'dependent_on')}, master_dependent_on: {$adb->query_result($result, 0, 'master_dependent_on')}) is already working.");
|
|
}
|
|
$triggerQueueManager->lockDependents($id);
|
|
|
|
// check if the previous element has been executed
|
|
$dependent_on = $adb->query_result($result, 0, 'dependent_on');
|
|
if (!empty($dependent_on)) {
|
|
// if the dependent_on failed set failed also this row
|
|
$result1 = $adb->pquery("SELECT * FROM {$table_prefix}_trigger_queue_failed WHERE id = ?", array($dependent_on));
|
|
if ($result1 && $adb->num_rows($result1) > 0) {
|
|
$this->log("{$logtitle} error", "Queue element {$id} depends on the failed {$dependent_on}.");
|
|
$triggerQueueManager->move2Failed($id,'DEPENDENT_ON_FAILED');
|
|
$triggerQueueManager->unlockDependents($id);
|
|
$this->log("{$logtitle} info", "End processing element, moved to failed");
|
|
return $id;
|
|
}
|
|
$result1 = $adb->pquery("SELECT * FROM {$table_prefix}_trigger_queue WHERE id = ?", array($dependent_on));
|
|
if ($result1 && $adb->num_rows($result1) > 0) {
|
|
$this->log("{$logtitle} error", "Queue element {$id} depends on {$dependent_on}.");
|
|
$this->log("{$logtitle} info", "End processing element, exception thrown");
|
|
throw new \Exception("Queue element $id depends on $dependent_on.");
|
|
}
|
|
}
|
|
|
|
// increment and check attempts
|
|
$adb->pquery("UPDATE {$table_prefix}_trigger_queue set attempts = attempts+1 WHERE id = ?", array($id));
|
|
$attempts = $adb->query_result($result, 0, 'attempts');
|
|
if ($attempts < $rabbitmqConnection['max_trigger_attempts']) {
|
|
|
|
$crmid = $adb->query_result($result, 0, 'crmid');
|
|
$module = $adb->query_result($result, 0, 'module');
|
|
$mode = $adb->query_result($result, 0, 'mode');
|
|
$info = \Zend_Json::decode($adb->query_result_no_html($result, 0, 'info'));
|
|
|
|
$focus = \CRMEntity::getInstance($module);
|
|
$err = $focus->retrieve_entity_info_no_html($crmid, $module, false);
|
|
if (empty($err)) {
|
|
require_once('data/VTEntityDelta.php');
|
|
$entityDelta = new \VTEntityDelta();
|
|
|
|
if (!empty($info['old_column_fields'])) {
|
|
$entity = \VTEntityData::fromEntityId($adb, $crmid);
|
|
$entity->focus->column_fields = $info['old_column_fields'];
|
|
$entityDelta->setOldEntity($module, $crmid, $entity);
|
|
}
|
|
$entity = \VTEntityData::fromEntityId($adb, $crmid);
|
|
$entity->focus->column_fields = $info['new_column_fields'];
|
|
$entityDelta->setNewEntity($module, $crmid, $entity);
|
|
$entityDelta->computeDelta($module, $crmid);
|
|
|
|
$focus->column_fields = $info['new_column_fields'];
|
|
$focus->mode = $mode;
|
|
|
|
require_once('include/events/include.inc');
|
|
require_once('modules/Settings/ProcessMaker/ProcessMakerHandler.php');
|
|
$em = new \VTEventsManager($adb);
|
|
// Initialize Event trigger cache
|
|
$em->initTriggerCache();
|
|
$entityData = \VTEntityData::fromCRMEntity($focus);
|
|
|
|
if ($mode == '') $entityData->setNew(true);
|
|
$processMakerHandler = new \ProcessMakerHandler();
|
|
$processMakerHandler->handleEvent('vte.entity.aftersave', $entityData);//crmv@207852
|
|
} else{
|
|
// skip row and set failed if the crmid is deleted
|
|
$this->log("{$logtitle} error", " [{$err}] retrieve failed");
|
|
$this->log("{$logtitle} info", "End processing element, moved to failed");
|
|
$allok = false;
|
|
$triggerQueueManager->move2Failed($id,$error);
|
|
}
|
|
} else {
|
|
$max_attempts_exhausted = true;
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
// catches exceptions inside the try{}
|
|
$triggerQueueManager->unlockDependents($id);
|
|
$this->log("{$logtitle} error", "Exception: ".$e->getMessage());
|
|
$this->log("{$logtitle} info", "End processing element, exception thrown");
|
|
throw new \Exception($e->getMessage()); //."\n".$e->getTraceAsString());
|
|
}
|
|
|
|
// restore die on error
|
|
$adb->setDieOnError($oldDieOnError);
|
|
|
|
// restore display errors
|
|
ini_set('display_errors', $oldDieOnError);
|
|
|
|
$triggerQueueManager->unlockDependents($id);
|
|
if (!$max_attempts_exhausted) {
|
|
$triggerQueueManager->deleteFromTriggerQueue($id);
|
|
} else {
|
|
$this->log("{$logtitle} error", "max attempts exhausted ({$rabbitmqConnection['max_trigger_attempts']})");
|
|
$this->log("{$logtitle} info", "End processing element, moved to failed");
|
|
$triggerQueueManager->move2Failed($id,'MAX_ATTEMPTS_EXHAUSTED');
|
|
}
|
|
|
|
$rabbitmqConnection = $triggerQueueManager->getConnectionParams('rabbitmq');
|
|
|
|
require_once('modules/Settings/ProcessMaker/ProcessMakerUtils.php');
|
|
$PMUtils = \ProcessMakerUtils::getInstance();
|
|
|
|
$triggerQueueManager->execute($rabbitmqConnection['defreeze_topic_name'], \Zend_Json::encode(array('id' => $id, 'action' => __FUNCTION__, 'module' => $module, 'record' => $crmid)), true);
|
|
|
|
$brothers = $PMUtils->getRecordsBrothers($crmid);
|
|
foreach ($brothers as $bid) {
|
|
$triggerQueueManager->execute($rabbitmqConnection['defreeze_topic_name'], \Zend_Json::encode(array('id' => $id, 'action' => __FUNCTION__, 'module' => $module, 'record' => $bid)), true);
|
|
}
|
|
|
|
if ($allok){
|
|
$end_time = microtime(true);
|
|
$time_consumed = round(($end_time-$start_time),2);
|
|
$this->log("{$logtitle} info", "End processing element, OK, took {$time_consumed}s");
|
|
}
|
|
|
|
return $id;
|
|
}
|
|
|
|
// catches fatal errors and exceptions
|
|
static function errorHandler($id) {
|
|
global $server;
|
|
|
|
$error = error_get_last();
|
|
$catchTypes = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR);
|
|
|
|
// NOTICE: in case of die() or exit(), it's not possible to detect if there was an error or not, so the next code won't be executed
|
|
if ($error !== null && in_array($error['type'], $catchTypes)) {
|
|
require_once('include/utils/TriggerQueueManager.php');
|
|
$triggerQueueManager = \TriggerQueueManager::getInstance();
|
|
$triggerQueueManager->unlockDependents($id);
|
|
$server->sendException(new \Exception($error['message'].' in '.$error['file'].'('.$error['line'].')'));
|
|
}
|
|
}
|
|
// crmv@171524e crmv@178291e crmv@199641e
|
|
} |