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..7581c4f1 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/FetchRecordsWithGrouping.php @@ -0,0 +1,91 @@ +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()); + + $fetchIds = array(); + while($resultrow = $adb->fetch_array($alertResult)) { + $fetchIds[] = "{$moduleWSID}x" . $resultrow['crmid']; + } + return $this->fetchRecordsWithId($module, $fetchIds, $alert->getUser()); + } + +} 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..a440897d --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/ws/SaveRecord.php @@ -0,0 +1,82 @@ +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)) { + $values = Zend_Json::decode($valuesJSONString); + } + + $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/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/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..129b2124 --- /dev/null +++ b/oss/vtiger/trunk/modules/Mobile/api/wsapi.php @@ -0,0 +1,126 @@ +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($values); + $request->set('module', $module); + $request->set('record', $record); + 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