From f97dbf17827d5feb055a8b4e5c0198e8360de228 Mon Sep 17 00:00:00 2001 From: YuCheng Hu Date: Tue, 16 Nov 2010 14:51:21 +0000 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=88=B0=205.2=20=E6=AD=A3?= =?UTF-8?q?=E5=BC=8F=E7=89=88=E5=90=8E=E7=AC=AC=E4=B8=80=E6=AC=A1=20SVN=20?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit yuchenghu@hawebs.net git-svn-id: https://svn.code.sf.net/p/hawebs/svn@625 a2543c7e-f6e9-4f8a-8bff-1ffc34733512 --- .../trunk/modules/Mobile/Mobile-CHANGES | 8 + oss/vtiger/trunk/modules/Mobile/Mobile-README | 16 + .../trunk/modules/Mobile/Mobile.Config.php | 21 ++ oss/vtiger/trunk/modules/Mobile/Mobile.js | 60 ++++ oss/vtiger/trunk/modules/Mobile/Mobile.php | 229 +++++++++++++ .../trunk/modules/Mobile/MobileAjax.php | 13 + .../trunk/modules/Mobile/MobileHandler.php | 24 ++ oss/vtiger/trunk/modules/Mobile/api.php | 124 +++++++ .../trunk/modules/Mobile/api/Relation.php | 13 + .../trunk/modules/Mobile/api/Request.php | 56 ++++ .../trunk/modules/Mobile/api/Response.php | 62 ++++ .../trunk/modules/Mobile/api/Session.php | 43 +++ .../Mobile/api/ws/AddRecordComment.php | 59 ++++ .../Mobile/api/ws/AlertDetailsWithMessage.php | 50 +++ .../modules/Mobile/api/ws/Controller.php | 52 +++ .../modules/Mobile/api/ws/DeleteRecords.php | 42 +++ .../trunk/modules/Mobile/api/ws/Describe.php | 26 ++ .../modules/Mobile/api/ws/FetchAllAlerts.php | 35 ++ .../Mobile/api/ws/FetchModuleFilters.php | 79 +++++ .../modules/Mobile/api/ws/FetchRecord.php | 76 +++++ .../Mobile/api/ws/FetchRecordWithGrouping.php | 169 ++++++++++ .../api/ws/FetchRecordsWithGrouping.php | 110 +++++++ .../Mobile/api/ws/FilterDetailsWithCount.php | 57 ++++ .../Mobile/api/ws/ListModuleRecords.php | 188 +++++++++++ .../trunk/modules/Mobile/api/ws/Login.php | 62 ++++ .../Mobile/api/ws/LoginAndFetchModules.php | 50 +++ .../trunk/modules/Mobile/api/ws/Query.php | 61 ++++ .../Mobile/api/ws/QueryWithGrouping.php | 35 ++ .../api/ws/RelatedRecordsWithGrouping.php | 79 +++++ .../modules/Mobile/api/ws/SaveRecord.php | 84 +++++ .../Mobile/api/ws/SyncModuleRecords.php | 172 ++++++++++ .../trunk/modules/Mobile/api/ws/Utils.php | 305 ++++++++++++++++++ .../modules/Mobile/api/ws/models/Alert.php | 100 ++++++ .../modules/Mobile/api/ws/models/Filter.php | 47 +++ .../modules/Mobile/api/ws/models/Paging.php | 71 ++++ .../Mobile/api/ws/models/SearchFilter.php | 58 ++++ .../ws/models/alerts/EventsOfMineToday.php | 35 ++ .../ws/models/alerts/IdleTicketsOfMine.php | 27 ++ .../api/ws/models/alerts/NewTicketOfMine.php | 37 +++ .../ws/models/alerts/PendingTicketsOfMine.php | 33 ++ .../ws/models/alerts/PotentialsDueIn5Days.php | 33 ++ .../models/alerts/ServerTimeSampleAlert.php | 38 +++ oss/vtiger/trunk/modules/Mobile/api/wsapi.php | 127 ++++++++ oss/vtiger/trunk/modules/Mobile/ical.php | 214 ++++++++++++ oss/vtiger/trunk/modules/Mobile/index.php | 108 +++++++ .../modules/Mobile/language/en_us.lang.php | 15 + .../modules/Mobile/language/zh_cn.lang.php | 33 ++ oss/vtiger/trunk/modules/Mobile/schema.xml | 16 + 48 files changed, 3452 insertions(+) create mode 100644 oss/vtiger/trunk/modules/Mobile/Mobile-CHANGES create mode 100644 oss/vtiger/trunk/modules/Mobile/Mobile-README create mode 100644 oss/vtiger/trunk/modules/Mobile/Mobile.Config.php create mode 100644 oss/vtiger/trunk/modules/Mobile/Mobile.js create mode 100644 oss/vtiger/trunk/modules/Mobile/Mobile.php create mode 100644 oss/vtiger/trunk/modules/Mobile/MobileAjax.php create mode 100644 oss/vtiger/trunk/modules/Mobile/MobileHandler.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/Relation.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/Request.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/Response.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/Session.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/AddRecordComment.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/AlertDetailsWithMessage.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/Controller.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/DeleteRecords.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/Describe.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/FetchAllAlerts.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/FetchModuleFilters.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecord.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordWithGrouping.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordsWithGrouping.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/FilterDetailsWithCount.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/ListModuleRecords.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/Login.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/LoginAndFetchModules.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/Query.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/QueryWithGrouping.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/RelatedRecordsWithGrouping.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/SaveRecord.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/SyncModuleRecords.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/Utils.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/Alert.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/Filter.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/Paging.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/SearchFilter.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/EventsOfMineToday.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/IdleTicketsOfMine.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/NewTicketOfMine.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/PendingTicketsOfMine.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/PotentialsDueIn5Days.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/ServerTimeSampleAlert.php create mode 100644 oss/vtiger/trunk/modules/Mobile/api/wsapi.php create mode 100644 oss/vtiger/trunk/modules/Mobile/ical.php create mode 100644 oss/vtiger/trunk/modules/Mobile/index.php create mode 100644 oss/vtiger/trunk/modules/Mobile/language/en_us.lang.php create mode 100644 oss/vtiger/trunk/modules/Mobile/language/zh_cn.lang.php create mode 100644 oss/vtiger/trunk/modules/Mobile/schema.xml diff --git a/oss/vtiger/trunk/modules/Mobile/Mobile-CHANGES b/oss/vtiger/trunk/modules/Mobile/Mobile-CHANGES new file mode 100644 index 00000000..cb5c6e17 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/Mobile-CHANGES @@ -0,0 +1,8 @@ +CHANGES TO CORE PRODUCT +======================= + +Support for ORDER BY ... DESC was missing in 5.1.0, you will need to apply the patch available at: +* http://trac.vtiger.com/cgi-bin/trac.cgi/ticket/6635 - Should be available in 5.2.0 + +Support for overriding GetRelatedList global API +* http://trac.vtiger.com/cgi-bin/trac.cgi/ticket/6768 - Should be available in 5.2.0 diff --git a/oss/vtiger/trunk/modules/Mobile/Mobile-README b/oss/vtiger/trunk/modules/Mobile/Mobile-README new file mode 100644 index 00000000..76758d85 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/Mobile-README @@ -0,0 +1,16 @@ +Project URL: http://code.google.com/p/iui + +Modified version: http://www.k10design.net//iui/iui.js + +Further Customization: + +iui.js ++ Avoided re-fetching link each time by re-writing href after first fetch ++ To get back default behavior use 'nocache' for the link's class attribute + +iui.css ++ Added -moz- specific rules + +******************************************************************************* +Project URL: http://code.google.com/p/qcal/ +GNU Library or Lesser General Public License (LGPL) diff --git a/oss/vtiger/trunk/modules/Mobile/Mobile.Config.php b/oss/vtiger/trunk/modules/Mobile/Mobile.Config.php new file mode 100644 index 00000000..e180ef70 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/Mobile.Config.php @@ -0,0 +1,21 @@ + 'default.css', // Available in resources/skins + 'Navigation.Limit' => 25, + + // Control number of records sent out through API (SyncModuleRecords, Query...) which supports paging. + 'API_RECORD_FETCH_LIMIT' => 99, // NOTE: vtws_query internally limits fetch to 100 and give room to perform 1 extra fetch to determine paging + +); + +?> diff --git a/oss/vtiger/trunk/modules/Mobile/Mobile.js b/oss/vtiger/trunk/modules/Mobile/Mobile.js new file mode 100644 index 00000000..c4a24d1a --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/Mobile.js @@ -0,0 +1,60 @@ +/*+********************************************************************************** + * 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. + ************************************************************************************/ +if(typeof($) == 'undefined') { + $ = function(id) { + var node = document.getElementById(id); + if(typeof(node) == 'undefined') node = false; + return node; + } + $fnT = function(id1, id2) { + var node1 = $(id1); + var node2 = $(id2); + if(node1) node1.style.display = 'none'; + if(node2) node2.style.display = 'block'; + } + $fnFocus = function(id) { + try { + var node = $(id); + node.focus(); + } catch(error) { + } + } + + $fnAddClass = function(node, toadd) { + var classValue = node.className; + var regex = new RegExp(toadd, "g"); + if(classValue.match(regex) == null) { + classValue += " " + toadd; + node.className = classValue; + } + } + $fnRemoveClass = function(node, toremove) { + var classValue = node.className; + var regex = new RegExp(toremove, "g"); + classValue = classValue.replace(regex, ''); + node.className = classValue; + } + + $fnCheckboxOn = function(idprefix) { + //$fnT((idprefix+'_on'), (idprefix+'_off')); + + var nodeon = $(idprefix+'_on'); + var nodeoff = $(idprefix+'_off'); + if(nodeon) $fnAddClass(nodeon.parentNode, 'hide'); + if(nodeoff) $fnRemoveClass(nodeoff.parentNode, 'hide'); + } + $fnCheckboxOff = function(idprefix) { + //$fnT((idprefix+'_off'), (idprefix+'_on')); + + var nodeon = $(idprefix+'_on'); + var nodeoff = $(idprefix+'_off'); + if(nodeon) $fnRemoveClass(nodeon.parentNode, 'hide'); + if(nodeoff) $fnAddClass(nodeoff.parentNode, 'hide'); + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/Mobile.php b/oss/vtiger/trunk/modules/Mobile/Mobile.php new file mode 100644 index 00000000..32ab8845 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/Mobile.php @@ -0,0 +1,229 @@ +pquery("SELECT id FROM vtiger_mobile_alerts WHERE handler_path=? and handler_class=?", array($handlerPath, $handlerClass)); + if ($adb->num_rows($check)) { + return $adb->query_result($check, 0, 'id'); + } + return false; + } + static function alert_register($handlerPath, $handlerClass) { + global $adb; + if (self::alert_lookup($handlerPath, $handlerClass) === false) { + Vtiger_Utils::Log("Registered alert {$handlerClass} [$handlerPath]"); + $adb->pquery("INSERT INTO vtiger_mobile_alerts (handler_path, handler_class, deleted) VALUES(?,?,?)", array($handlerPath, $handlerClass, 0)); + } + } + static function alert_deregister($handlerPath, $handlerClass) { + global $adb; + Vtiger_Utils::Log("De-registered alert {$handlerClass} [$handlerPath]"); + $adb->pquery("DELETE FROM vtiger_mobile_alerts WHERE handler_path=? AND handler_class=?", array($handlerPath, $handlerClass)); + } + static function alert_markdeleted($handlerPath, $handlerClass, $flag) { + global $adb; + $adb->pquery("UPDATE vtiger_mobile_alerts SET deleted=? WHERE handler_path=? AND handler_class=?", array($flag, $handlerPath, $handlerClass)); + } + + /** + * Invoked when special actions are performed on the module. + * @param String Module name + * @param String Event Type (module.postinstall, module.disabled, module.enabled, module.preuninstall) + */ + function vtlib_handler($modulename, $event_type) { + + $registerWSAPI = false; + $registerAlerts = false; + + if($event_type == 'module.postinstall') { + $registerWSAPI = true; + $registerAlerts= true; + } else if($event_type == 'module.disabled') { + // TODO Handle actions when this module is disabled. + } else if($event_type == 'module.enabled') { + // TODO Handle actions when this module is enabled. + } else if($event_type == 'module.preuninstall') { + // TODO Handle actions when this module is about to be deleted. + } else if($event_type == 'module.preupdate') { + // TODO Handle actions before this module is updated. + } else if($event_type == 'module.postupdate') { + $registerWSAPI = true; + $registerAlerts= true; + } + + // Register alerts + if ($registerAlerts) { + self::alert_register('modules/Mobile/api/ws/models/alerts/IdleTicketsOfMine.php', 'Mobile_WS_AlertModel_IdleTicketsOfMine'); + self::alert_register('modules/Mobile/api/ws/models/alerts/NewTicketOfMine.php', 'Mobile_WS_AlertModel_NewTicketOfMine'); + self::alert_register('modules/Mobile/api/ws/models/alerts/PendingTicketsOfMine.php', 'Mobile_WS_AlertModel_PendingTicketsOfMine'); + self::alert_register('modules/Mobile/api/ws/models/alerts/PotentialsDueIn5Days.php', 'Mobile_WS_AlertModel_PotentialsDueIn5Days'); + self::alert_register('modules/Mobile/api/ws/models/alerts/EventsOfMineToday.php', 'Mobile_WS_AlertModel_EventsOfMineToday'); + } + + // Register webservice API + if($registerWSAPI) { + $operations = array(); + + $operations[] = array ( + 'name' => 'mobile.fetchallalerts', + 'handler' => 'mobile_ws_fetchAllAlerts', + ); + + $operations[] = array ( + 'name' => 'mobile.alertdetailswithmessage', + 'handler' => 'mobile_ws_alertDetailsWithMessage', + 'parameters' => array( array( 'name' => 'alertid', 'type' => 'string' ) ) + ); + + $operations[] = array ( + 'name' => 'mobile.fetchmodulefilters', + 'handler' => 'mobile_ws_fetchModuleFilters', + 'parameters' => array( array( 'name' => 'module', 'type' => 'string' ) ) + ); + + $operations[] = array ( + 'name' => 'mobile.fetchrecord', + 'handler' => 'mobile_ws_fetchRecord', + 'parameters' => array( array( 'name' => 'record', 'type' => 'string' ) ) + ); + + $operations[] = array ( + 'name' => 'mobile.fetchrecordwithgrouping', + 'handler' => 'mobile_ws_fetchRecordWithGrouping', + 'parameters' => array( array( 'name' => 'record', 'type' => 'string' ) ) + ); + + $operations[] = array ( + 'name' => 'mobile.filterdetailswithcount', + 'handler' => 'mobile_ws_filterDetailsWithCount', + 'parameters' => array( array( 'name' => 'filterid', 'type' => 'string' ) ) + ); + + $operations[] = array ( + 'name' => 'mobile.listmodulerecords', + 'handler' => 'mobile_ws_listModuleRecords', + 'parameters' => array( array( 'name' => 'elements', 'type' => 'encoded' ) ) + ); + + $operations[] = array ( + 'name' => 'mobile.saverecord', + 'handler' => 'mobile_ws_saveRecord', + 'parameters' => array( array( 'name' => 'module', 'type' => 'string' ), + array( 'name' => 'record', 'type' => 'string' ), + array( 'name' => 'values', 'type' => 'encoded' ), + ) + ); + + $operations[] = array ( + 'name' => 'mobile.syncModuleRecords', + 'handler' => 'mobile_ws_syncModuleRecords', + 'parameters' => array( array( 'name' => 'module', 'type' => 'string' ), + array( 'name' => 'syncToken', 'type' => 'string' ), + array( 'name' => 'page', 'type' => 'string' ), + ) + ); + + $operations[] = array ( + 'name' => 'mobile.query', + 'handler' => 'mobile_ws_query', + 'parameters' => array( array( 'name' => 'module', 'type' => 'string' ), + array( 'name' => 'query', 'type' => 'string' ), + array( 'name' => 'page', 'type' => 'string' ), + ) + ); + + $operations[] = array ( + 'name' => 'mobile.querywithgrouping', + 'handler' => 'mobile_ws_queryWithGrouping', + 'parameters' => array( array( 'name' => 'module', 'type' => 'string' ), + array( 'name' => 'query', 'type' => 'string' ), + array( 'name' => 'page', 'type' => 'string' ), + ) + ); + + foreach($operations as $o) { + $operation = new Mobile_WS_Operation($o['name'], $o['handler'], 'modules/Mobile/api/wsapi.php', 'POST'); + if(!empty($o['parameters'])) { + foreach($o['parameters'] as $p) { + $operation->addParameter($p['name'], $p['type']); + } + } + $operation->register(); + } + } + } +} + +/* Helper functions */ +class Mobile_WS_Operation { + var $opName, $opClass, $opFile, $opType; + var $parameters = array(); + + function __construct($apiName, $className, $handlerFile, $reqType) { + $this->opName = $apiName; + $this->opClass= $className; + $this->opFile = $handlerFile; + $this->opType = $reqType; + } + + function addParameter($name, $type) { + $this->parameters[] = array('name' => $name, 'type' => $type); + return $this; + } + + function register() { + global $adb; + $checkresult = $adb->pquery("SELECT 1 FROM vtiger_ws_operation WHERE name = ?", array($this->opName)); + if($adb->num_rows($checkresult)) { + return; + } + + Vtiger_Utils::Log("Enabling webservice operation {$this->opName}", true); + + $operationid = vtws_addWebserviceOperation($this->opName, $this->opFile, $this->opClass, $this->opType); + for($index = 0; $index < count($this->parameters); ++$index) { + vtws_addWebserviceOperationParam($operationid, $this->parameters[$index]['name'], $this->parameters[$index]['type'], ($index+1)); + } + } +} + +?> diff --git a/oss/vtiger/trunk/modules/Mobile/MobileAjax.php b/oss/vtiger/trunk/modules/Mobile/MobileAjax.php new file mode 100644 index 00000000..23f2cb50 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/MobileAjax.php @@ -0,0 +1,13 @@ + diff --git a/oss/vtiger/trunk/modules/Mobile/MobileHandler.php b/oss/vtiger/trunk/modules/Mobile/MobileHandler.php new file mode 100644 index 00000000..40dfc172 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/MobileHandler.php @@ -0,0 +1,24 @@ + diff --git a/oss/vtiger/trunk/modules/Mobile/api.php b/oss/vtiger/trunk/modules/Mobile/api.php new file mode 100644 index 00000000..ed7d693a --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api.php @@ -0,0 +1,124 @@ + array('file' => '/api/ws/Login.php', 'class' => 'Mobile_WS_Login'), + 'loginAndFetchModules' => array('file' => '/api/ws/LoginAndFetchModules.php', 'class' => 'Mobile_WS_LoginAndFetchModules'), + 'fetchModuleFilters' => array('file' => '/api/ws/FetchModuleFilters.php' , 'class' => 'Mobile_WS_FetchModuleFilters'), + 'filterDetailsWithCount' => array('file' => '/api/ws/FilterDetailsWithCount.php', 'class' => 'Mobile_WS_FilterDetailsWithCount'), + 'fetchAllAlerts' => array('file' => '/api/ws/FetchAllAlerts.php', 'class' => 'Mobile_WS_FetchAllAlerts'), + 'alertDetailsWithMessage' => array('file' => '/api/ws/AlertDetailsWithMessage.php', 'class' => 'Mobile_WS_AlertDetailsWithMessage'), + 'listModuleRecords' => array('file' => '/api/ws/ListModuleRecords.php', 'class' => 'Mobile_WS_ListModuleRecords'), + 'fetchRecord' => array('file' => '/api/ws/FetchRecord.php', 'class' => 'Mobile_WS_FetchRecord'), + 'fetchRecordWithGrouping' => array('file' => '/api/ws/FetchRecordWithGrouping.php', 'class' => 'Mobile_WS_FetchRecordWithGrouping'), + 'fetchRecordsWithGrouping' => array('file' => '/api/ws/FetchRecordsWithGrouping.php', 'class' => 'Mobile_WS_FetchRecordsWithGrouping'), + 'describe' => array('file' => '/api/ws/Describe.php', 'class' => 'Mobile_WS_Describe'), + 'saveRecord' => array('file' => '/api/ws/SaveRecord.php', 'class' => 'Mobile_WS_SaveRecord'), + 'syncModuleRecords' => array('file' => '/api/ws/SyncModuleRecords.php', 'class' => 'Mobile_WS_SyncModuleRecords'), + + 'query' => array('file' => '/api/ws/Query.php', 'class' => 'Mobile_WS_Query'), + 'queryWithGrouping' => array('file' => '/api/ws/QueryWithGrouping.php', 'class' => 'Mobile_WS_QueryWithGrouping'), + + 'relatedRecordsWithGrouping' => array('file' => '/api/ws/RelatedRecordsWithGrouping.php', 'class' => 'Mobile_WS_RelatedRecordsWithGrouping'), + 'deleteRecords' => array('file' => '/api/ws/DeleteRecords.php', 'class' => 'Mobile_WS_DeleteRecords'), + + 'addRecordComment' => array('file' => '/api/ws/AddRecordComment.php', 'class' => 'Mobile_WS_AddRecordComment'), + ); + + static function process(Mobile_API_Request $request) { + $operation = $request->getOperation(); + $sessionid = $request->getSession(); + + $response = false; + if(isset(self::$opControllers[$operation])) { + + $operationFile = self::$opControllers[$operation]['file']; + $operationClass= self::$opControllers[$operation]['class']; + + include_once dirname(__FILE__) . $operationFile; + $operationController = new $operationClass; + + $operationSession = false; + if($operationController->requireLogin()) { + $operationSession = Mobile_API_Session::init($sessionid); + if($operationController->hasActiveUser() === false) { + $operationSession = false; + } + //Mobile_WS_Utils::initAppGlobals(); + } else { + // By-pass login + $operationSession = true; + } + + if($operationSession === false) { + $response = new Mobile_API_Response(); + $response->setError(1501, 'Login required'); + } else { + + try { + $response = $operationController->process($request); + } catch(Exception $e) { + $response = new Mobile_API_Response(); + $response->setError($e->getCode(), $e->getMessage()); + } + } + + } else { + $response = new Mobile_API_Response(); + $response->setError(1404, 'Operation not found: ' . $operation); + } + + if($response !== false) { + echo $response->emitJSON(); + } + } +} + +/** Take care of stripping the slashes */ +function stripslashes_recursive($value) { + $value = is_array($value) ? array_map('stripslashes_recursive', $value) : stripslashes($value); + return $value; +} +/** END **/ + +if(!defined('MOBILE_API_CONTROLLER_AVOID_TRIGGER')) { + $clientRequestValues = $_POST; // $_REQUEST or $_GET + + $clientRequestValuesRaw = array(); + + // Set of request key few controllers are interested in raw values (example, SaveRecord) + /*$rawValueHeaders = array('values'); + foreach($rawValueHeaders as $rawValueHeader) { + if(isset($clientRequestValues[$rawValueHeader])) { + $clientRequestValuesRaw[$rawValueHeader] = $clientRequestValues[$rawValueHeader]; + } + }*/ + // END + + if (get_magic_quotes_gpc()) { + $clientRequestValues = stripslashes_recursive($clientRequestValues); + } + Mobile_API_Controller::process(new Mobile_API_Request($clientRequestValues, $clientRequestValuesRaw)); +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/Relation.php b/oss/vtiger/trunk/modules/Mobile/api/Relation.php new file mode 100644 index 00000000..8a165f04 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/Relation.php @@ -0,0 +1,13 @@ + $query ); +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/Request.php b/oss/vtiger/trunk/modules/Mobile/api/Request.php new file mode 100644 index 00000000..105215da --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/Request.php @@ -0,0 +1,56 @@ +valuemap = $values; + $this->rawvaluemap = $rawvalues; + } + + function get($key, $defvalue = '') { + if(isset($this->valuemap[$key])) { + return $this->valuemap[$key]; + } + if($defvalue === '' && isset($this->defaultmap[$key])) { + $defvalue = $this->defaultmap[$key]; + } + return $defvalue; + } + + function has($key) { + return isset($this->valuemap[$key]); + } + + function getRaw($key, $defvalue = '') { + if (isset($this->rawvaluemap[$key])) { + return $this->rawvaluemap[$key]; + } + return $this->get($key, $defvalue); + } + + function set($key, $newvalue) { + $this->valuemap[$key]= $newvalue; + } + + function setDefault($key, $defvalue) { + $this->defaultmap[$key] = $defvalue; + } + + function getOperation() { + return $this->get('_operation'); + } + + function getSession() { + return $this->get('_session'); + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/Response.php b/oss/vtiger/trunk/modules/Mobile/api/Response.php new file mode 100644 index 00000000..d51f34db --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/Response.php @@ -0,0 +1,62 @@ + $code, 'message' => $message); + $this->error = $error; + } + + function getError() { + return $this->error; + } + + function hasError() { + return !is_null($this->error); + } + + function setResult($result) { + $this->result = $result; + } + + function getResult() { + return $this->result; + } + + function addToResult($key, $value) { + $this->result[$key] = $value; + } + + function prepareResponse() { + $response = array(); + if($this->result === NULL) { + $response['success'] = false; + $response['error'] = $this->error; + } else { + $response['success'] = true; + $response['result'] = $this->result; + } + return $response; + } + + function emitJSON() { + return Zend_Json::encode($this->prepareResponse()); + } + + function emitHTML() { + if($this->result === NULL) return (is_string($this->error))? $this->error : var_export($this->error, true); + return $this->result; + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/Session.php b/oss/vtiger/trunk/modules/Mobile/api/Session.php new file mode 100644 index 00000000..f11625ef --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/Session.php @@ -0,0 +1,43 @@ +retrieve_entity_info($recordComponents[1], $targetModule); + $focus->id = $recordComponents[1]; + $focus->mode = 'edit'; + $focus->column_fields['comments'] = $commentcontent; + $focus->save($targetModule); + return false; + } + + function process(Mobile_API_Request $request) { + + $values = Zend_Json::decode($request->get('values')); + $relatedTo = $values['related_to']; + $commentContent = $values['commentcontent']; + + $user = $this->getActiveUser(); + + $targetModule = ''; + if (!empty($relatedTo) && Mobile_WS_Utils::detectModulenameFromRecordId($relatedTo) == 'HelpDesk') { + $targetModule = 'HelpDesk'; + } else { + $targetModule = 'ModComments'; + } + + $response = false; + if ($targetModule == 'HelpDesk') { + $response = $this->saveCommentToHelpDesk($commentContent, $relatedTo, $user); + } else { + if (vtlib_isModuleActive($targetModule)) { + $request->set('module', $targetModule); + $values['assigned_user_id'] = sprintf('%sx%s', Mobile_WS_Utils::getEntityModuleWSId('Users'), $user->id); + $request->set('values', Zend_Json::encode($values) ); + + $response = parent::process($request); + } + } + return $response; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/AlertDetailsWithMessage.php b/oss/vtiger/trunk/modules/Mobile/api/ws/AlertDetailsWithMessage.php new file mode 100644 index 00000000..2a65732c --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/AlertDetailsWithMessage.php @@ -0,0 +1,50 @@ +get('alertid'); + $current_user = $this->getActiveUser(); + + $alert = $this->getAlertDetails($alertid); + if(empty($alert)) { + $response->setError(1401, 'Alert not found'); + } else { + $result = array(); + $result['alert'] = $this->getAlertDetails($alertid); + $response->setResult($result); + } + + return $response; + } + + function getAlertDetails($alertid) { + + $alertModel = Mobile_WS_AlertModel::modelWithId($alertid); + + $alert = false; + if($alertModel) { + $alert = $alertModel->serializeToSend(); + + $alertModel->setUser($this->getActiveUser()); + $alert['message'] = $alertModel->message(); + } + + return $alert; + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/Controller.php b/oss/vtiger/trunk/modules/Mobile/api/ws/Controller.php new file mode 100644 index 00000000..c6eb578f --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/Controller.php @@ -0,0 +1,52 @@ +activeUser = $user; + } + + protected function setActiveUser($user) { + $this->sessionSet('_authenticated_user_id', $user->id); + $this->initActiveUser($user); + } + + protected function getActiveUser() { + if($this->activeUser === false) { + $userid = $this->sessionGet('_authenticated_user_id'); + if(!empty($userid)) { + $this->activeUser = CRMEntity::getInstance('Users'); + $this->activeUser->retrieveCurrentUserInfoFromFile($userid); + } + } + return $this->activeUser; + } + + function hasActiveUser() { + $user = $this->getActiveUser(); + return ($user !== false); + } + + function sessionGet($key, $defvaule = '') { + return Mobile_API_Session::get($key, $defvalue); + } + + function sessionSet($key, $value) { + Mobile_API_Session::set($key, $value); + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/DeleteRecords.php b/oss/vtiger/trunk/modules/Mobile/api/ws/DeleteRecords.php new file mode 100644 index 00000000..694aa1da --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/DeleteRecords.php @@ -0,0 +1,42 @@ +getActiveUser(); + + $records = $request->get('records'); + if (empty($records)) { + $records = array($request->get('record')); + } else { + $records = Zend_Json::decode($records); + } + + $deleted = array(); + foreach($records as $record) { + try { + vtws_delete($record, $current_user); + $result = true; + } catch(Exception $e) { + $result = false; + } + $deleted[$record] = $result; + } + + $response = new Mobile_API_Response(); + $response->setResult(array('deleted' => $deleted)); + + return $response; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/Describe.php b/oss/vtiger/trunk/modules/Mobile/api/ws/Describe.php new file mode 100644 index 00000000..2fcf84b8 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/Describe.php @@ -0,0 +1,26 @@ +getActiveUser(); + + $module = $request->get('module'); + $describeInfo = vtws_describe($module, $current_user); + Mobile_WS_Utils::fixDescribeFieldInfo($module, $describeInfo); + + $response = new Mobile_API_Response(); + $response->setResult(array('describe' => $describeInfo)); + + return $response; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/FetchAllAlerts.php b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchAllAlerts.php new file mode 100644 index 00000000..7348b845 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchAllAlerts.php @@ -0,0 +1,35 @@ +getActiveUser(); + + $result = array(); + $result['alerts'] = $this->getAlertDetails(); + $response->setResult($result); + + return $response; + } + + function getAlertDetails() { + $alertModels = Mobile_WS_AlertModel::models(); + + $alerts = array(); + foreach($alertModels as $alertModel) { + $alerts[] = $alertModel->serializeToSend();; + } + return $alerts; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/FetchModuleFilters.php b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchModuleFilters.php new file mode 100644 index 00000000..e27e1759 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchModuleFilters.php @@ -0,0 +1,79 @@ +get('module'); + $current_user = $this->getActiveUser(); + + $result = array(); + + $filters = $this->getModuleFilters($module, $current_user); + $yours = array(); + $others= array(); + if(!empty($filters)) { + foreach($filters as $filter) { + if($filter['userName'] == $current_user->column_fields['user_name']) { + $yours[] = $filter; + } else { + $others[]= $filter; + } + } + } + + $result['filters'] = array('yours' => $yours, 'others' => $others); + $response->setResult($result); + + return $response; + } + + protected function getModuleFilters($moduleName, $user) { + + $filters = array(); + + global $adb; + $sql = "SELECT vtiger_customview.*, vtiger_users.user_name FROM vtiger_customview + INNER JOIN vtiger_users ON vtiger_customview.userid = vtiger_users.id WHERE vtiger_customview.entitytype=?"; + $parameters = array($moduleName); + + if(!is_admin($user)) { + require('user_privileges/user_privileges_'.$user->id.'.php'); + + $sql .= " AND (vtiger_customview.status=0 or vtiger_customview.userid = ? or vtiger_customview.status = 3 or vtiger_customview.userid IN + (SELECT vtiger_user2role.userid FROM vtiger_user2role INNER JOIN vtiger_users on vtiger_users.id=vtiger_user2role.userid + INNER JOIN vtiger_role on vtiger_role.roleid=vtiger_user2role.roleid WHERE vtiger_role.parentrole LIKE '".$current_user_parent_role_seq."::%'))"; + + array_push($parameters, $current_user->id); + } + + $result = $adb->pquery($sql, $parameters); + if($result && $adb->num_rows($result)) { + while($resultrow = $adb->fetch_array($result)) { + $filters[] = $this->prepareFilterDetailUsingResultRow($resultrow); + } + } + + return $filters; + } + + protected function prepareFilterDetailUsingResultRow($resultrow) { + $filter = array(); + $filter['cvid'] = $resultrow['cvid']; + $filter['viewname'] = decode_html($resultrow['viewname']); + $filter['setdefault'] = $resultrow['setdefault']; + $filter['setmetrics'] = $resultrow['setmetrics']; + $filter['moduleName'] = decode_html($resultrow['entitytype']); + $filter['status'] = decode_html($resultrow['status']); + $filter['userName'] = decode_html($resultrow['user_name']); + return $filter; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecord.php b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecord.php new file mode 100644 index 00000000..fdb64a54 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecord.php @@ -0,0 +1,76 @@ +module === false) { + $this->module = Mobile_WS_Utils::detectModulenameFromRecordId($recordid); + } + return $this->module; + } + + protected function processRetrieve(Mobile_API_Request $request) { + $current_user = $this->getActiveUser(); + + $recordid = $request->get('record'); + $record = vtws_retrieve($recordid, $current_user); + + return $record; + } + + function process(Mobile_API_Request $request) { + $current_user = $this->getActiveUser(); + $record = $this->processRetrieve($request); + + $this->resolveRecordValues($record, $current_user); + + $response = new Mobile_API_Response(); + $response->setResult(array('record' => $record)); + + return $response; + } + + function resolveRecordValues(&$record, $user, $ignoreUnsetFields=false) { + if(empty($record)) return $record; + + $fieldnamesToResolve = Mobile_WS_Utils::detectFieldnamesToResolve( + $this->detectModuleName($record['id']) ); + + if(!empty($fieldnamesToResolve)) { + foreach($fieldnamesToResolve as $resolveFieldname) { + if ($ignoreUnsetFields === false || isset($record[$resolveFieldname])) { + $fieldvalueid = $record[$resolveFieldname]; + $fieldvalue = $this->fetchRecordLabelForId($fieldvalueid, $user); + $record[$resolveFieldname] = array('value' => $fieldvalueid, 'label'=>$fieldvalue); + } + } + } + } + + function fetchRecordLabelForId($id, $user) { + $value = null; + + if (isset($this->resolvedValueCache[$id])) { + $value = $this->resolvedValueCache[$id]; + } else if(!empty($id)) { + $value = trim(vtws_getName($id, $user)); + $this->resolvedValueCache[$id] = $value; + } else { + $value = $id; + } + return $value; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordWithGrouping.php b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordWithGrouping.php new file mode 100644 index 00000000..15571464 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordWithGrouping.php @@ -0,0 +1,169 @@ +_cachedDescribeInfo = $describeInfo; + $this->_cachedDescribeFieldInfo = array(); + if(!empty($describeInfo['fields'])) { + foreach($describeInfo['fields'] as $describeFieldInfo) { + $this->_cachedDescribeFieldInfo[$describeFieldInfo['name']] = $describeFieldInfo; + } + } + } + + protected function cachedDescribeInfo() { + return $this->_cachedDescribeInfo; + } + + protected function cachedDescribeFieldInfo($fieldname) { + if ($this->_cachedDescribeFieldInfo !== false) { + if(isset($this->_cachedDescribeFieldInfo[$fieldname])) { + return $this->_cachedDescribeFieldInfo[$fieldname]; + } + } + return false; + } + + protected function cachedEntityFieldnames($module) { + $describeInfo = $this->cachedDescribeInfo(); + $labelFields = $describeInfo['labelFields']; + switch($module) { + case 'HelpDesk': $labelFields = 'ticket_title'; break; + case 'Documents': $labelFields = 'notes_title'; break; + } + return explode(',', $labelFields); + } + + protected function isTemplateRecordRequest(Mobile_API_Request $request) { + $recordid = $request->get('record'); + return (preg_match("/([0-9]+)x0/", $recordid)); + } + + protected function processRetrieve(Mobile_API_Request $request) { + $recordid = $request->get('record'); + + // Create a template record for use + if ($this->isTemplateRecordRequest($request)) { + $current_user = $this->getActiveUser(); + + $module = $this->detectModuleName($recordid); + $describeInfo = vtws_describe($module, $current_user); + Mobile_WS_Utils::fixDescribeFieldInfo($module, $describeInfo); + + $this->cacheDescribeInfo($describeInfo); + + $templateRecord = array(); + foreach($describeInfo['fields'] as $describeField) { + $templateFieldValue = ''; + if (isset($describeField['type']) && isset($describeField['type']['defaultValue'])) { + $templateFieldValue = $describeField['type']['defaultValue']; + } else if (isset($describeField['default'])) { + $templateFieldValue = $describeField['default']; + } + $templateRecord[$describeField['name']] = $templateFieldValue; + } + if (isset($templateRecord['assigned_user_id'])) { + $templateRecord['assigned_user_id'] = sprintf("%sx%s", Mobile_WS_Utils::getEntityModuleWSId('Users'), $current_user->id); + } + // Reset the record id + $templateRecord['id'] = $recordid; + + return $templateRecord; + } + + // Or else delgate the action to parent + return parent::processRetrieve($request); + } + + function process(Mobile_API_Request $request) { + $response = parent::process($request); + return $this->processWithGrouping($request, $response); + } + + protected function processWithGrouping(Mobile_API_Request $request, $response) { + $isTemplateRecord = $this->isTemplateRecordRequest($request); + $result = $response->getResult(); + + $resultRecord = $result['record']; + $module = $this->detectModuleName($resultRecord['id']); + + $modifiedRecord = $this->transformRecordWithGrouping($resultRecord, $module, $isTemplateRecord); + $response->setResult(array('record' => $modifiedRecord)); + + return $response; + } + + protected function transformRecordWithGrouping($resultRecord, $module, $isTemplateRecord=false) { + $current_user = $this->getActiveUser(); + + $moduleFieldGroups = Mobile_WS_Utils::gatherModuleFieldGroupInfo($module); + + $modifiedResult = array(); + + $blocks = array(); $labelFields = false; + foreach($moduleFieldGroups as $blocklabel => $fieldgroups) { + $fields = array(); + foreach($fieldgroups as $fieldname => $fieldinfo) { + // Pickup field if its part of the result + if(isset($resultRecord[$fieldname])) { + $field = array( + 'name' => $fieldname, + 'value' => $resultRecord[$fieldname], + 'label' => $fieldinfo['label'], + 'uitype'=> $fieldinfo['uitype'] + ); + + // Template record requested send more details if available + if ($isTemplateRecord) { + $describeFieldInfo = $this->cachedDescribeFieldInfo($fieldname); + foreach($describeFieldInfo as $k=>$v) { + if (isset($field[$k])) continue; + $field[$k] = $v; + } + // Entity fieldnames + $labelFields = $this->cachedEntityFieldnames($module); + } + // Fix the assigned to uitype + if ($field['uitype'] == '53') { + $field['type']['defaultValue'] = array('value' => "19x{$current_user->id}", 'label' => $current_user->column_fields['last_name']); + } else if($field['uitype'] == '117') { + $field['type']['defaultValue'] = $field['value']; + } + // END + + $fields[] = $field; + } + } + $blocks[] = array( 'label' => $blocklabel, 'fields' => $fields ); + } + + $sections = array(); + $moduleFieldGroupKeys = array_keys($moduleFieldGroups); + foreach($moduleFieldGroupKeys as $blocklabel) { + // Eliminate empty blocks + if(isset($groups[$blocklabel]) && !empty($groups[$blocklabel])) { + $sections[] = array( 'label' => $blocklabel, 'count' => count($groups[$blocklabel]) ); + } + } + + $modifiedResult = array('blocks' => $blocks, 'id' => $resultRecord['id']); + if($labelFields) $modifiedResult['labelFields'] = $labelFields; + + return $modifiedResult; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordsWithGrouping.php b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordsWithGrouping.php new file mode 100644 index 00000000..1800cc59 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordsWithGrouping.php @@ -0,0 +1,110 @@ +getActiveUser(); + + $module = $request->get('module'); + $moduleWSID = Mobile_WS_Utils::getEntityModuleWSId($module); + + if (empty($module)) { + $response->setError(1501, "Module not specified."); + return $response; + } + + $records = array(); + + // Fetch the request parameters + $idlist = $request->get('ids'); + $alertid = $request->get('alertid'); + + // List of ids specified? + if (!empty($idlist)) { + $idlist = Zend_Json::decode($idlist); + $records = $this->fetchRecordsWithId($module, $idlist, $current_user); + } + // Alert id specified? + else if (!empty($alertid)) { + $alert = Mobile_WS_AlertModel::modelWithId($alertid); + if ($alert === false) { + $response->setError(1404, "Alert not found."); + $records = false; + } + + $alert->setUser($current_user); + $records = $this->fetchAlertRecords($module, $alert); + } + + if ($records !== false) { + $response->setResult(array('records' => $records)); + } + + return $response; + } + + function fetchRecordsWithId($module, $idlist, $user) { + if (empty($idlist)) return array(); + + $wsresult = vtws_query(sprintf("SELECT * FROM {$module} WHERE id IN ('%s');", implode("','", $idlist)), $user); + if (!empty($wsresult)) { + $resolvedRecords = array(); + foreach($wsresult as $record) { + $this->resolveRecordValues($record, $user); + $resolvedRecords[] = $this->transformRecordWithGrouping($record, $module, false); + } + } + return $resolvedRecords; + } + + function fetchAlertRecords($module, $alert) { + global $adb; + + // Initialize global variable: ($alert->query() could indirectly depend if its using Module API as its base) + global $current_user; + if (!isset($current_user)) $current_user = $alert->getUser(); + + $moduleWSID = Mobile_WS_Utils::getEntityModuleWSId($module); + + $alertResult = $adb->pquery($alert->query(), $alert->queryParameters()); + $alertRecords = array(); + + // For Calendar module there is a need for merging Todo's + if ($module == 'Calendar') { + $eventsWSID = Mobile_WS_Utils::getEntityModuleWSId('Events'); + $eventIds = array(); $taskIds = array(); + while($resultrow = $adb->fetch_array($alertResult)) { + if (isset($resultrow['activitytype']) && $resultrow['activitytype'] == 'Task') { + $taskIds[] = "{$moduleWSID}x". $resultrow['crmid']; + } else { + $eventIds[] = "{$eventsWSID}x". $resultrow['crmid']; + } + } + $alertRecords = $this->fetchRecordsWithId($module, $taskIds, $alert->getUser()); + if (!empty($eventIds)) { + $alertRecords = array_merge($alertRecords, $this->fetchRecordsWithId('Events', $eventIds, $alert->getUser())); + } + } else { + $fetchIds = array(); + while($resultrow = $adb->fetch_array($alertResult)) { + $fetchIds[] = "{$moduleWSID}x" . $resultrow['crmid']; + } + $alertRecords = $this->fetchRecordsWithId($module, $fetchIds, $alert->getUser()); + } + return $alertRecords; + } + +} diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/FilterDetailsWithCount.php b/oss/vtiger/trunk/modules/Mobile/api/ws/FilterDetailsWithCount.php new file mode 100644 index 00000000..8879408f --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/FilterDetailsWithCount.php @@ -0,0 +1,57 @@ +get('filterid'); + $current_user = $this->getActiveUser(); + + $result = array(); + $result['filter'] = $this->getModuleFilterDetails($filterid); + $response->setResult($result); + + return $response; + } + + protected function getModuleFilterDetails($filterid) { + global $adb; + $result = $adb->pquery("SELECT * FROM vtiger_customview WHERE cvid=?", array($filterid)); + if ($result && $adb->num_rows($result)) { + $resultrow = $adb->fetch_array($result); + + $module = $resultrow['entitytype']; + + $view = new CustomView($module); + $viewid = $resultrow['cvid']; + $view->getCustomViewByCvid($viewid); + $viewQuery = $view->getModifiedCvListQuery($viewid, getListQuery($module), $module); + + $countResult = $adb->pquery(mkCountQuery($viewQuery), array()); + $count = 0; + if($countResult && $adb->num_rows($countResult)) { + $count = $adb->query_result($countResult, 0, 'count'); + } + + $filter = $this->prepareFilterDetailUsingResultRow($resultrow); + $filter['userName'] = getUserName($resultrow['userid']); + $filter['count'] = $count; + + return $filter; + } + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/ListModuleRecords.php b/oss/vtiger/trunk/modules/Mobile/api/ws/ListModuleRecords.php new file mode 100644 index 00000000..3a438f3c --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/ListModuleRecords.php @@ -0,0 +1,188 @@ +get('page', 0); + return Mobile_WS_PagingModel::modelWithPageStart($page); + } + + function process(Mobile_API_Request $request) { + return $this->processSearchRecordLabel($request); + } + + function processSearchRecordLabel(Mobile_API_Request $request) { + global $current_user; // Few core API assumes this variable availability + + $current_user = $this->getActiveUser(); + $module = $request->get('module'); + $alertid = $request->get('alertid'); + $filterid = $request->get('filterid'); + $search = $request->get('search'); + + $filterOrAlertInstance = false; + if(!empty($alertid)) { + $filterOrAlertInstance = Mobile_WS_AlertModel::modelWithId($alertid); + } + else if(!empty($filterid)) { + $filterOrAlertInstance = Mobile_WS_FilterModel::modelWithId($module, $filterid); + } + else if(!empty($search)) { + $filterOrAlertInstance = $this->getSearchFilterModel($module, $search); + } + + if($filterOrAlertInstance && strcmp($module, $filterOrAlertInstance->moduleName)) { + $response = new Mobile_API_Response(); + $response->setError(1001, 'Mistached module information.'); + return $response; + } + + // Initialize with more information + if($filterOrAlertInstance) { + $filterOrAlertInstance->setUser($current_user); + } + + // Paging model + $pagingModel = $this->getPagingModel($request); + + if($this->isCalendarModule($module)) { + return $this->processSearchRecordLabelForCalendar($request, $pagingModel); + } + $records = $this->fetchRecordLabelsForModule($module, $current_user, array(), $filterOrAlertInstance, $pagingModel); + + $modifiedRecords = array(); + foreach($records as $record) { + if ($record instanceof SqlResultIteratorRow) { + $record = $record->data; + // Remove all integer indexed mappings + for($index = count($record); $index > -1; --$index) { + if(isset($record[$index])) { + unset($record[$index]); + } + } + } + + $recordid = $record['id']; + unset($record['id']); + + $eventstart = ''; + if($this->isCalendarModule($module)) { + $eventstart = $record['date_start']; + unset($record['date_start']); + } + + $values = array_values($record); + $label = implode(' ', $values); + + $modifiedRecord = array('id' => $recordid, 'label'=>$label); + if(!empty($eventstart)) { + $modifiedRecord['eventstart'] = $eventstart; + } + $modifiedRecords[] = $modifiedRecord; + } + + $response = new Mobile_API_Response(); + $response->setResult(array('records'=>$modifiedRecords, 'module'=>$module)); + + return $response; + } + + function processSearchRecordLabelForCalendar(Mobile_API_Request $request, $pagingModel = false) { + $current_user = $this->getActiveUser(); + + // Fetch both Calendar (Todo) and Event information + $moreMetaFields = array('date_start', 'time_start', 'activitytype', 'location'); + $eventsRecords = $this->fetchRecordLabelsForModule('Events', $current_user, $moreMetaFields, false, $pagingModel); + $calendarRecords=$this->fetchRecordLabelsForModule('Calendar', $current_user, $moreMetaFields, false, $pagingModel); + + // Merge the Calendar & Events information + $records = array_merge($eventsRecords, $calendarRecords); + + $modifiedRecords = array(); + foreach($records as $record) { + $modifiedRecord = array(); + $modifiedRecord['id'] = $record['id']; unset($record['id']); + $modifiedRecord['eventstartdate'] = $record['date_start']; unset($record['date_start']); + $modifiedRecord['eventstarttime'] = $record['time_start']; unset($record['time_start']); + $modifiedRecord['eventtype'] = $record['activitytype']; unset($record['activitytype']); + $modifiedRecord['eventlocation'] = $record['location']; unset($record['location']); + + $modifiedRecord['label'] = implode(' ',array_values($record)); + + $modifiedRecords[] = $modifiedRecord; + } + + $response = new Mobile_API_Response(); + $response->setResult(array('records' =>$modifiedRecords, 'module'=>'Calendar')); + + return $response; + } + + function fetchRecordLabelsForModule($module, $user, $morefields=array(), $filterOrAlertInstance=false, $pagingModel = false) { + if($this->isCalendarModule($module)) { + $fieldnames = Mobile_WS_Utils::getEntityFieldnames('Calendar'); + } else { + $fieldnames = Mobile_WS_Utils::getEntityFieldnames($module); + } + + if(!empty($morefields)) { + foreach($morefields as $fieldname) $fieldnames[] = $fieldname; + } + + if($filterOrAlertInstance === false) { + $filterOrAlertInstance = Mobile_WS_SearchFilterModel::modelWithCriterias($module); + $filterOrAlertInstance->setUser($user); + } + + return $this->queryToSelectFilteredRecords($module, $fieldnames, $filterOrAlertInstance, $pagingModel); + } + + function queryToSelectFilteredRecords($module, $fieldnames, $filterOrAlertInstance, $pagingModel) { + + if ($filterOrAlertInstance instanceof Mobile_WS_SearchFilterModel) { + return $filterOrAlertInstance->execute($fieldnames, $pagingModel); + } + + global $adb; + + $moduleWSId = Mobile_WS_Utils::getEntityModuleWSId($module); + $columnByFieldNames = Mobile_WS_Utils::getModuleColumnTableByFieldNames($module, $fieldnames); + + // Build select clause similar to Webservice query + $selectColumnClause = "CONCAT('{$moduleWSId}','x',vtiger_crmentity.crmid) as id,"; + foreach($columnByFieldNames as $fieldname=>$fieldinfo) { + $selectColumnClause .= sprintf("%s.%s as %s,", $fieldinfo['table'],$fieldinfo['column'],$fieldname); + } + $selectColumnClause = rtrim($selectColumnClause, ','); + + $query = $filterOrAlertInstance->query(); + $query = preg_replace("/SELECT.*FROM(.*)/i", "SELECT $selectColumnClause FROM $1", $query); + + if ($pagingModel !== false) { + $query .= sprintf(" LIMIT %s, %s", $pagingModel->currentCount(), $pagingModel->limit()); + } + + $prequeryResult = $adb->pquery($query, $filterOrAlertInstance->queryParameters()); + return new SqlResultIterator($adb, $prequeryResult); + } + +} diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/Login.php b/oss/vtiger/trunk/modules/Mobile/api/ws/Login.php new file mode 100644 index 00000000..08310316 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/Login.php @@ -0,0 +1,62 @@ +get('username'); + $password = $request->get('password'); + + $current_user = CRMEntity::getInstance('Users'); + $current_user->column_fields['user_name'] = $username; + + if(vtlib_isModuleActive('Mobile') === false) { + $response->setError(1501, 'Service not available'); + return $response; + } + + if(!$current_user->doLogin($password)) { + + $response->setError(1210, 'Authentication Failed'); + + } else { + // Start session now + $sessionid = Mobile_API_Session::init(); + + if($sessionid === false) { + echo "Session init failed $sessionid\n"; + } + + $current_user->id = $current_user->retrieve_user_id($username); + $this->setActiveUser($current_user); + + $result = array(); + $result['login'] = array( + 'userid' => $current_user->id, + 'session'=> $sessionid, + 'vtiger_version' => Mobile_WS_Utils::getVtigerVersion(), + 'mobile_module_version' => Mobile_WS_Utils::getVersion() + ); + $response->setResult($result); + + $this->postProcess($response); + } + return $response; + } + + function postProcess(Mobile_API_Response $response) { + return $response; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/LoginAndFetchModules.php b/oss/vtiger/trunk/modules/Mobile/api/ws/LoginAndFetchModules.php new file mode 100644 index 00000000..47073346 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/LoginAndFetchModules.php @@ -0,0 +1,50 @@ +getActiveUser(); + + if ($current_user) { + $result = $response->getResult(); + $result['modules'] = $this->getListing($current_user); + $response->setResult($result); + } + } + + function getListing($user) { + $modulewsids = Mobile_WS_Utils::getEntityModuleWSIds(); + + // Disallow modules + unset($modulewsids['Users']); + + // Calendar & Events module will be merged + unset($modulewsids['Events']); + + $listresult = vtws_listtypes($user); + + $listing = array(); + foreach($listresult['types'] as $index => $modulename) { + if(!isset($modulewsids[$modulename])) continue; + + $listing[] = array( + 'id' => $modulewsids[$modulename], + 'name' => $modulename, + 'isEntity' => $listresult['information'][$modulename]['isEntity'], + 'label' => $listresult['information'][$modulename]['label'], + 'singular' => $listresult['information'][$modulename]['singular'], + ); + } + + return $listing; + } +} diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/Query.php b/oss/vtiger/trunk/modules/Mobile/api/ws/Query.php new file mode 100644 index 00000000..8c76f120 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/Query.php @@ -0,0 +1,61 @@ +resolveRecordValues($record, $user); + return $record; + } + + function process(Mobile_API_Request $request) { + $current_user = $this->getActiveUser(); + + $query = $request->get('query'); + $nextPage = 0; + $queryResult = false; + + if (preg_match("/(.*) LIMIT[^;]+;/i", $query)) { + $queryResult = vtws_query($query, $current_user); + } else { + // Implicit limit and paging + $query = rtrim($query, ";"); + + $currentPage = intval($request->get('page', 0)); + $FETCH_LIMIT = Mobile::config('API_RECORD_FETCH_LIMIT'); + $startLimit = $currentPage * $FETCH_LIMIT; + + $queryWithLimit = sprintf("%s LIMIT %u,%u;", $query, $startLimit, ($FETCH_LIMIT+1)); + $queryResult = vtws_query($queryWithLimit, $current_user); + + // Determine paging + $hasNextPage = (count($queryResult) > $FETCH_LIMIT); + if ($hasNextPage) { + array_pop($queryResult); // Avoid sending next page record now + $nextPage = $currentPage + 1; + } + } + + $records = array(); + if (!empty($queryResult)) { + foreach($queryResult as $recordValues) { + $records[] = $this->processQueryResultRecord($recordValues, $current_user); + } + } + $result = array('records' => $records, 'nextPage' => $nextPage ); + + $response = new Mobile_API_Response(); + $response->setResult($result); + return $response; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/QueryWithGrouping.php b/oss/vtiger/trunk/modules/Mobile/api/ws/QueryWithGrouping.php new file mode 100644 index 00000000..bdb84c85 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/QueryWithGrouping.php @@ -0,0 +1,35 @@ +cachedDescribeInfo() === false) { + $describeInfo = vtws_describe($this->queryModule, $user); + $this->cacheDescribeInfo($describeInfo); + } + $transformedRecord = $this->transformRecordWithGrouping($record, $this->queryModule); + // Update entity fieldnames + $transformedRecord['labelFields'] = $this->cachedEntityFieldnames($this->queryModule); + return $transformedRecord; + } + + function process(Mobile_API_Request $request) { + $this->queryModule = $request->get('module'); + return parent::process($request); + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/RelatedRecordsWithGrouping.php b/oss/vtiger/trunk/modules/Mobile/api/ws/RelatedRecordsWithGrouping.php new file mode 100644 index 00000000..f187b3ff --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/RelatedRecordsWithGrouping.php @@ -0,0 +1,79 @@ +getActiveUser(); + + $response = new Mobile_API_Response(); + + $record = $request->get('record'); + $relatedmodule = $request->get('relatedmodule'); + $currentPage = $request->get('page', 0); + + // Input validation + if (empty($record)) { + $response->setError(1001, 'Record id is empty'); + return $response; + } + $recordid = vtws_getIdComponents($record); + $recordid = $recordid[1]; + + $module = Mobile_WS_Utils::detectModulenameFromRecordId($record); + + // Initialize global variable + $currentModule = $module; + + $functionHandler = Mobile_WS_Utils::getRelatedFunctionHandler($module, $relatedmodule); + + if ($functionHandler) { + $sourceFocus = CRMEntity::getInstance($module); + $relationResult = call_user_func_array( array($sourceFocus, $functionHandler), array($recordid, getTabid($module), getTabid($relatedmodule)) ); + $query = $relationResult['query']; + + $querySEtype = "vtiger_crmentity.setype as setype"; + if ($relatedmodule == 'Calendar') { + $querySEtype = "vtiger_activity.activitytype as setype"; + } + + $query = sprintf("SELECT vtiger_crmentity.crmid, $querySEtype %s", substr($query, stripos($query, 'FROM'))); + $queryResult = $adb->query($query); + + // Gather resolved record id's + $relatedRecords = array(); + while($row = $adb->fetch_array($queryResult)) { + $targetSEtype = $row['setype']; + if ($relatedmodule == 'Calendar') { + if ($row['setype'] != 'Task' && $row['setype'] != 'Emails') { + $targetSEtype = 'Events'; + } else { + $targetSEtype = $relatedmodule; + } + } + $relatedRecords[] = sprintf("%sx%s", Mobile_WS_Utils::getEntityModuleWSId($targetSEtype), $row['crmid']); + } + + // Perform query to get record information with grouping + $wsquery = sprintf("SELECT * FROM %s WHERE id IN ('%s');", $relatedmodule, implode("','", $relatedRecords)); + $newRequest = new Mobile_API_Request(); + $newRequest->set('module', $relatedmodule); + $newRequest->set('query', $wsquery); + $newRequest->set('page', $currentPage); + + $response = parent::process($newRequest); + } + + return $response; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/SaveRecord.php b/oss/vtiger/trunk/modules/Mobile/api/ws/SaveRecord.php new file mode 100644 index 00000000..414faa3f --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/SaveRecord.php @@ -0,0 +1,84 @@ +recordValues; + } + + function process(Mobile_API_Request $request) { + global $current_user; // Required for vtws_update API + $current_user = $this->getActiveUser(); + + $module = $request->get('module'); + $recordid = $request->get('record'); + $valuesJSONString = $request->get('values'); + + $values = ""; + if(!empty($valuesJSONString) && is_string($valuesJSONString)) { + $values = Zend_Json::decode($valuesJSONString); + } else { + $values = $valuesJSONString; // Either empty or already decoded. + } + + $response = new Mobile_API_Response(); + + if (empty($values)) { + $response->setError(1501, "Values cannot be empty!"); + return $response; + } + + try { + // Retrieve or Initalize + if (!empty($recordid) && !$this->isTemplateRecordRequest($request)) { + $this->recordValues = vtws_retrieve($recordid, $current_user); + } else { + $this->recordValues = array(); + } + + // Set the modified values + foreach($values as $name => $value) { + $this->recordValues[$name] = $value; + } + + // Update or Create + if (isset($this->recordValues['id'])) { + $this->recordValues = vtws_update($this->recordValues, $current_user); + } else { + + // Set right target module name for Calendar/Event record + if ($module == 'Calendar') { + if (!empty($this->recordValues['eventstatus'])) { + $module = 'Events'; + } + } + $this->recordValues = vtws_create($module, $this->recordValues, $current_user); + } + + // Update the record id + $request->set('record', $this->recordValues['id']); + + // Gather response with full details + $response = parent::process($request); + + } catch(Exception $e) { + $response->setError($e->getCode(), $e->getMessage()); + } + return $response; + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/SyncModuleRecords.php b/oss/vtiger/trunk/modules/Mobile/api/ws/SyncModuleRecords.php new file mode 100644 index 00000000..84c7f438 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/SyncModuleRecords.php @@ -0,0 +1,172 @@ +get('mode', $defmode), self::$SYNC_MODE_PRIVATE) === 0); + } + + function process(Mobile_API_Request $request) { + $current_user = $this->getActiveUser(); + $current_user_wsid = sprintf("%sx%s", Mobile_WS_Utils::getEntityModuleWSId("Users"), $current_user->id); + + $module = $request->get('module'); + $lastSyncTime = $request->get('syncToken', 0); + $currentPage = intval($request->get('page', 0)); + $isPrivateMode = $this->isModePrivate($request); + + $FETCH_LIMIT = Mobile::config('API_RECORD_FETCH_LIMIT'); + $startLimit = $currentPage * $FETCH_LIMIT; + + // Keep track of sync-token for futher reference + $maxSyncTime = $lastSyncTime; + + $describeInfo = vtws_describe($module, $current_user); + $this->cacheDescribeInfo($describeInfo); + + $hasAssignedToField = false; + foreach ($describeInfo['fields'] as $fieldinfo) { + if ($fieldinfo['name'] == 'assigned_user_id') { + $hasAssignedToField = true; + break; + } + } + + ///////////////////////////// + // MODIFIED RECORDS TRACKING + ///////////////////////////// + if (empty($lastSyncTime)) { + // No previous state information available? Lookup records recently modified + if ($hasAssignedToField && $isPrivateMode) { + $queryActive = sprintf("SELECT * FROM %s WHERE assigned_user_id = '%s' ORDER BY modifiedtime DESC", $module, $current_user_wsid); + } else { + $queryActive = sprintf("SELECT * FROM %s ORDER BY modifiedtime DESC", $module); + } + + } else { + // Attempt to lookup records from previous state + if ($hasAssignedToField && $isPrivateMode) { + $queryActive = sprintf("SELECT * FROM %s WHERE assigned_user_id = '%s' AND modifiedtime > '%s'", $module, $current_user_wsid, date("Y-m-d H:i:s", $lastSyncTime)); + } else { + $queryActive = sprintf("SELECT * FROM %s WHERE modifiedtime > '%s'", $module, date("Y-m-d H:i:s", $lastSyncTime)); + } + } + + // Try to fetch record with paging (one extra record fetch is attempted to determine presence of next page) + $activeQuery = sprintf("%s LIMIT %u,%u;", $queryActive, $startLimit, ($FETCH_LIMIT+1)); + $activeResult = vtws_query( $activeQuery, $current_user ); + + // Special case handling merge Events records + if ($module == 'Calendar') { + $activeResult2 = vtws_query(str_replace('Calendar', 'Events', $activeQuery), $current_user); + if (!empty($activeResult2)) $activeResult = array_merge($activeResult, $activeResult2); + $FETCH_LIMIT *= 2; + } + + // Determine paging + $hasNextPage = (count($activeResult) > $FETCH_LIMIT); + $nextPage = 0; + if ($hasNextPage) { + array_pop($activeResult); // Avoid sending next page record now + $nextPage = $currentPage + 1; + } + + // Resolved record details + $resolvedModifiedRecords = array(); + $resolvedDeletedRecords = array(); + + if (!empty($activeResult)) { + + foreach($activeResult as $recordValues) { + $this->resolveRecordValues($recordValues, $current_user); + $transformedRecord = $this->transformRecordWithGrouping($recordValues, $module); + // Update entity fieldnames + $transformedRecord['labelFields'] = $this->cachedEntityFieldnames($module); + $resolvedModifiedRecords[] = $transformedRecord; + + $modifiedTimeInSeconds = strtotime($recordValues['modifiedtime']); + if ($maxSyncTime < $modifiedTimeInSeconds) { + $maxSyncTime = $modifiedTimeInSeconds; + } + } + } + + //////////////////////////// + // DELETED RECORDS TRACKING + //////////////////////////// + // Only when there is previous state information and is first page + if (!empty($lastSyncTime) && $currentPage === 0) { + global $adb; + + $queryDeletedParameters = array($module, date('Y-m-d H:i:s', $lastSyncTime)); + $andsmowneridequal = ""; + + if ($hasAssignedToField) { + if ($isPrivateMode) { + $queryDeletedParameters[] = $current_user->id; + $andsmowneridequal = " AND vtiger_crmentity.smownerid=?"; + } else { + $andsmowneridequal = Mobile_WS_Utils::querySecurityFromSuffix($module, $current_user); + } + } + + // Since Calendar and Events are merged + if ($module == 'Calendar') { + $queryDeleted = $adb->pquery("SELECT activityid as crmid, activitytype as setype FROM vtiger_activity + INNER JOIN vtiger_crmentity ON vtiger_activity.activityid=vtiger_crmentity.crmid + AND vtiger_crmentity.deleted=1 AND vtiger_crmentity.setype=? AND vtiger_crmentity.modifiedtime > ? + LEFT JOIN vtiger_users ON vtiger_users.id = vtiger_crmentity.smownerid + LEFT JOIN vtiger_groups ON vtiger_groups.groupid = vtiger_crmentity.smownerid WHERE 1=1 $andsmowneridequal ", + $queryDeletedParameters); + } else { + $queryDeleted = $adb->pquery("SELECT crmid, modifiedtime, setype FROM vtiger_crmentity + LEFT JOIN vtiger_users ON vtiger_users.id = vtiger_crmentity.smownerid + LEFT JOIN vtiger_groups ON vtiger_groups.groupid = vtiger_crmentity.smownerid + WHERE vtiger_crmentity.deleted=1 AND vtiger_crmentity.setype=? AND vtiger_crmentity.modifiedtime > ? $andsmowneridequal", $queryDeletedParameters); + } + + while($row = $adb->fetch_array($queryDeleted)) { + $recordModule = $row['setype']; + if ($module == 'Calendar') { + if ($row['setype'] != 'Task' && $row['setype'] != 'Emails') { + $recordModule = 'Events'; + } else { + $recordModule = $module; + } + } + + $resolvedDeletedRecords[] = sprintf("%sx%s", Mobile_WS_Utils::getEntityModuleWSId($recordModule), $row['crmid']); + $modifiedTimeInSeconds = strtotime($row['modifiedtime']); + if ($maxSyncTime < $modifiedTimeInSeconds) { + $maxSyncTime = $modifiedTimeInSeconds; + } + } + } + + $result = array( + 'nextSyncToken' => $maxSyncTime, + 'deleted' => $resolvedDeletedRecords, + 'updated' => $resolvedModifiedRecords, + 'nextPage'=> $nextPage, // Applies only to retrieve updated record details + ); + + $response = new Mobile_API_Response(); + $response->setResult( array( 'sync' => $result) ); + + return $response; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/Utils.php b/oss/vtiger/trunk/modules/Mobile/api/ws/Utils.php new file mode 100644 index 00000000..229f9fc0 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/Utils.php @@ -0,0 +1,305 @@ +pquery("SELECT version FROM vtiger_tab WHERE name='Mobile'", array()); + return $adb->query_result($versionResult, 0, 'version'); + } + + static function array_replace($search, $replace, $array) { + $index = array_search($search, $array); + if($index !== false) { + $array[$index] = $replace; + } + return $array; + } + + static function getModuleListQuery($moduleName, $where = '1=1') { + $module = CRMEntity::getInstance($moduleName); + return $module->create_list_query('', $where); + } + + static $moduleWSIdCache = array(); + + static function getEntityModuleWSId($moduleName) { + + if (!isset(self::$moduleWSIdCache[$moduleName])) { + global $adb; + $result = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", array($moduleName)); + if ($result && $adb->num_rows($result)) { + self::$moduleWSIdCache[$moduleName] = $adb->query_result($result, 0, 'id'); + } + } + return self::$moduleWSIdCache[$moduleName]; + } + + static function getEntityModuleWSIds($ignoreNonModule = true) { + global $adb; + + $modulewsids = array(); + $result = false; + if($ignoreNonModule) { + $result = $adb->pquery("SELECT id, name FROM vtiger_ws_entity WHERE ismodule=1", array()); + } else { + $result = $adb->pquery("SELECT id, name FROM vtiger_ws_entity", array()); + } + + while($resultrow = $adb->fetch_array($result)) { + $modulewsids[$resultrow['name']] = $resultrow['id']; + } + return $modulewsids; + } + + static function getEntityFieldnames($module) { + global $adb; + $result = $adb->pquery("SELECT fieldname FROM vtiger_entityname WHERE modulename=?", array($module)); + $fieldnames = array(); + if($result && $adb->num_rows($result)) { + $fieldnames = explode(',', $adb->query_result($result, 0, 'fieldname')); + } + switch($module) { + case 'HelpDesk': $fieldnames = self::array_replace('title', 'ticket_title', $fieldnames); break; + case 'Document': $fieldnames = self::array_replace('title', 'notes_title', $fieldnames); break; + } + return $fieldnames; + } + + static function getModuleColumnTableByFieldNames($module, $fieldnames) { + global $adb; + $result = $adb->pquery("SELECT fieldname,columnname,tablename FROM vtiger_field WHERE tabid=? AND fieldname IN (". + generateQuestionMarks($fieldnames) . ")", array(getTabid($module), $fieldnames) + ); + $columnnames = array(); + if ($result && $adb->num_rows($result)) { + while($resultrow = $adb->fetch_array($result)) { + $columnnames[$resultrow['fieldname']] = array('column' => $resultrow['columnname'], 'table' => $resultrow['tablename']); + } + } + return $columnnames; + } + + static function detectModulenameFromRecordId($wsrecordid) { + global $adb; + $idComponents = vtws_getIdComponents($wsrecordid); + $result = $adb->pquery("SELECT name FROM vtiger_ws_entity WHERE id=?", array($idComponents[0])); + if($result && $adb->num_rows($result)) { + return $adb->query_result($result, 0, 'name'); + } + return false; + } + + static $detectFieldnamesToResolveCache = array(); + + static function detectFieldnamesToResolve($module) { + global $adb; + + // Cache hit? + if(isset(self::$detectFieldnamesToResolveCache[$module])) { + return self::$detectFieldnamesToResolveCache[$module]; + } + + $resolveUITypes = array(10, 101, 116, 117, 26, 357, 50, 51, 52, 53, 57, 58, 59, 66, 68, 73, 75, 76, 77, 78, 80, 81); + + $result = $adb->pquery( + "SELECT fieldname FROM vtiger_field WHERE uitype IN(". + generateQuestionMarks($resolveUITypes) .") AND tabid=?", array($resolveUITypes, getTabid($module)) + ); + $fieldnames = array(); + while($resultrow = $adb->fetch_array($result)) { + $fieldnames[] = $resultrow['fieldname']; + } + + // Cache information + self::$detectFieldnamesToResolveCache[$module] = $fieldnames; + + return $fieldnames; + } + + static $gatherModuleFieldGroupInfoCache = array(); + + static function gatherModuleFieldGroupInfo($module) { + global $adb; + + if($module == 'Events') $module = 'Calendar'; + + // Cache hit? + if(isset(self::$gatherModuleFieldGroupInfoCache[$module])) { + return self::$gatherModuleFieldGroupInfoCache[$module]; + } + + $result = $adb->pquery( + "SELECT fieldname, fieldlabel, blocklabel, uitype FROM vtiger_field INNER JOIN + vtiger_blocks ON vtiger_blocks.tabid=vtiger_field.tabid AND vtiger_blocks.blockid=vtiger_field.block + WHERE vtiger_field.tabid=? AND vtiger_field.presence != 1 ORDER BY vtiger_blocks.sequence, vtiger_field.sequence", array(getTabid($module)) + ); + + $fieldgroups = array(); + while($resultrow = $adb->fetch_array($result)) { + $blocklabel = getTranslatedString($resultrow['blocklabel'], $module); + if(!isset($fieldgroups[$blocklabel])) { + $fieldgroups[$blocklabel] = array(); + } + $fieldgroups[$blocklabel][$resultrow['fieldname']] = + array( + 'label' => getTranslatedString($resultrow['fieldlabel'], $module), + 'uitype'=> self::fixUIType($module, $resultrow['fieldname'], $resultrow['uitype']) + ); + } + + // Cache information + self::$gatherModuleFieldGroupInfoCache[$module] = $fieldgroups; + + return $fieldgroups; + } + + static function documentFoldersInfo() { + global $adb; + $folders = $adb->pquery("SELECT folderid, foldername FROM vtiger_attachmentsfolder", array()); + $folderOptions = array(); + while( $folderrow = $adb->fetch_array($folders) ) { + $folderwsid = sprintf("%sx%s", self::getEntityModuleWSId('DocumentFolders'), $folderrow['folderid']); + $folderOptions[] = array( 'value' => $folderwsid, 'label' => $folderrow['foldername'] ); + } + return $folderOptions; + } + + static function salutationValues() { + $values = vtlib_getPicklistValues('salutationtype'); + $options = array(); + foreach($values as $value) { + $options[] = array( 'value' => $value, 'label' => $value); + } + return $options; + } + + static function fixUIType($module, $fieldname, $uitype) { + if ($module == 'Contacts' || $module == 'Leads') { + if ($fieldname == 'salutationtype') { + return 16; + } + } + else if ($module == 'Calendar' || $module == 'Events') { + if ($fieldname == 'time_start' || $fieldname == 'time_end') { + // Special type for mandatory time type (not defined in product) + return 252; + } + } + return $uitype; + } + + static function fixDescribeFieldInfo($module, &$describeInfo) { + + if ($module == 'Leads' || $module == 'Contacts') { + foreach($describeInfo['fields'] as $index => $fieldInfo) { + if ($fieldInfo['name'] == 'salutationtype') { + $picklistValues = self::salutationValues(); + $fieldInfo['uitype'] = self::fixUIType($module, $fieldInfo['name'], $fieldInfo['uitype']) ; + $fieldInfo['type']['name'] = 'picklist'; + $fieldInfo['type']['picklistValues'] = $picklistValues; + //$fieldInfo['type']['defaultValue'] = $picklistValues[0]; + + $describeInfo['fields'][$index] = $fieldInfo; + } + } + } + else if ($module == 'Documents') { + foreach($describeInfo['fields'] as $index => $fieldInfo) { + if ($fieldInfo['name'] == 'folderid') { + $picklistValues = self::documentFoldersInfo(); + $fieldInfo['type']['picklistValues'] = $picklistValues; + //$fieldInfo['type']['defaultValue'] = $picklistValues[0]; + + $describeInfo['fields'][$index] = $fieldInfo; + } + } + } + else if($module == 'Calendar' || $module == 'Events') { + foreach($describeInfo['fields'] as $index => $fieldInfo) { + $fieldInfo['uitype'] = self::fixUIType($module, $fieldInfo['name'], $fieldInfo['uitype']); + $describeInfo['fields'][$index] = $fieldInfo; + } + } + } + + static function getRelatedFunctionHandler($sourceModule, $targetModule) { + global $adb; + $relationResult = $adb->pquery("SELECT name FROM vtiger_relatedlists WHERE tabid=? and related_tabid=? and presence=0", array(getTabid($sourceModule), getTabid($targetModule))); + $functionName = false; + if ($adb->num_rows($relationResult)) $functionName = $adb->query_result($relationResult, 0, 'name'); + return $functionName; + } + + /** + * Security restriction (sharing privilege) query part + */ + static function querySecurityFromSuffix($module, $current_user) { + require('user_privileges/user_privileges_'.$current_user->id.'.php'); + require('user_privileges/sharing_privileges_'.$current_user->id.'.php'); + + $querySuffix = ''; + $tabid = getTabid($module); + + if($is_admin==false && $profileGlobalPermission[1] == 1 && $profileGlobalPermission[2] == 1 + && $defaultOrgSharingPermission[$tabid] == 3) { + + $querySuffix .= " AND (vtiger_crmentity.smownerid in($current_user->id) OR vtiger_crmentity.smownerid IN + ( + SELECT vtiger_user2role.userid FROM vtiger_user2role + INNER JOIN vtiger_users ON vtiger_users.id=vtiger_user2role.userid + INNER JOIN vtiger_role ON vtiger_role.roleid=vtiger_user2role.roleid + WHERE vtiger_role.parentrole LIKE '".$current_user_parent_role_seq."::%' + ) + OR vtiger_crmentity.smownerid IN + ( + SELECT shareduserid FROM vtiger_tmp_read_user_sharing_per + WHERE userid=".$current_user->id." AND tabid=".$tabid." + ) + OR + ("; + + // Build the query based on the group association of current user. + if(sizeof($current_user_groups) > 0) { + $querySuffix .= " vtiger_groups.groupid IN (". implode(",", $current_user_groups) .") OR "; + } + $querySuffix .= " vtiger_groups.groupid IN + ( + SELECT vtiger_tmp_read_group_sharing_per.sharedgroupid + FROM vtiger_tmp_read_group_sharing_per + WHERE userid=".$current_user->id." and tabid=".$tabid." + )"; + $querySuffix .= ") + )"; + } + return $querySuffix; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/Alert.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/Alert.php new file mode 100644 index 00000000..b5edf674 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/Alert.php @@ -0,0 +1,100 @@ +recordsLinked = true; + } + + function setUser($userInstance) { + $this->user = $userInstance; + } + + function getUser() { + return $this->user; + } + + function serializeToSend() { + $category = $this->moduleName; + if (empty($category)) { + $category = "General"; + } + return array( + 'alertid' => (string)$this->alertid, + 'name' => $this->name, + 'category' => $category, + 'refreshRate'=> $this->refreshRate, + 'description'=> $this->description, + 'recordsLinked'=> $this->recordsLinked + ); + } + + abstract function query(); + abstract function queryParameters(); + + function message() { + return (string) $this->executeCount(); + } + + /*function execute() { + global $adb; + $result = $adb->pquery($this->query(), $this->queryParameters()); + return $result; + }*/ + + function executeCount() { + global $adb; + $result = $adb->pquery($this->countQuery(), $this->queryParameters()); + return $adb->query_result($result, 0, 'count'); + } + + // Function provided to enable sub-classes to over-ride in case required + protected function countQuery() { + return mkCountQuery($this->query()); + } + + static function models() { + global $adb; + + $models = array(); + $handlerResult = $adb->pquery("SELECT * FROM vtiger_mobile_alerts WHERE deleted = 0", array()); + if ($adb->num_rows($handlerResult)) { + while ($handlerRow = $adb->fetch_array($handlerResult)) { + $handlerPath = $handlerRow['handler_path']; + if (file_exists($handlerPath)) { + checkFileAccess($handlerPath); + include_once $handlerPath; + $alertModel = new $handlerRow['handler_class']; + $alertModel->alertid = $handlerRow['id']; + $models[] = $alertModel; + } + } + } + return $models; + } + + static function modelWithId($alertid) { + $models = self::models(); + foreach($models as $model) { + if ($model->alertid == $alertid) return $model; + } + return false; + } +} + diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/Filter.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/Filter.php new file mode 100644 index 00000000..7fc92f0c --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/Filter.php @@ -0,0 +1,47 @@ +moduleName = $moduleName; + $this->customView = new CustomView($moduleName); + } + + function setUser($userInstance) { + $this->user = $userInstance; + } + + function getUser() { + return $this->user; + } + + function query() { + $listquery = getListQuery($this->moduleName); + $query = $this->customView->getModifiedCvListQuery($this->filterid,$listquery,$this->moduleName); + return $query; + } + + function queryParameters() { + return false; + } + + static function modelWithId($moduleName, $filterid) { + $model = new Mobile_WS_FilterModel($moduleName); + $model->filterid = $filterid; + return $model; + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/Paging.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/Paging.php new file mode 100644 index 00000000..3b51f414 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/Paging.php @@ -0,0 +1,71 @@ +_limit = Mobile::config('API_RECORD_FETCH_LIMIT', 20); + } + + function start() { + return $this->_start; + } + + function limit() { + return $this->_limit; + } + + function currentCount() { + return ($this->current() * $this->limit()); + } + + function current() { + return $this->_page; + } + + function next() { + return ($this->current()+1); + } + + function previous() { + return ($this->current() < 1? 0 : ($this->current()-1)); + } + + function hasNext($countOnPage) { + return ($countOnPage >= $this->limit()); + } + + function hasPrevious() { + return ($this->start() != 0); + } + + function initStart($page) { + + if(empty($page)) $page = 0; + $this->_page = $page; + + if($page < 1) $this->_start = 0; + else $this->_start = ($page * $this->_limit); + } + + function setLimit($limit) { + $this->_limit = $limit; + } + + static function modelWithPageStart($start) { + $instance = new self(); + $instance->initStart($start); + return $instance; + } +} +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/SearchFilter.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/SearchFilter.php new file mode 100644 index 00000000..44341b17 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/SearchFilter.php @@ -0,0 +1,58 @@ +moduleName = $moduleName; + } + + function query() { + return false; + } + + function queryParameters() { + return false; + } + + function setCriterias($criterias) { + $this->criterias = $criterias; + } + + function execute($fieldnames, $pagingModel = false) { + + $selectClause = sprintf("SELECT %s", implode(',', $fieldnames)); + $fromClause = sprintf("FROM %s", $this->moduleName); + $whereClause = ""; + $orderClause = ""; + $groupClause = ""; + $limitClause = $pagingModel? " LIMIT {$pagingModel->currentCount()},{$pagingModel->limit()}" : "" ; + + if (!empty($this->criterias)) { + $_sortCriteria = $this->criterias['_sort']; + if(!empty($_sortCriteria)) { + $orderClause = $_sortCriteria; + } + } + + $query = sprintf("%s %s %s %s %s %s;", $selectClause, $fromClause, $whereClause, $orderClause, $groupClause, $limitClause); + return vtws_query($query, $this->getUser()); + } + + static function modelWithCriterias($moduleName, $criterias = false) { + $model = new Mobile_WS_SearchFilterModel($moduleName); + $model->setCriterias($criterias); + return $model; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/EventsOfMineToday.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/EventsOfMineToday.php new file mode 100644 index 00000000..146fee0c --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/EventsOfMineToday.php @@ -0,0 +1,35 @@ +name = 'Your events for the day'; + $this->moduleName = 'Calendar'; + $this->refreshRate= 1 * (24* 60 * 60); // 1 day + $this->description='Alert sent when events are scheduled for the day'; + } + + function query() { + $today = date('Y-m-d'); + $sql = "SELECT crmid, activitytype FROM vtiger_activity INNER JOIN + vtiger_crmentity ON vtiger_crmentity.crmid=vtiger_activity.activityid + WHERE vtiger_crmentity.deleted=0 AND vtiger_crmentity.smownerid=? AND + vtiger_activity.activitytype <> 'Emails' AND + (vtiger_activity.date_start = '{$today}' OR vtiger_activity.due_date = '{$today}')"; + return $sql; + } + + function queryParameters() { + return array($this->getUser()->id); + } +} diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/IdleTicketsOfMine.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/IdleTicketsOfMine.php new file mode 100644 index 00000000..d59399ec --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/IdleTicketsOfMine.php @@ -0,0 +1,27 @@ +name = 'Idle Ticket Alert'; + $this->moduleName = 'HelpDesk'; + $this->refreshRate= 1 * (60 * 60); // 1 hour + $this->description='Alert sent when ticket has not been updated in 24 hours'; + } + + function query() { + $sql = parent::query(); + $sql .= " AND DATEDIFF(CURDATE(), vtiger_crmentity.modifiedtime) > 1"; + return $sql; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/NewTicketOfMine.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/NewTicketOfMine.php new file mode 100644 index 00000000..51815205 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/NewTicketOfMine.php @@ -0,0 +1,37 @@ +name = 'New Ticket Alert'; + $this->moduleName = 'HelpDesk'; + $this->refreshRate= 1 * (60 * 60); // 1 hour + $this->description='Alert sent when a ticket is assigned to you'; + } + + function query() { + $sql = parent::query(); + $sql .= " ORDER BY crmid DESC LIMIT 1"; + return $sql; + } + + function countQuery() { + return str_replace("ORDER BY crmid DESC", "", $this->query()); + } + + function executeCount() { + global $adb; + $result = $adb->pquery($this->countQuery(), $this->queryParameters()); + return $adb->num_rows($result); + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/PendingTicketsOfMine.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/PendingTicketsOfMine.php new file mode 100644 index 00000000..0b15dda6 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/PendingTicketsOfMine.php @@ -0,0 +1,33 @@ +name = 'Pending Ticket Alert'; + $this->moduleName = 'HelpDesk'; + $this->refreshRate= 1 * (24* 60 * 60); // 1 day + $this->description='Alert sent when ticket assigned is not yet closed'; + } + + function query() { + $sql = "SELECT crmid FROM vtiger_troubletickets INNER JOIN + vtiger_crmentity ON vtiger_crmentity.crmid=vtiger_troubletickets.ticketid + WHERE vtiger_crmentity.deleted=0 AND vtiger_crmentity.smownerid=? AND + vtiger_troubletickets.status <> 'Closed'"; + return $sql; + } + + function queryParameters() { + return array($this->getUser()->id); + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/PotentialsDueIn5Days.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/PotentialsDueIn5Days.php new file mode 100644 index 00000000..c26ad851 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/PotentialsDueIn5Days.php @@ -0,0 +1,33 @@ +name = 'Upcoming Opportunity'; + $this->moduleName = 'Potentials'; + $this->refreshRate= 1 * (24 * 60 * 60); // 1 day + $this->description='Alert sent when Potential Close Date is due before 5 days or less'; + } + + function query() { + $sql = Mobile_WS_Utils::getModuleListQuery('Potentials', + "vtiger_potential.sales_stage not like 'Closed%' AND + DATEDIFF(vtiger_potential.closingdate, CURDATE()) <= 5" + ); + return preg_replace("/^SELECT count\(\*\) as count(.*)/i", "SELECT crmid $1", mkCountQuery($sql)); + } + + function queryParameters() { + return array(); + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/ServerTimeSampleAlert.php b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/ServerTimeSampleAlert.php new file mode 100644 index 00000000..99211d91 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/models/alerts/ServerTimeSampleAlert.php @@ -0,0 +1,38 @@ +name = 'Server Time Alert'; + $this->description='Alert to get server time information'; + $this->refreshRate= 1; // 1 second + $this->recordsLinked = FALSE; + // There is no module records linked with message. + // If set to true $this->moduleName needs to be set. + } + + function message() { + return date('Y-m-d H:i:s'); + } + + /** Override base class methods */ + function query() { + return false; + } + + function queryParameters() { + return false; + } +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/api/wsapi.php b/oss/vtiger/trunk/modules/Mobile/api/wsapi.php new file mode 100644 index 00000000..cf656205 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/wsapi.php @@ -0,0 +1,127 @@ +set('alertid', $alertid); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_AlertDetailsWithMessage', 'ws/AlertDetailsWithMessage.php'); +} + +function mobile_ws_fetchModuleFilters($module, $user) { + $request = new Mobile_API_Request(); + $request->set('module', $module); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_FetchModuleFilters', 'ws/FetchModuleFilters.php'); +} + +function mobile_ws_fetchRecord($record, $user) { + $request = new Mobile_API_Request(); + $request->set('record', $record); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_FetchRecord', 'ws/FetchRecord.php'); +} + +function mobile_ws_fetchRecordWithGrouping($record, $user) { + $request = new Mobile_API_Request(); + $request->set('record', $record); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_FetchRecordWithGrouping', 'ws/FetchRecordWithGrouping.php'); +} + +function mobile_ws_filterDetailsWithCount($filterid, $user) { + $request = new Mobile_API_Request(); + $request->set('filterid', $filterid); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_FilterDetailsWithCount', 'ws/FilterDetailsWithCount.php'); +} + +function mobile_ws_listModuleRecords($elements, $user) { + $request = new Mobile_API_Request($elements); // elements can have key (module, alertid, filterid, search, page) + return Mobile_WS_API::process($request, $user, 'Mobile_WS_ListModuleRecords', 'ws/ListModuleRecords.php'); +} + +function mobile_ws_saveRecord($module, $record, $values, $user) { + $request = new Mobile_API_Request(); + $request->set('module', $module); + $request->set('record', $record); + $request->set('values', $values); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_SaveRecord', 'ws/SaveRecord.php'); +} + +function mobile_ws_syncModuleRecords($module, $syncToken, $page, $user) { + $request = new Mobile_API_Request(); + $request->set('module', $module); + $request->set('syncToken', $syncToken); + $request->set('page', $page); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_SyncModuleRecords', 'ws/SyncModuleRecords.php'); +} + +function mobile_ws_query($module, $query, $page, $user) { + $request = new Mobile_API_Request(); + $request->set('module', $module); + $request->set('query', $query); + $request->set('page', $page); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_Query', 'ws/Query.php'); +} + +function mobile_ws_queryWithGrouping($module, $query, $page, $user) { + $request = new Mobile_API_Request(); + $request->set('module', $module); + $request->set('query', $query); + $request->set('page', $page); + return Mobile_WS_API::process($request, $user, 'Mobile_WS_QueryWithGrouping', 'ws/QueryWithGrouping.php'); +} +/**********************************************************************/ + +/** + * Mobile WS API Controller + */ +include_once dirname(__FILE__) . '/Request.php'; +include_once dirname(__FILE__) . '/Response.php'; +include_once dirname(__FILE__) . '/Session.php'; +include_once dirname(__FILE__) . '/ws/Controller.php'; + +class Mobile_WS_API { + private $controller; + + function initController($className, $handlerPath, $user) { + include_once dirname(__FILE__) . "/$handlerPath"; + + $this->controller = new $className(); + Mobile_API_Session::init(session_id()); + $this->controller->initActiveUser($user); + return $this->controller; + } + + function getController() { + return $this->controller; + } + + static function process(Mobile_API_Request $request, $user, $className, $handlerPath) { + + if(vtlib_isModuleActive('Mobile') === false) { + throw new WebServiceException('1501', 'Service not available'); + } + + $wsapiController = new self(); + $response = $wsapiController->initController($className, $handlerPath, $user)->process($request); + if($response->hasError()) { + $error = $response->getError(); + throw new WebServiceException($error['code'], $error['message']); + } + return $response->getResult(); + } + +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/ical.php b/oss/vtiger/trunk/modules/Mobile/ical.php new file mode 100644 index 00000000..ff193797 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/ical.php @@ -0,0 +1,214 @@ +db = PearDatabase::getInstance(); + } + + /** + * Authenticate user + * + * @param String $username + * @param String $password + * @return True if authenticated, false otherwise + */ + function authenticate($username, $password) { + + $this->userfocus = CRMEntity::getInstance('Users'); + $this->userfocus->column_fields['user_name'] = $username; + $authsuccess = $this->userfocus->doLogin($password); + + if($authsuccess) { + if(!isset($this->userfocus->id)) { + $this->userfocus->id = $this->userfocus->retrieve_user_id($username); + } + } + + return $authsuccess; + } + + /** + * Prepare date to useable icalendar format + * + * @param String $date yyyy-mm-dd format + * @return yyyymmdd + */ + function formatDate($date) { + if(empty($date)) $date = date('Y-m-d'); + return str_replace('-', '', $date); + } + + /** + * Prepare date-time to useable icalendar format + * + * @param String $date yyyy-mm-dd format + * @param String $time hh:ii:ss format + * @return yyyymmddThhiissZ + */ + function formatDateTime($date, $time) { + if(empty($date) || preg_match("/0000-00-00/", $date)) { + $date = date('Y-m-d'); + } + + if(empty($time)) $time = "00:00:00"; + + // Hous not padded? + if(preg_match("/([0-9]):([0-9][0-9])$/", $time, $m)) { + $time = sprintf("0%s:%s", $m[1], $m[2]); + } + // Minutes not padded? + if(preg_match("/([0-9][0-9]):([0-9])$/", $time, $m)) { + $time = sprintf("%s:0%s", $m[1], $m[2]); + } + if(strlen($time) == 5) $time = "{$time}:00"; + + return sprintf("%sT%sZ", $this->formatDate($date), str_replace(':','',$time)); + } + + /** + * Prepare date-timestamp to useable icalendar format + * + * @param String $value yyyy-mm-dd hh:ii:ss format + * @return yyyymmddThhiissZ + */ + function formatDateTimestamp($value) { + return str_replace(array('-', ':', ' '), array('','','T'), trim($value)) . 'Z'; + } + + /** + * Format value based on its current state. + * + * @param String $value + * @param String $defvalue + * @return unknown|unknown + */ + function formatValue($value, $defvalue='') { + if(is_null($value) || empty($value)) return $defvalue; + return $value; + } + + /** + * Generate icalendar data output. + * + * @return String + */ + function generate() { + + $properties = array(); + $properties['prodid'] = '-//vtiger/Mobile/NONSGML 1.0//EN'; + + $ical = new qCal($properties); + + // TODO Configure timezone information. + + $fieldnames = array( + 'activityid', 'subject', 'description', 'activitytype', 'location', 'reminder_time', + 'date_start', 'time_start', 'due_date', 'time_end', 'modifiedtime' + ); + + $query = "SELECT " . implode(',', $fieldnames) . " FROM vtiger_activity + INNER JOIN vtiger_crmentity ON + (vtiger_activity.activityid=vtiger_crmentity.crmid AND vtiger_crmentity.deleted = 0 AND vtiger_crmentity.smownerid = ?) + LEFT JOIN vtiger_activity_reminder ON vtiger_activity_reminder.activity_id=vtiger_activity.activityid + WHERE vtiger_activity.activitytype != 'Emails'"; + + $result = $this->db->pquery($query, array($this->userfocus->id)); + + while($resultrow = $this->db->fetch_array($result)) { + + $properties = array(); + $properties['uid'] = $resultrow['activityid']; + $properties['summary'] = $this->formatValue(decode_html($resultrow['subject'])); + $properties['description'] = $this->formatValue(decode_html($resultrow['description'])); + $properties['class'] = 'PRIVATE'; + $properties['dtstart'] = $this->formatDateTime( $resultrow['date_start'], $resultrow['time_start']); + $properties['dtend'] = $this->formatDateTime( $resultrow['due_date'], $resultrow['time_end']); + $properties['dtstamp'] = $this->formatDateTimestamp($resultrow['modifiedtime']); + $properties['location'] = $this->formatValue($resultrow['location']); + + if($resultrow['activitytype'] == 'Task') { + // Tranform the parameter + $properties['due'] = $properties['dtend']; + unset($properties['dtend']); + + $icalComponent = new qCal_Component_Vtodo($properties); + } else { + + $icalComponent = new qCal_Component_Vevent($properties); + + if(!empty($resultrow['reminder_time'])) { + $alarmProperties = array(); + $alarmProperties['trigger'] = $resultrow['reminder_time'] * 60; + $icalComponent->attach(new qCal_Component_Valarm($alarmProperties)); + } + } + + $ical->attach($icalComponent); + } + + return $ical->render(); + } + + /** + * Helper method to process the request and emit output + * + * @param String $username + * @param String $password + */ + static function process($username, $password) { + $mobileical = new Mobile_ICAL(); + + if(!$mobileical->authenticate($username, $password)) { + header('Content-type: text/plain'); + echo "FAILED"; + } else { + $icalContent = $mobileical->generate(); + header('Content-Disposition: attachment; filename="icalendar.ics"'); + header('Content-Length: '. strlen($icalContent)); + echo $icalContent; + } + } +} + +// To make it easier for subscribing to Calendar via applications we support the +// url format: http://localhost:81/modules/Mobile/ical.php/username@password + +// Retrieve username and password from the URL +$pathinfo = $_SERVER['PATH_INFO']; +if(empty($pathinfo)) $pathinfo = "/ @ "; +preg_match("/\/([^@]+)@(.*)/", $pathinfo, $matches); + +// Process the request +if (vtlib_isModuleActive('Mobile')) { + Mobile_ICAL::process($matches[1], $matches[2]); +} diff --git a/oss/vtiger/trunk/modules/Mobile/index.php b/oss/vtiger/trunk/modules/Mobile/index.php new file mode 100644 index 00000000..769e1eba --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/index.php @@ -0,0 +1,108 @@ + array('file' => '/ui/Logout.php', 'class' => 'Mobile_UI_Logout'), + 'login' => array('file' => '/ui/Login.php', 'class' => 'Mobile_UI_Login'), + 'loginAndFetchModules' => array('file' => '/ui/LoginAndFetchModules.php', 'class' => 'Mobile_UI_LoginAndFetchModules'), + 'listModuleRecords' => array('file' => '/ui/ListModuleRecords.php', 'class' => 'Mobile_UI_ListModuleRecords'), + 'fetchRecordWithGrouping' => array('file' => '/ui/FetchRecordWithGrouping.php', 'class' => 'Mobile_UI_FetchRecordWithGrouping'), + + 'searchConfig' => array('file' => '/ui/SearchConfig.php', 'class' => 'Mobile_UI_SearchConfig' ) + ); + + static function process(Mobile_API_Request $request) { + $operation = $request->getOperation(); + $sessionid = HTTP_Session::detectId(); //$request->getSession(); + + if (empty($operation)) $operation = 'login'; + + $response = false; + if(isset(self::$opControllers[$operation])) { + $operationFile = self::$opControllers[$operation]['file']; + $operationClass= self::$opControllers[$operation]['class']; + + include_once dirname(__FILE__) . $operationFile; + $operationController = new $operationClass; + + $operationSession = false; + if($operationController->requireLogin()) { + $operationSession = Mobile_API_Session::init($sessionid); + if($operationController->hasActiveUser() === false) { + $operationSession = false; + } + //Mobile_WS_Utils::initAppGlobals(); + } else { + // By-pass login + $operationSession = true; + } + + if($operationSession === false) { + $response = new Mobile_API_Response(); + $response->setError(1501, 'Login required'); + } else { + + try { + $response = $operationController->process($request); + } catch(Exception $e) { + $response = new Mobile_API_Response(); + $response->setError($e->getCode(), $e->getMessage()); + } + } + + } else { + $response = new Mobile_API_Response(); + $response->setError(1404, 'Operation not found: ' . $operation); + } + + if($response !== false) { + + if ($response->hasError()) { + include_once dirname(__FILE__) . '/ui/Error.php'; + $errorController = new Mobile_UI_Error(); + $errorController->setError($response->getError()); + echo $errorController->process($request)->emitHTML(); + } else { + echo $response->emitHTML(); + } + } + } +} + +/** Take care of stripping the slashes */ +function stripslashes_recursive($value) { + $value = is_array($value) ? array_map('stripslashes_recursive', $value) : stripslashes($value); + return $value; +} +if (get_magic_quotes_gpc()) { + //$_GET = stripslashes_recursive($_GET ); + //$_POST = stripslashes_recursive($_POST ); + $_REQUEST = stripslashes_recursive($_REQUEST); +} +/** END **/ + +if(!defined('MOBILE_INDEX_CONTROLLER_AVOID_TRIGGER')) { + Mobile_Index_Controller::process(new Mobile_API_Request($_REQUEST)); +} \ No newline at end of file diff --git a/oss/vtiger/trunk/modules/Mobile/language/en_us.lang.php b/oss/vtiger/trunk/modules/Mobile/language/en_us.lang.php new file mode 100644 index 00000000..ad5031df --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/language/en_us.lang.php @@ -0,0 +1,15 @@ + 'Mobile', + +); + +?> diff --git a/oss/vtiger/trunk/modules/Mobile/language/zh_cn.lang.php b/oss/vtiger/trunk/modules/Mobile/language/zh_cn.lang.php new file mode 100644 index 00000000..257610e9 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/language/zh_cn.lang.php @@ -0,0 +1,33 @@ + 'Mobile', + +); + + + + + + +?> diff --git a/oss/vtiger/trunk/modules/Mobile/schema.xml b/oss/vtiger/trunk/modules/Mobile/schema.xml new file mode 100644 index 00000000..519501f8 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/schema.xml @@ -0,0 +1,16 @@ + + + + + vtiger_mobile_alerts + +
+
+