├── README.md
├── doc
└── images
│ └── vtiger-user-access-key.png
├── examples
└── php
│ ├── RestfulApi.php
│ ├── RestfulApiCalls.php
│ └── index.php
└── src
├── languages
├── en_us
│ └── RestfulApi.php
└── fr_fr
│ └── RestfulApi.php
├── manifest.xml
└── modules
└── RestfulApi
├── .htaccess
├── RestfulApi.php
├── actions
├── Api.php
└── Auth.php
├── index.php
├── models
└── Rest.php
└── schema.xml
/README.md:
--------------------------------------------------------------------------------
1 | # VTRestfulAPI
2 |
3 | RestfulApi is a Vtiger module which allows to communicate with your CRM using a REST API.
4 |
5 | ## Installation
6 |
7 | Simply install RestfulApi like a classic Vtiger module.
8 |
9 | __CRM Settings > Modules > Install from zip__
10 |
11 | ## Use
12 |
13 | You can easily communicate with your CRM using the REST protocol.
14 |
15 | **GET:** Authentificate an user / Retrieve an entity or a list of entities / Retrieve PickList values
16 |
17 | **POST:** Add an entity
18 |
19 | **PUT:** Update an entity
20 |
21 | **DELETE:** Remove an entity
22 |
23 | ### Authentification
24 |
25 | To communicate with your CRM you must be authentificated.
26 |
27 | There are 2 solutions to login an user:
28 |
29 | 1. __With login and password__
30 |
31 | ```
32 | http://yourcrm.com/modules/RestfulApi/auth/LOGIN/PASSWORD
33 |
34 | or
35 |
36 | http://yourcrm.com/modules/RestfulApi/index.php?module=Auth&login=LOGIN&password=PASSWORD
37 | ```
38 |
39 | *Replace LOGIN and PASSWORD by your credentials.*
40 |
41 |
42 | 2. __With access Key__
43 |
44 | ```
45 | http://yourcrm.com/modules/RestfulApi/auth/USER_ACCESS_KEY
46 |
47 | or
48 |
49 | http://yourcrm.com/modules/RestfulApi/index.php?module=Auth&key=USER_ACCESS_KEY
50 | ```
51 |
52 | *Replace USER_ACCESS_KEY by the user's access key auto-generated by Vtiger.*
53 |
54 | 
55 |
56 |
57 | **Result:**
58 |
59 | ```
60 | {"success":true,"result":"f7171f3d8e13326762a45641c2dd1e39"}
61 |
62 | //Token: f7171f3d8e13326762a45641c2dd1e39
63 | ```
64 |
65 | You must use the generated token in your other calls.
66 |
67 | *N.B: With this autentification method the Vtiger ACL are respected.*
68 |
69 | ### Retrieve data (GET)
70 |
71 | We must use the generated token for all calls.
72 | In the following examples we will use the Accounts module, but you can use RestfulApi with all Vtiger modules.
73 |
74 | #### Get entities list
75 |
76 | ```
77 | http://yourcrm.com/modules/RestfulApi/Accounts/f7171f3d8e13326762a45641c2dd1e39
78 |
79 | or
80 |
81 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&token=f7171f3d8e13326762a45641c2dd1e39
82 | ```
83 |
84 | Result:
85 |
86 | ```
87 | {"success":true,"result":[{"accountname":"MEDIA 3+","account_no":"ACC1","phone":"","website":"","fax":"","tickersymbol":"","otherphone":"","account_id":"0","email1":"","employees":"0","email2":"","ownership":"","rating":"","industry":"","siccode":"","accounttype":"","annual_revenue":"0.00000000","emailoptout":"0","notify_owner":"0","assigned_user_id":"1","createdtime":"2016-10-28 14:40:37","modifiedtime":"2017-09-04 09:29:15","modifiedby":"1","bill_street":"34 all\u00e9e des Fr\u00eanes","ship_street":"34 all\u00e9e des Fr\u00eanes","bill_city":"Champs sur Marne","ship_city":"Champs sur Marne","bill_state":"","ship_state":"","bill_code":"77420","ship_code":"77420","bill_country":"France","ship_country":"France","bill_pobox":"","ship_pobox":"","description":"","campaignrelstatus":"","isconvertedfromlead":"0","record_id":"3","record_module":"Accounts","api_date_now":"2017-09-04 09:29:22"},{"accountname":"MEDIACRM","account_no":"ACC2","phone":"","website":"www.mediacrm.fr","fax":"","tickersymbol":"","otherphone":"","account_id":"0","email1":"","employees":"0","email2":"","ownership":"","rating":"","industry":"","siccode":"","accounttype":"","annual_revenue":"0.00000000","emailoptout":"0","notify_owner":"0","assigned_user_id":"1","createdtime":"2017-09-04 09:28:27","modifiedtime":"2017-09-04 09:28:27","modifiedby":"1","bill_street":"","ship_street":"","bill_city":"","ship_city":"","bill_state":"","ship_state":"","bill_code":"","ship_code":"","bill_country":"","ship_country":"","bill_pobox":"","ship_pobox":"","description":"","campaignrelstatus":"","isconvertedfromlead":"0","record_id":"24","record_module":"Accounts","api_date_now":"2017-09-04 09:29:22"}]}
88 | ```
89 |
90 | #### Get entities list with pagination
91 |
92 | You can manage pagination through the API. By default only the 100 first entities are returned.
93 |
94 | You can use ```start```, ```length```, and ```order```params to manage the pagination.
95 |
96 | ```
97 | http://yourcrm.com/modules/RestfulApi/Accounts/start/0/length/2/order/vtiger_account.accountname%20DESC/f7171f3d8e13326762a45641c2dd1e39
98 |
99 | or
100 |
101 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&start=0&length=2&order=vtiger_account.accountname%20DESC&token=f7171f3d8e13326762a45641c2dd1e39
102 | ```
103 |
104 | Result:
105 |
106 | ```
107 | {"success":true,"result":[{"accountname":"MEDIACRM","account_no":"ACC2","phone":"","website":"www.mediacrm.fr","fax":"","tickersymbol":"","otherphone":"","account_id":"0","email1":"","employees":"0","email2":"","ownership":"","rating":"","industry":"","siccode":"","accounttype":"","annual_revenue":"0.00000000","emailoptout":"0","notify_owner":"0","assigned_user_id":"1","createdtime":"2017-09-04 09:28:27","modifiedtime":"2017-09-04 09:28:27","modifiedby":"1","bill_street":"","ship_street":"","bill_city":"","ship_city":"","bill_state":"","ship_state":"","bill_code":"","ship_code":"","bill_country":"","ship_country":"","bill_pobox":"","ship_pobox":"","description":"","campaignrelstatus":"","isconvertedfromlead":"0","record_id":"24","record_module":"Accounts","api_date_now":"2017-09-04 10:14:38"},{"accountname":"MEDIA 3+","account_no":"ACC1","phone":"","website":"","fax":"","tickersymbol":"","otherphone":"","account_id":"0","email1":"","employees":"0","email2":"","ownership":"","rating":"","industry":"","siccode":"","accounttype":"","annual_revenue":"0.00000000","emailoptout":"0","notify_owner":"0","assigned_user_id":"1","createdtime":"2016-10-28 14:40:37","modifiedtime":"2017-09-04 09:29:15","modifiedby":"1","bill_street":"34 all\u00e9e des Fr\u00eanes","ship_street":"34 all\u00e9e des Fr\u00eanes","bill_city":"Champs sur Marne","ship_city":"Champs sur Marne","bill_state":"","ship_state":"","bill_code":"77420","ship_code":"77420","bill_country":"France","ship_country":"France","bill_pobox":"","ship_pobox":"","description":"","campaignrelstatus":"","isconvertedfromlead":"0","record_id":"3","record_module":"Accounts","api_date_now":"2017-09-04 10:14:38"}]}
108 | ```
109 |
110 | #### Search entities list
111 |
112 | Add criteria to the SQL query. You can concatenate several criteria with a semicolon.
113 |
114 | One criterium pattern : ```table.column:value```
115 |
116 | Several criteria pattern : ```table.column1:value1;table.column2:value2;...;table.columnN:valueN```
117 |
118 | ```
119 | http://yourcrm.com/modules/RestfulApi/Accounts/criteria/vtiger_account.accountname:MEDIACRM;vtiger_account.website:www.mediacrm.fr/f7171f3d8e13326762a45641c2dd1e39
120 |
121 | or
122 |
123 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&criteria=vtiger_account.accountname:MEDIACRM;vtiger_account.website:www.mediacrm.fr&token=f7171f3d8e13326762a45641c2dd1e39
124 | ```
125 |
126 | Result:
127 |
128 | ```
129 | {"success":true,"result":[{"accountname":"MEDIACRM","account_no":"ACC2","phone":"","website":"www.mediacrm.fr","fax":"","tickersymbol":"","otherphone":"","account_id":"0","email1":"","employees":"0","email2":"","ownership":"","rating":"","industry":"","siccode":"","accounttype":"","annual_revenue":"0.00000000","emailoptout":"0","notify_owner":"0","assigned_user_id":"1","createdtime":"2017-09-04 09:28:27","modifiedtime":"2017-09-04 09:28:27","modifiedby":"1","bill_street":"","ship_street":"","bill_city":"","ship_city":"","bill_state":"","ship_state":"","bill_code":"","ship_code":"","bill_country":"","ship_country":"","bill_pobox":"","ship_pobox":"","description":"","campaignrelstatus":"","isconvertedfromlead":"0","record_id":"24","record_module":"Accounts","api_date_now":"2017-09-04 09:47:16"}]}
130 | ```
131 |
132 | #### Retrieve entity by ID
133 |
134 | In the following example we retrieve the account with ID = 3.
135 |
136 | ```
137 | http://yourcrm.com/modules/RestfulApi/Accounts/3/f7171f3d8e13326762a45641c2dd1e39
138 |
139 | or
140 |
141 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&id=3&token=f7171f3d8e13326762a45641c2dd1e39
142 | ```
143 |
144 | Result:
145 |
146 | ```
147 | {"success":true,"result":{"accountname":"MEDIA 3+","account_no":"ACC1","phone":"","website":"","fax":"","tickersymbol":"","otherphone":"","account_id":"0","email1":"","employees":"0","email2":"","ownership":"","rating":"","industry":"","siccode":"","accounttype":"","annual_revenue":"0.00000000","emailoptout":"0","notify_owner":"0","assigned_user_id":"1","createdtime":"2016-10-28 14:40:37","modifiedtime":"2017-09-04 09:29:15","modifiedby":"1","bill_street":"34 all\u00e9e des Fr\u00eanes","ship_street":"34 all\u00e9e des Fr\u00eanes","bill_city":"Champs sur Marne","ship_city":"Champs sur Marne","bill_state":"","ship_state":"","bill_code":"77420","ship_code":"77420","bill_country":"France","ship_country":"France","bill_pobox":"","ship_pobox":"","description":"","campaignrelstatus":"","isconvertedfromlead":"0","record_id":"3","record_module":"Accounts","api_date_now":"2017-09-04 09:53:04"}}
148 | ```
149 |
150 | #### Retrieve PickList values
151 |
152 | You can retrieve the values from a PickList of a module.
153 | Simply specify the module and the PickList fieldname.
154 |
155 |
156 | ```
157 | http://yourcrm.com/modules/RestfulApi/Accounts/picklist/rating/f7171f3d8e13326762a45641c2dd1e39
158 |
159 | or
160 |
161 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&picklist=rating&token=f7171f3d8e13326762a45641c2dd1e39
162 | ```
163 |
164 | Result:
165 |
166 | ```
167 | {"success":true,"result":{"values":{"Acquired":"Acquired","Active":"Actif","Market Failed":"Market Failed","Project Cancelled":"Project Cancelled","Shutdown":"Shutdown"}}}
168 | ```
169 |
170 | You can also retrieve PickList dependencies if exists. Simply add ```&picklistdep=1``` param
171 |
172 | ```
173 | http://yourcrm.com/modules/RestfulApi/Accounts/picklist/rating/picklistdep/1/f7171f3d8e13326762a45641c2dd1e39
174 |
175 | or
176 |
177 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&picklist=rating&picklistdep=1&token=f7171f3d8e13326762a45641c2dd1e39
178 | ```
179 |
180 | Result:
181 |
182 | ```
183 | {"success":true,"result":{"values":{"Acquired":"Acquired","Active":"Actif","Market Failed":"Market Failed","Project Cancelled":"Project Cancelled","Shutdown":"Shutdown"},"dependencies":{"Acquired":{"accounttype":["Customer","Integrator","Investor","Partner","Press","Prospect","Reseller","Other"]},"__DEFAULT__":{"accounttype":["Analyst","Competitor","Customer","Integrator","Investor","Partner","Press","Prospect","Reseller","Other"]},"Active":{"accounttype":["Analyst","Competitor","Investor","Partner","Press","Prospect","Reseller","Other"]},"Market Failed":{"accounttype":["Analyst","Competitor","Customer","Integrator","Press","Prospect","Reseller","Other"]},"Project Cancelled":{"accounttype":["Analyst","Competitor","Customer","Integrator","Investor","Partner","Reseller","Other"]},"Shutdown":{"accounttype":["Analyst","Competitor","Customer","Integrator","Investor","Partner","Press","Prospect"]}}}}
184 | ```
185 |
186 | ### Add entity (POST)
187 |
188 | We must use the generated token for all calls.
189 | In the following examples we will use the Accounts module, but you can use RestfulApi with all Vtiger modules.
190 |
191 | To create a new entity you must at least specify the mandatory fields of the module. It is not mandatory to add all other fields.
192 |
193 | To specify a field you must use its fieldname (you can find it in vtiger_field table).
194 |
195 | ```
196 | URL:
197 |
198 | http://localhost/vtiger6.5/modules/RestfulApi/Accounts/f7171f3d8e13326762a45641c2dd1e39
199 |
200 | or
201 |
202 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&token=f7171f3d8e13326762a45641c2dd1e39
203 |
204 | Data to post:
205 |
206 | accountname=MediaToolBox&email1=mediatoolbox@yopmail.com&phone=+33656896325&bill_street=100 street beta test\r\nBat. B&bill_code=34000&bill_city=Montpellier&bill_country=France
207 | ```
208 |
209 | Result:
210 | ```
211 | {"success": true,"result": 25}
212 |
213 | //25 is the ID of the new created entity
214 | ```
215 |
216 | ### Update entity (PUT or POST)
217 |
218 | We must use the generated token for all calls.
219 | In the following examples we will use the Accounts module, but you can use RestfulApi with all Vtiger modules.
220 |
221 | To update an entity you must specify its ID and the fields values you want to update.
222 |
223 | To specify a field you must use its fieldname (you can find it in vtiger_field table).
224 |
225 | ```
226 | URL:
227 |
228 | http://localhost/vtiger6.5/modules/RestfulApi/Accounts/25/f7171f3d8e13326762a45641c2dd1e39
229 |
230 | or
231 |
232 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&id=25&token=f7171f3d8e13326762a45641c2dd1e39
233 |
234 | Data to post:
235 |
236 | accountname=MediaToolBox2&email1=mediatoolbox2@yopmail.com
237 | ```
238 |
239 | Result:
240 | ```
241 | {"success": true,"result": 25}
242 |
243 | //25 is the ID of the updated entity
244 | ```
245 |
246 | ### Delete entity (DELETE)
247 |
248 | We must use the generated token for all calls.
249 | In the following examples we will use the Accounts module, but you can use RestfulApi with all Vtiger modules.
250 |
251 | To remove an entity you must specify its ID.
252 |
253 | ```
254 | http://yourcrm.com/modules/RestfulApi/Accounts/25/f7171f3d8e13326762a45641c2dd1e39
255 |
256 | or
257 |
258 | http://yourcrm.com/modules/RestfulApi/index.php?module=Accounts&id=25&token=f7171f3d8e13326762a45641c2dd1e39
259 | ```
260 |
261 | Result:
262 |
263 | ```
264 | {"success": true,"result": true}
265 | ```
266 |
--------------------------------------------------------------------------------
/doc/images/vtiger-user-access-key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sardoj/VTRestfulAPI/312aa03353d518633e941bfa3640f9f4ee6079bd/doc/images/vtiger-user-access-key.png
--------------------------------------------------------------------------------
/examples/php/RestfulApi.php:
--------------------------------------------------------------------------------
1 | crmUrl = $crmUrl;
29 |
30 | $url = $this->crmUrl . 'modules/RestfulApi/index.php?module=Auth&login=' . $userLogin . '&password=' . $userPassword;
31 |
32 | $response = $this->callUrl($url);
33 |
34 | if($response->success)
35 | {
36 | $this->token = $response->result;
37 |
38 | $result = $this->token;
39 | }
40 | else
41 | {
42 | $result = $response;
43 | }
44 |
45 | return $result;
46 | }
47 |
48 | public function signInByKey($crmUrl, $userAccessKey)
49 | {
50 | $result = false;
51 |
52 | if(substr($crmUrl, -1) != '/')
53 | {
54 | $crmUrl .= '/';
55 | }
56 |
57 | $this->crmUrl = $crmUrl;
58 |
59 | $url = $this->crmUrl . 'modules/RestfulApi/index.php?module=Auth&key=' . $userAccessKey;
60 |
61 | $response = $this->callUrl($url);
62 |
63 | if($response->success)
64 | {
65 | $this->token = $response->result;
66 |
67 | $result = $this->token;
68 | }
69 | else
70 | {
71 | $result = $response;
72 | }
73 |
74 | return $result;
75 | }
76 |
77 | public function setToken($crmUrl, $token)
78 | {
79 | if(substr($crmUrl, -1) != '/')
80 | {
81 | $crmUrl .= '/';
82 | }
83 |
84 | $this->crmUrl = $crmUrl;
85 | $this->token = $token;
86 | }
87 |
88 | public function call($url_params, $a_params=array())
89 | {
90 | if(!preg_match('`&token=`', $url))
91 | {
92 | $url_params .= '&token=' . $this->token;
93 | }
94 |
95 | $crmUrl = $this->crmUrl;
96 |
97 | if(!preg_match('`^' . $crmUrl . '`', $url_params))
98 | {
99 | $url = $this->crmUrl . "modules/RestfulApi/index.php?" . $url_params;
100 | }
101 | else
102 | {
103 | $url = $url_params;
104 | }
105 |
106 | return $this->callUrl($url, $a_params);
107 | }
108 |
109 | protected function callUrl($url, $a_params=array())
110 | {
111 | //Use Curl
112 | $ch = curl_init();
113 |
114 | if(!empty($a_params) && $a_params != "DELETE")
115 | {
116 | //Sanitize params
117 | $a_params = $this->sanitizeParams($a_params);
118 |
119 | //url-ify the data for the POST
120 | $params_string = "";
121 | foreach ($a_params as $key => $value) {
122 | $params_string .= $key . '=' . $value . '&';
123 | }
124 | rtrim($params_string, '&');
125 |
126 |
127 | curl_setopt($ch, CURLOPT_POST, count($a_params));
128 | curl_setopt($ch, CURLOPT_POSTFIELDS, $params_string);
129 | }
130 |
131 | //If there is ?id= or &id= in the url, set PUT method
132 | if(preg_match('/[?|&]id=/', $url) && $a_params=="DELETE")
133 | {
134 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
135 | }
136 | elseif(preg_match('/[?|&]id=/', $url) && !empty($a_params))
137 | {
138 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
139 | }
140 |
141 | //Set the url, number of POST vars, POST data
142 | curl_setopt($ch, CURLOPT_URL, $url);
143 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
144 | curl_setopt($ch, CURLOPT_HEADER, 0);
145 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
146 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
147 |
148 | //Execute request
149 | $result = curl_exec($ch);
150 | //echo 'Curl error: ' . curl_error($ch);
151 |
152 | //Close connection
153 | curl_close($ch);
154 |
155 | $o_result = json_decode($result);
156 |
157 | return $o_result;
158 | }
159 |
160 | protected function sanitizeParams($a_params)
161 | {
162 | if(is_array($a_params))
163 | {
164 | foreach ($a_params as $key => $value) {
165 | $a_params[$key] = str_replace(
166 | array(
167 | "'",
168 | "+",
169 | "/"
170 | ),
171 | array(
172 | "%27",
173 | "%2B",
174 | "\/"
175 | ),
176 | $value
177 | );
178 | }
179 | }
180 |
181 | return $a_params;
182 | }
183 | }
--------------------------------------------------------------------------------
/examples/php/RestfulApiCalls.php:
--------------------------------------------------------------------------------
1 | api = new RestfulApi();
23 | }
24 |
25 | public function setToken($crmUrl, $token)
26 | {
27 | return $this->api->setToken($crmUrl, $token);
28 | }
29 |
30 | public function signInByLoginPassword($crmUrl, $userLogin, $userPassword)
31 | {
32 | return $this->api->signInByLoginPassword($crmUrl, $userLogin, $userPassword);
33 | }
34 |
35 | public function signInByKey($crmUrl, $userAccessKey)
36 | {
37 | return $this->api->signInByKey($crmUrl, $userAccessKey);
38 | }
39 |
40 | public function getEntities($moduleName, $criteria="", $start=0, $length=20, $order="CE.createdtime DESC")
41 | {
42 | $url_params = "module=$moduleName&start=$start&length=$length&order=".urlencode($order)."&criteria=$criteria";
43 |
44 | $o_response = $this->api->call($url_params);
45 |
46 | if($o_response->success)
47 | {
48 | $a_entities = $o_response->result;
49 | }
50 | else
51 | {
52 | $a_entities = $o_response;
53 | }
54 |
55 | return $a_entities;
56 | }
57 |
58 | public function getEntityById($moduleName, $id)
59 | {
60 | $o_entity = null;
61 |
62 | $url_params = "module=$moduleName&id=$id";
63 |
64 | $o_response = $this->api->call($url_params);
65 |
66 | if($o_response->success)
67 | {
68 | $o_entity = $o_response->result;
69 | }
70 | else
71 | {
72 | $o_entity = $o_response;
73 | }
74 |
75 | return $o_entity;
76 | }
77 |
78 | public function getPickListValues($moduleName, $pickListName, $getDependencies=false)
79 | {
80 | $url_params = "module=$moduleName&picklist=$pickListName";
81 |
82 | if($getDependencies)
83 | {
84 | $url_params .= '&picklistdep=1';
85 | }
86 |
87 | $o_response = $this->api->call($url_params);
88 |
89 | if($o_response->success)
90 | {
91 | $return = $o_response->result;
92 | }
93 | else
94 | {
95 | $return = $o_response;
96 | }
97 |
98 | return $return;
99 | }
100 |
101 | public function createEntity($moduleName, $a_params)
102 | {
103 | $url_params = "module=$moduleName";
104 |
105 | $o_response = $this->api->call($url_params, $a_params);
106 |
107 | if($o_response->success)
108 | {
109 | $entityId = $o_response->result;
110 | }
111 | else
112 | {
113 | $entityId = $o_response;
114 | }
115 |
116 | return $entityId;
117 | }
118 |
119 | public function updateEntity($moduleName, $id, $a_params)
120 | {
121 | $url_params = "module=$moduleName&id=$id";
122 |
123 | $o_response = $this->api->call($url_params, $a_params);
124 |
125 | if($o_response->success)
126 | {
127 | $entityId = $o_response->result;
128 | }
129 | else
130 | {
131 | $entityId = $o_response;
132 | }
133 |
134 | return $entityId;
135 | }
136 |
137 | public function deleteEntity($moduleName, $id)
138 | {
139 | $b_deleted = false;
140 |
141 | $url_params = "module=$moduleName&id=$id";
142 |
143 | $o_response = $this->api->call($url_params, "DELETE");
144 |
145 | if($o_response->success && $o_response->result != false)
146 | {
147 | $b_deleted = true;
148 | }
149 |
150 | return $b_deleted;
151 | }
152 | }
--------------------------------------------------------------------------------
/examples/php/index.php:
--------------------------------------------------------------------------------
1 | Token';
30 | if(empty($_SESSION["RestfulApiToken"]))
31 | {
32 | //Signin by user access key...
33 | $token = $api->signInByKey($crmUrl, $userAccessKey);
34 |
35 | // ... or Signin by user login and password
36 | //$token = $api->signInByLoginPassword($crmUrl, $userLogin, $userPassword);
37 |
38 | if(!empty($token)) //Store token into session
39 | {
40 | $_SESSION["RestfulApiToken"] = $token;
41 | }
42 |
43 | pr($token);
44 | }
45 | else
46 | {
47 | $token = $_SESSION["RestfulApiToken"]; //Get token from session
48 | $api->setToken($crmUrl, $token);
49 | pr($token.' (from session)');
50 | }
51 |
52 | //Get products list (only actives products)
53 | $a_products = $api->getEntities('Products', 'vtiger_products.discontinued:1');
54 | echo '
Products
';
55 | pr($a_products);
56 |
57 | //Get product categories
58 | $a_categories = $api->getPickListValues('Products', 'productcategory');
59 | echo 'Product categories
';
60 | pr($a_categories);
61 |
62 | //Get product categories with dependencies
63 | $a_categories = $api->getPickListValues('Products', 'productcategory', true);
64 | echo 'Product categories with dependencies
';
65 | pr($a_categories);
66 |
67 | //Create a product
68 | $product = array(
69 | "productname" => "Test",
70 | "unit_price" => "20.52"
71 | );
72 | $productId = $api->createEntity('Products', $product);
73 | echo 'New added product id
';
74 | pr($productId);
75 |
76 | //Update product
77 | $product_update = array(
78 | "productname" => "My product",
79 | );
80 | $productId = $api->updateEntity('Products', $productId, $product_update);
81 | echo 'Updated product id
';
82 | pr($productId);
83 |
84 | //Get product by id
85 | $o_product = $api->getEntityById('Products', $productId);
86 |
87 | //Create a quote
88 | $quantity = 3;
89 | $vat_rate = 0.20; //20%
90 | $quote = array(
91 | "subject" => "New Quote",
92 | "account_id" => $accountId,
93 | "contact_id" => "",
94 | "quote_date" => date("Y-m-d"),
95 | "validtill" => date("Y-m-d", strtotime("+30 days", mktime())),
96 | "quotestage" => "Created",
97 | "bill_street" => "100 street of beta test",
98 | "bill_code" => "34000",
99 | "bill_city" => "Montpellier",
100 | "bill_country" => "France",
101 | "ship_street" => "100 street of beta test",
102 | "ship_code" => "34000",
103 | "ship_city" => "Montpellier",
104 | "ship_country" => "France",
105 | "display_bank_name" => "1",
106 | "display_iban" => "1",
107 | "display_swift_code" => "1",
108 | "taxtype" => "group",
109 | "discount_percentage_final" => 0,
110 | "discount_amount_final" => 0,
111 | "shipping_handling_charge" => 0,
112 | "tax1_group_percentage" => 20,
113 | "tax2_group_percentage" => 0,
114 | "tax3_group_percentage" => 0,
115 | "shtax1_sh_percent" => 0,
116 | "pre_tax_total" => $o_product->unit_price * $quantity,
117 | "adjustment" => 0,
118 | "subtotal" => $o_product->unit_price * $quantity,
119 | "total" => $o_product->unit_price * $quantity * (1 + $vat_rate),
120 | "items" => json_encode(array(
121 | array(
122 | 'hdnProductId' => $o_product->record_id,
123 | 'qty' => $quantity,
124 | 'listPrice' => $o_product->unit_price,
125 | 'comment' => $o_product->description,
126 | 'discount_amount' => 0,
127 | 'discount_percentage' => 0
128 | )
129 | ))
130 | );
131 |
132 | $quoteId = $api->createEntity('Quotes', $quote);
133 | echo 'New added quote id
';
134 | pr($quoteId);
135 |
136 | //Delete quote
137 | $result = $api->deleteEntity('Quotes', $quoteId);
138 | echo 'Delete quote
';
139 | pr($result);
140 |
141 |
142 |
143 | function pr($data)
144 | {
145 | echo '';
146 | print_r($data);
147 | echo '
';
148 | }
--------------------------------------------------------------------------------
/src/languages/en_us/RestfulApi.php:
--------------------------------------------------------------------------------
1 | 'Api Tokens',
13 | 'SINGLE_RestfulApi' => 'Api Token',
14 | 'LBL_BLOCK_GENERAL_INFORMATION' => 'General information',
15 | 'LBL_BLOCK_SYSTEM_INFORMATION' => 'System information',
16 | 'LBL_TOKEN' => 'Token',
17 | 'LBL_IP' => 'IP',
18 | 'LBL_EXPIRATION_DATE' => 'Expiration date',
19 | 'LBL_CALLS' => 'Calls',
20 |
21 | );
22 |
23 | ?>
24 |
--------------------------------------------------------------------------------
/src/languages/fr_fr/RestfulApi.php:
--------------------------------------------------------------------------------
1 | 'Api Tokens',
13 | 'SINGLE_RestfulApi' => 'Api Token',
14 | 'LBL_BLOCK_GENERAL_INFORMATION' => 'Informations générales',
15 | 'LBL_BLOCK_SYSTEM_INFORMATION' => 'Informations système',
16 | 'LBL_TOKEN' => 'Token',
17 | 'LBL_IP' => 'IP',
18 | 'LBL_EXPIRATION_DATE' => 'Date d\'expiration',
19 | 'LBL_CALLS' => 'Appels',
20 |
21 | );
22 |
23 | ?>
24 |
--------------------------------------------------------------------------------
/src/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2017-09-04 07:01:35
4 | RestfulApi
5 |
6 |
7 | ion ion-ios-pricetags
8 | 1.0.0
9 |
10 | 6.0.0
11 |
12 |
13 |
14 | vtiger_restfulapi
15 |
24 |
25 |
26 | vtiger_restfulapicf
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | token
39 | 1
40 | token
41 | vtiger_restfulapi
42 | 1
43 | LBL_TOKEN
44 | 1
45 | 2
46 |
47 | 1
48 | 100
49 | V~O
50 | 0
51 | 0
52 | 1
53 | BAS
54 |
55 | 1
56 | 0
57 |
58 | restfulapiid
59 | restfulapiid
60 |
61 |
62 |
63 | ip
64 | 1
65 | ip
66 | vtiger_restfulapi
67 | 1
68 | LBL_IP
69 | 1
70 | 2
71 |
72 | 2
73 | 100
74 | V~O
75 | 0
76 | 0
77 | 1
78 | BAS
79 |
80 | 1
81 | 0
82 |
83 |
84 | calls
85 | 7
86 | calls
87 | vtiger_restfulapi
88 | 1
89 | LBL_CALLS
90 | 1
91 | 2
92 | 0
93 | 3
94 | 100
95 | I~O
96 | 1
97 |
98 | 1
99 | BAS
100 |
101 | 1
102 | 0
103 |
104 |
105 | expiration_date
106 | 70
107 | expiration_date
108 | vtiger_restfulapi
109 | 2
110 | LBL_EXPIRATION_DATE
111 | 1
112 | 2
113 |
114 | 4
115 | 100
116 | DT~O
117 | 0
118 | 0
119 | 1
120 | BAS
121 |
122 | 1
123 | 0
124 |
125 |
126 | user_id
127 | 7
128 | user_id
129 | vtiger_restfulapi
130 | 1
131 | LBL_USER_ID
132 | 1
133 | 2
134 |
135 | 5
136 | 100
137 | N~O
138 | 1
139 |
140 | 1
141 | BAS
142 |
143 | 1
144 | 0
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | assigned_user_id
153 | 53
154 | smownerid
155 | vtiger_crmentity
156 | 1
157 | Assigned To
158 | 1
159 | 2
160 |
161 | 1
162 | 100
163 | V~M
164 | 0
165 | 0
166 | 1
167 | BAS
168 |
169 | 1
170 | 0
171 |
172 |
173 | createdtime
174 | 70
175 | createdtime
176 | vtiger_crmentity
177 | 1
178 | Created Time
179 | 1
180 | 2
181 |
182 | 2
183 | 100
184 | T~O
185 | 0
186 | 0
187 | 2
188 | BAS
189 |
190 | 0
191 | 0
192 |
193 |
194 | modifiedtime
195 | 70
196 | modifiedtime
197 | vtiger_crmentity
198 | 1
199 | Modified Time
200 | 1
201 | 2
202 |
203 | 3
204 | 100
205 | DT~O
206 | 0
207 | 0
208 | 2
209 | BAS
210 |
211 | 0
212 | 0
213 |
214 |
215 |
216 |
217 |
218 |
219 | All
220 | true
221 | false
222 |
223 |
224 | token
225 | 1
226 |
227 |
228 | ip
229 | 2
230 |
231 |
232 | calls
233 | 3
234 |
235 |
236 | expiration_date
237 | 4
238 |
239 |
240 |
241 |
242 |
243 | public_readwritedelete
244 |
245 |
246 |
247 |
248 | enabled
249 |
250 |
251 |
252 | enabled
253 |
254 |
255 |
256 | enabled
257 |
258 |
259 |
260 |
261 | DETAILVIEWBASIC
262 | View History
263 |
264 |
265 | 0
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
--------------------------------------------------------------------------------
/src/modules/RestfulApi/.htaccess:
--------------------------------------------------------------------------------
1 | Options -Indexes
2 | Options +FollowSymlinks
3 |
4 | RewriteEngine on
5 | RewriteCond %{REQUEST_FILENAME} -s [OR]
6 | RewriteCond %{REQUEST_FILENAME} -l [OR]
7 | RewriteCond %{REQUEST_FILENAME} -d
8 | RewriteRule ^.*$ - [NC,L]
9 |
10 | RewriteRule ^[auth|Auth]+/([^/]+)$ index.php?module=Auth&key=$1 [L]
11 | RewriteRule ^[auth|Auth]+/([^/]+)/([^/]+)$ index.php?module=Auth&login=$1&password=$2 [L]
12 | RewriteRule ^([^/]+)/([0-9]+)/([a-zA-Z0-9]{32})$ index.php?module=$1&id=$2&token=$3 [L]
13 | RewriteRule ^([^/]+)/criteria/([^/]+)/([a-zA-Z0-9]{32})$ index.php?module=$1&token=$3&criteria=$2 [L]
14 | RewriteRule ^([^/]+)/criteria/([^/]+)/(.+)/([a-zA-Z0-9]{32})$ index.php?module=$1&token=$4&criteria=$2¶ms=$3 [L]
15 | RewriteRule ^([^/]+)/picklist/([^/]+)/([a-zA-Z0-9]{32})$ index.php?module=$1&token=$3&picklist=$2 [L]
16 | RewriteRule ^([^/]+)/action/([^/]+)/(.+)/([a-zA-Z0-9]{32})$ index.php?module=$1&action=$2&token=$4¶ms=$3 [L]
17 | RewriteRule ^([^/]+)/(.+)/([a-zA-Z0-9]{32})$ index.php?module=$1&token=$3¶ms=$2 [L]
18 | RewriteRule ^([^/]+)/([^/]+)$ index.php?module=$1&token=$2 [L]
--------------------------------------------------------------------------------
/src/modules/RestfulApi/RestfulApi.php:
--------------------------------------------------------------------------------
1 | 'crmid',
32 | 'vtiger_restfulapi' => 'restfulapiid',
33 | 'vtiger_restfulapicf'=>'restfulapiid');
34 |
35 | /**
36 | * Mandatory for Listing (Related listview)
37 | */
38 | var $list_fields = Array (
39 | 'LBL_TOKEN' => array('restfulapi', 'token'),
40 | 'LBL_IP' => array('restfulapi', 'ip'),
41 | 'LBL_EXPIRATION_DATE' => array('restfulapi', 'expiration_date'),
42 | 'LBL_CALLS' => array('restfulapi', 'calls'),
43 |
44 | );
45 | var $list_fields_name = Array (
46 | 'LBL_TOKEN' => 'token',
47 | 'LBL_IP' => 'ip',
48 | 'LBL_EXPIRATION_DATE' => 'expiration_date',
49 | 'LBL_CALLS' => 'calls',
50 |
51 | );
52 |
53 | // Make the field link to detail view
54 | var $list_link_field = 'token';
55 |
56 | // For Popup listview and UI type support
57 | var $search_fields = Array(
58 | 'LBL_CALLS' => array('restfulapi', 'calls'),
59 | 'LBL_EXPIRATION_DATE' => array('restfulapi', 'expiration_date'),
60 | 'LBL_IP' => array('restfulapi', 'ip'),
61 | 'LBL_TOKEN' => array('restfulapi', 'token'),
62 |
63 | );
64 | var $search_fields_name = Array (
65 | 'LBL_CALLS' => 'calls',
66 | 'LBL_EXPIRATION_DATE' => 'expiration_date',
67 | 'LBL_IP' => 'ip',
68 | 'LBL_TOKEN' => 'token',
69 |
70 | );
71 |
72 | // For Popup window record selection
73 | var $popup_fields = Array ('token');
74 |
75 | // For Alphabetical search
76 | var $def_basicsearch_col = 'token';
77 |
78 | // Column value to use on detail view record text display
79 | var $def_detailview_recname = 'token';
80 |
81 | // Used when enabling/disabling the mandatory fields for the module.
82 | // Refers to vtiger_field.fieldname values.
83 | var $mandatory_fields = Array('token','assigned_user_id');
84 |
85 | var $default_order_by = 'token';
86 | var $default_sort_order='ASC';
87 |
88 | function RestfulApi() {
89 | $this->log =LoggerManager::getLogger('RestfulApi');
90 | $this->db = PearDatabase::getInstance();
91 | $this->column_fields = getColumnFields('RestfulApi');
92 | }
93 |
94 | /**
95 | * Invoked when special actions are performed on the module.
96 | * @param String Module name
97 | * @param String Event Type
98 | */
99 | function vtlib_handler($moduleName, $eventType) {
100 | if($eventType == 'module.postinstall') {
101 | //Enable ModTracker for the module
102 | static::enableModTracker($moduleName);
103 | //Create Related Lists
104 | static::createRelatedLists();
105 | } else if($eventType == 'module.disabled') {
106 | // Handle actions before this module is being uninstalled.
107 | } else if($eventType == 'module.preuninstall') {
108 | // Handle actions when this module is about to be deleted.
109 | } else if($eventType == 'module.preupdate') {
110 | // Handle actions before this module is updated.
111 | } else if($eventType == 'module.postupdate') {
112 | //Create Related Lists
113 | static::createRelatedLists();
114 | }
115 | }
116 |
117 | /**
118 | * Enable ModTracker for the module
119 | */
120 | public static function enableModTracker($moduleName)
121 | {
122 | include_once 'vtlib/Vtiger/Module.php';
123 | include_once 'modules/ModTracker/ModTracker.php';
124 |
125 | //Enable ModTracker for the module
126 | $moduleInstance = Vtiger_Module::getInstance($moduleName);
127 | ModTracker::enableTrackingForModule($moduleInstance->getId());
128 | }
129 |
130 | protected static function createRelatedLists()
131 | {
132 | include_once('vtlib/Vtiger/Module.php');
133 |
134 | }
135 | }
--------------------------------------------------------------------------------
/src/modules/RestfulApi/actions/Api.php:
--------------------------------------------------------------------------------
1 | module = $request->get('module');
34 |
35 | if($this->module == 'Auth') //Auth
36 | {
37 | //Get auth action
38 | $this->_getAuthAction($request);
39 | }
40 | else //Call
41 | {
42 | //Get user from token
43 | $this->_getUserFromToken($request);
44 |
45 | // Get asked action
46 | $this->_getRequestAction($request);
47 |
48 | // Get passed parameters
49 | $this->_getRequestParams($request);
50 | }
51 | }
52 |
53 | public function process(Vtiger_Request $request)
54 | {
55 | //PreProcess
56 | $this->preProcess($request);
57 |
58 | //Process
59 | if(!empty($this->module) && !empty($this->action))
60 | {
61 | if($this->module == 'Auth')
62 | {
63 | //Login
64 | $m_result = $this->_login();
65 | }
66 | else
67 | {
68 | //Call module action
69 | $m_result = $this->_callModuleAction();
70 | }
71 | }
72 | else
73 | {
74 | $this->response("", 406);
75 | }
76 |
77 | //PostProcess
78 | $this->postProcess($m_result);
79 | }
80 |
81 | protected function postProcess($m_result)
82 | {
83 | if($this->action == 'Create' && preg_match('`^[0-9]+`', $m_result))
84 | {
85 | $this->response($m_result, 201); //Created
86 | }
87 | elseif(is_array($m_result) && isset($m_result["success"]) && $m_result["success"] === false)
88 | {
89 | $this->response($m_result, 200, true); //OK with error
90 | }
91 | else
92 | {
93 | $this->response($m_result, 200); //OK
94 | }
95 | }
96 |
97 | public function response($data, $status, $error=false) //Must be public as in class RestFulApi_Rest_Model
98 | {
99 | $this->_code = ($status) ? $status : 200;
100 | $this->set_headers();
101 |
102 | $this->response = new Vtiger_Response();
103 |
104 | if(!$error && ($status == 200 || $status == 201))
105 | {
106 | $this->response->setResult($data);
107 | }
108 | else
109 | {
110 | $this->response->setError($data["code"], $data["message"]);
111 | }
112 |
113 | $this->response->emit();
114 |
115 | die(); //To do not launch the following actions
116 | }
117 |
118 | protected function _getAuthAction(Vtiger_Request $request)
119 | {
120 | if($request->has('key'))
121 | {
122 | $this->action = 'loginByKey';
123 | $this->a_params = array("key" => $request->get('key'));
124 | }
125 | elseif($request->has('login') && $request->has('password'))
126 | {
127 | $this->action = 'login';
128 | $this->a_params = array("login" => $request->get('login'), "password" => $request->get('password'));
129 | }
130 | else
131 | {
132 | $this->response("", 406);
133 | }
134 | }
135 |
136 | protected function _getUserFromToken(Vtiger_Request $request)
137 | {
138 | if(!$request->has('token'))
139 | {
140 | $error = array("code" => "TOKEN_NOT_FOUND", "message" => "Token not found");
141 | }
142 | else
143 | {
144 | $authController = new RestfulApi_Auth_Action();
145 | $m_result = $authController->checkToken($request->get('token'));
146 |
147 | if($m_result["success"] === false)
148 | {
149 | $error = $m_result;
150 | }
151 | else
152 | {
153 | $this->userId = $m_result["user_id"];
154 | }
155 | }
156 | if(!empty($error))
157 | {
158 | $this->response($error, 400, true);
159 | }
160 | }
161 |
162 | protected function _getRequestAction(Vtiger_Request $request)
163 | {
164 | switch($this->get_request_method())
165 | {
166 | //CRUD
167 | case 'POST':
168 | if($request->has("id") && $request->get("id") > 0 )
169 | {
170 | $this->action = 'Update'; //U
171 | }
172 | else
173 | {
174 | $this->action = 'Create'; //C
175 | }
176 | break;
177 | case 'GET':
178 | $this->action = 'Retrieve'; //R
179 | break;
180 | case 'PUT':
181 | $this->action = 'Update'; //U
182 | break;
183 | case 'DELETE':
184 | $this->action = 'Delete'; //D
185 | break;
186 | default:
187 | $this->action = null;
188 | break;
189 | }
190 | }
191 |
192 | protected function _getRequestParams(Vtiger_Request $request)
193 | {
194 | $this->a_params = array();
195 | foreach($this->_request as $key => $val)
196 | {
197 | if($key != 'module' && $key != 'action' && $key != 'token')
198 | {
199 | if(!preg_match('`[^\\\]/`', $val))
200 | {
201 | $this->a_params[$key] = str_replace("\/", "/", $val);
202 | }
203 | else
204 | {
205 | $a_data = explode("/", $val);
206 |
207 | for($i=0; $ia_params[$param_name] = str_replace("\/", "/", $param_value);
213 | }
214 | }
215 | }
216 | elseif($key == 'action')
217 | {
218 | $this->action = $val;
219 | }
220 | }
221 |
222 | if($this->action == 'Update' || $this->action == 'Delete')
223 | {
224 | if($request->has("id"))
225 | {
226 | $this->a_params["id"] = $request->get("id");
227 | }
228 | else
229 | {
230 | $this->response("", 406);
231 | }
232 | }
233 | }
234 |
235 | protected function _login()
236 | {
237 | $m_result = null;
238 |
239 | $authController = new RestFulApi_Auth_Action();
240 |
241 | if(!empty($this->a_params["key"]))
242 | {
243 | $m_result = $authController->loginByKey($this->a_params["key"]);
244 | }
245 | else
246 | {
247 | $m_result = $authController->login($this->a_params["login"], $this->a_params["password"]);
248 | }
249 |
250 | return $m_result;
251 | }
252 |
253 | protected function _callModuleAction()
254 | {
255 | $moduleInstance = Vtiger_Module::getInstance($this->module);
256 |
257 | if(empty($moduleInstance))
258 | {
259 | $this->response("", 404);
260 | }
261 | else
262 | {
263 | switch($this->action)
264 | {
265 | //CRUD
266 |
267 | case 'Create': //C
268 | $m_result = $this->_createItem();
269 | break;
270 |
271 | case 'Retrieve': //R
272 | $id = !empty($this->a_params["id"]) ? $this->a_params["id"] : null;
273 | $start = !empty($this->a_params["start"]) ? $this->a_params["start"] : 0;
274 | $length = !empty($this->a_params["length"]) ? $this->a_params["length"] : 20;
275 | $order = !empty($this->a_params["order"]) ? $this->a_params["order"] : '';
276 | $criteria = !empty($this->a_params["criteria"]) ? $this->a_params["criteria"] : '';
277 | $picklist = !empty($this->a_params["picklist"]) ? $this->a_params["picklist"] : '';
278 | $picklistDependencies = !empty($this->a_params["picklistdep"]) ? $this->a_params["picklistdep"] : false;
279 |
280 | if(!empty($id))
281 | {
282 | //Unique item
283 | $m_result = $this->_retrieveItem($id);
284 | }
285 | elseif(!empty($picklist))
286 | {
287 | //Picklist values
288 | $m_result = $this->_retrievePickListValues($picklist, $picklistDependencies);
289 | }
290 | else
291 | {
292 | //Multiples items
293 | $m_result = $this->_retrieveItems($start, $length, $criteria, $order);
294 | }
295 | break;
296 |
297 | case 'Update': //U
298 | $id = !empty($this->a_params["id"]) ? $this->a_params["id"] : null;
299 |
300 | $m_result = $this->_updateItem($id);
301 | break;
302 |
303 | case 'Delete': //D
304 | $id = !empty($this->a_params["id"]) ? $this->a_params["id"] : null;
305 |
306 | if(!empty($id))
307 | {
308 | //Delete item
309 | $m_result = $this->_deleteItem($id);
310 | }
311 | break;
312 | }
313 | }
314 |
315 | return $m_result;
316 | }
317 |
318 | //Create
319 | public function _createItem()
320 | {
321 | require_once("modules/{$this->module}/{$this->module}.php"); //Mandatory else there is the following exeption: 'Sorry! Attempt to access restricted file.'
322 |
323 | //New item
324 | $focus = CRMEntity::getInstance($this->module);
325 | $focus->mode = '';
326 |
327 | //Set item data
328 | $this->_setItemData($focus);
329 |
330 | if($this->_isInventory() && !empty($this->a_params["items"]))
331 | {
332 | /*Si on est en inventory, on enregistre toutes les lignes d'une meme entité en une fois:
333 | - soit dans la boucle while()
334 | - soit apres la boucle pour la fin du fichier
335 |
336 | a chaque iteration dans le while(), on ajoute les lignes produits numérotés dans $lastEntity*/
337 |
338 | $a_items = json_decode($this->a_params["items"]);
339 |
340 | $focus->column_fields['totalProductCount'] += count($a_items);
341 |
342 | //Set currency automaticaly if necessary
343 | if(empty($focus->column_fields['currency_id']))
344 | {
345 | $focus->column_fields['currency_id'] = CurrencyField::getDBCurrencyId();
346 | }
347 |
348 | foreach($a_items as $j => $item)
349 | {
350 | $index = $j + 1;
351 |
352 | foreach ($this->productFields as $field)
353 | {
354 | $focus->column_fields[ $field.$index ] = $item->{$field};
355 | }
356 |
357 | if(!empty($item->discount_percentage) && $item->discount_percentage > 0)
358 | {
359 | $focus->column_fields["discount$index"] = "on";
360 | $focus->column_fields["discount_type$index"] = "percentage";
361 | }
362 | elseif(!empty($item->discount_amount) && $item->discount_amount > 0)
363 | {
364 | $focus->column_fields["discount$index"] = "on";
365 | $focus->column_fields["discount_type$index"] = "amount";
366 | }
367 | }
368 |
369 | $focusId = $this->_createInventoryEntity($focus->column_fields);
370 | }
371 | else //NOT Inventory
372 | {
373 | //Save data
374 | $focus->save($this->module);
375 | $focusId = $focus->id;
376 |
377 | }
378 |
379 | return $focusId;
380 | }
381 |
382 | protected function _createInventoryEntity($a_columnFields)
383 | {
384 | if(!empty($a_columnFields["discount_percentage_final"]) && $a_columnFields["discount_percentage_final"] > 0)
385 | {
386 | $a_columnFields["discount_final"] = "on";
387 | $a_columnFields["discount_type_final"] = "percentage";
388 | }
389 | elseif(!empty($a_columnFields["discount_amount_final"]) && $a_columnFields["discount_amount_final"] > 0)
390 | {
391 | $a_columnFields["discount_final"] = "on";
392 | $a_columnFields["discount_type_final"] = "amount";
393 | }
394 |
395 | $_REQUEST = $a_columnFields;
396 |
397 | $focus = CRMEntity::getInstance($this->module);
398 | $focus->mode = '';
399 | $focus->column_fields = $_REQUEST;
400 |
401 | $focus->save($this->module);
402 |
403 | return $focus->id;
404 | }
405 |
406 | //Retrieve multiple
407 | protected function _retrieveItems($start=0, $length=20, $criteriaList='', $order='')
408 | {
409 | $a_items = array();
410 |
411 | $result = $this->_doRetrieveQuery($start, $length, $criteriaList, $order);
412 |
413 | $db = PearDatabase::getInstance();
414 | while($row = $db->fetchByAssoc($result))
415 | {
416 | $item = $this->_retrieveItem($row["id"], false); //Don't check entity existence because here this existence is obvious (returned by the previous query)
417 |
418 | if(!empty($item))
419 | {
420 | $a_items[] = $item;
421 | }
422 | }
423 |
424 | return $a_items;
425 | }
426 |
427 | //Retrieve unique
428 | protected function _retrieveItem($id, $checkModuleEntityExistence=true)
429 | {
430 | $m_result = null;
431 |
432 | require_once("modules/{$this->module}/{$this->module}.php");
433 |
434 | //Check if an entity exists for the module and the id
435 | if($checkModuleEntityExistence)
436 | {
437 | $db = PearDatabase::getInstance();
438 | $query = "SELECT *
439 | FROM vtiger_crmentity CE
440 | WHERE CE.deleted = 0
441 | AND CE.setype LIKE ?
442 | AND CE.crmid = ?";
443 |
444 | $result = $db->pquery($query, array($this->module, $id));
445 | $count = $db->num_rows($result);
446 | }
447 |
448 | //If entity exists, retrieve info
449 | if(!$checkModuleEntityExistence || $count > 0)
450 | {
451 | $focus = CRMEntity::getInstance($this->module);
452 | $focus->retrieve_entity_info($id, $this->module);
453 |
454 | foreach($focus->column_fields as &$field)
455 | {
456 | $field = vtlib_purify($field);
457 | }
458 |
459 | $m_result = $focus->column_fields;
460 | $m_result["api_date_now"] = date("Y-m-d H:i:s"); //Added to control the serveur hour
461 | }
462 |
463 | return $m_result;
464 | }
465 |
466 | //Retrieve picklist values
467 | protected function _retrievePickListValues($picklistFieldName, $getDependencies=false)
468 | {
469 | $m_result = null;
470 |
471 | require_once("modules/PickList/PickListUtils.php");
472 | //require_once("modules/PickList/DependentPickListUtils.php");
473 |
474 | $a_translations = $this->_getTranslations();
475 |
476 | $a_values = getAllPickListValues($picklistFieldName, $a_translations);
477 |
478 | $m_result = array(
479 | "values" => $a_values
480 | );
481 |
482 | if($getDependencies)
483 | {
484 | $m_result["dependencies"] = array();
485 |
486 | //Get field dependencies
487 | $a_dependancies = Vtiger_DependencyPicklist::getPicklistDependencyDatasource($this->module);
488 |
489 | foreach($a_dependancies as $fieldName => $dependency)
490 | {
491 | if($fieldName == $picklistFieldName)
492 | {
493 | $m_result["dependencies"] = $dependency;
494 | }
495 | }
496 | }
497 |
498 | return $m_result;
499 | }
500 |
501 | //Update
502 | protected function _updateItem($id)
503 | {
504 | $m_result = false;
505 |
506 | $item = $this->_retrieveItem($id);
507 |
508 | if(!empty($item))
509 | {
510 | //Get item
511 | $focus = CRMEntity::getInstance($this->module);
512 | $focus->retrieve_entity_info($id, $this->module);
513 |
514 | foreach($focus->column_fields as &$field)
515 | {
516 | $field = vtlib_purify($field);
517 | }
518 |
519 | $focus->mode = 'edit';
520 | $focus->id = $id;
521 |
522 | //Set item data
523 | $this->_setItemData($focus);
524 |
525 | //Save data
526 | try
527 | {
528 | $focus->save($this->module);
529 |
530 | $m_result = $id;
531 | }
532 | catch(Exception $e)
533 | {
534 | $m_result = $e;
535 | }
536 | }
537 |
538 | return $m_result;
539 | }
540 |
541 | //Delete
542 | protected function _deleteItem($id)
543 | {
544 | $b_deleted = false;
545 |
546 | $item = $this->_retrieveItem($id);
547 |
548 | if(!empty($item))
549 | {
550 | $db = PearDatabase::getInstance();
551 | $query = "UPDATE vtiger_crmentity
552 | SET deleted = 1
553 | WHERE crmid = ?";
554 | $db->pquery($query, array($id));
555 |
556 | $b_deleted = true;
557 | }
558 |
559 | return $b_deleted;
560 | }
561 |
562 | protected function _doRetrieveQuery($start=0, $length=20, $criteriaList='', $order='')
563 | {
564 | //Get module database data
565 | require_once("modules/{$this->module}/{$this->module}.php");
566 |
567 | $moduleName = $this->module;
568 | $o_module = new $moduleName();
569 | $a_moduleTables = $o_module->tab_name_index; //List of used tables
570 |
571 | $moduleInstance = Vtiger_Module::getInstance($this->module);
572 |
573 | $tableName = $moduleInstance->basetable;
574 | $tableId = $moduleInstance->basetableid;
575 |
576 | $order = !empty($order) ? $order : 'CE.createdtime ASC';
577 |
578 | //Get criteria
579 | $a_criteriaParams = array();
580 | $a_criteria = explode(";", $criteriaList);
581 | if(!empty($a_criteria))
582 | {
583 | $criteriaQuery = "1 ";
584 |
585 | foreach($a_criteria as $criteria)
586 | {
587 | $a_criteriaFields = explode(":", $criteria);
588 |
589 | //Without operator
590 | if(count($a_criteriaFields) == 2)
591 | {
592 | $field = trim($a_criteriaFields[0]);
593 | $value = trim($a_criteriaFields[1]);
594 |
595 | if(!empty($field) && !empty($value))
596 | {
597 | $criteriaQuery .= "AND $field = ? ";
598 |
599 | $a_criteriaParams[] = $value;
600 | }
601 | else
602 | {
603 | $criteriaQuery .= "AND 0 ";
604 | }
605 | }
606 | //With operator
607 | elseif(count($a_criteriaFields) > 2)
608 | {
609 | $field = trim($a_criteriaFields[0]);
610 | $operatorStr = trim($a_criteriaFields[1]);
611 | $value = trim($a_criteriaFields[2]);
612 |
613 | //Securize query
614 | switch($operatorStr)
615 | {
616 | case 'neq':
617 | $operator = '!=';
618 | break;
619 |
620 | case 'lt':
621 | $operator = '<';
622 | break;
623 |
624 | case 'gt':
625 | $operator = '>';
626 | break;
627 |
628 | case 'lte':
629 | $operator = '<=';
630 | break;
631 |
632 | case 'gte':
633 | $operator = '>=';
634 | break;
635 |
636 | case 'like':
637 | $operator = 'LIKE';
638 |
639 | case 'eq':
640 | default:
641 | $operator = '=';
642 | break;
643 | }
644 |
645 | if(!empty($field) && !empty($operator) && !empty($value))
646 | {
647 | $criteriaQuery .= "AND $field $operator ? ";
648 |
649 | $a_criteriaParams[] = $value;
650 | }
651 | else
652 | {
653 | $criteriaQuery .= "AND 0 ";
654 | }
655 | }
656 | }
657 | }
658 |
659 | //Add SELECT clause
660 | $query = "SELECT T.$tableId AS id
661 | FROM $tableName T ";
662 |
663 | //Add JOIN clauses
664 | $query .= "INNER JOIN vtiger_crmentity CE ON CE.crmid = T.$tableId AND CE.deleted = 0 ";
665 | foreach($a_moduleTables as $table => $idField)
666 | {
667 | if($table != 'vtiger_crmentity')
668 | {
669 | $query .= "LEFT JOIN $table ON $table.$idField = T.$tableId ";
670 | }
671 | }
672 |
673 | //Add WHERE, ORDERY BY, LIMIT clauses
674 | $query .= "WHERE $criteriaQuery
675 | ORDER BY $order
676 | LIMIT $start, $length";
677 |
678 | $db = PearDatabase::getInstance();
679 | $result = $db->pquery($query, array($a_criteriaParams));
680 |
681 | return $result;
682 | }
683 |
684 | protected function _setItemData(&$focus)
685 | {
686 | foreach($this->a_params as $fieldName => $fieldValue)
687 | {
688 | if(is_array($fieldValue))
689 | {
690 | $focus->column_fields[$fieldName] = $fieldValue;
691 | }
692 | else if($fieldValue !== null)
693 | {
694 | $focus->column_fields[$fieldName] = decode_html($fieldValue);
695 | }
696 | }
697 |
698 | if(empty($focus->column_fields["assigned_user_id"]))
699 | {
700 | $focus->column_fields["assigned_user_id"] = $this->userId;
701 | }
702 | }
703 |
704 | protected function _getTranslations()
705 | {
706 | global $default_language;
707 |
708 | //Get translations
709 | $userLanguage = Vtiger_Language_Handler::getLanguage();
710 | $languageStrings = $jsLanguageStrings = array();
711 |
712 | $a_translations = array();
713 |
714 | if(file_exists("../../languages/$userLanguage/Vtiger.php")) //User language. Warning: file_exists() do not take in consideration the include path (so we add ../../)
715 | {
716 | include("languages/$userLanguage/Vtiger.php"); //CRM default language
717 | }
718 | else
719 | {
720 | include("languages/fr_fr/Vtiger.php");
721 | }
722 |
723 | $a_translations = array_merge($languageStrings, $jsLanguageStrings);
724 |
725 | //Module
726 | if(file_exists("../../languages/$userLanguage/".$this->module.".php")) //User language. Warning: file_exists() do not take in consideration the include path (so we add ../../)
727 | {
728 | include("languages/$userLanguage/".$this->module.".php"); //CRM default language
729 | }
730 | else
731 | {
732 | include("languages/$default_language/".$this->module.".php");
733 | }
734 |
735 | $a_moduleTranslations = array_merge($languageStrings, $jsLanguageStrings);
736 |
737 | foreach($a_moduleTranslations as $label => $translation)
738 | {
739 | $a_translations[$label] = vtlib_purify($translation);
740 | }
741 |
742 | return $a_translations;
743 | }
744 |
745 | protected function _isInventory()
746 | {
747 | return in_array($this->module, $this->inventoryModules);
748 | }
749 | }
--------------------------------------------------------------------------------
/src/modules/RestfulApi/actions/Auth.php:
--------------------------------------------------------------------------------
1 | column_fields['user_name'] = $username;
29 |
30 | $isAuthentificated = $user->doLogin($password);
31 |
32 | if(!$isAuthentificated)
33 | {
34 | return array("success" => false, "error" => array("code" => "USER_NOT_FOUND", "message" => "User not found"));
35 | }
36 |
37 | //Retrieve user id
38 | $user_id = $user->retrieve_user_id($username);
39 |
40 | //Simulate a login for the user
41 | $this->loginAsUser($user_id);
42 |
43 | //Generate a token
44 | return $this->generateToken($user_id);
45 | }
46 |
47 | public function loginByKey($key)
48 | {
49 | $db = PearDatabase::getInstance();
50 | $query = "SELECT id, user_name
51 | FROM vtiger_users
52 | WHERE accesskey = ?
53 | AND deleted = 0
54 | LIMIT 1";
55 | $result = $db->pquery($query, array($key));
56 |
57 | $user = $db->fetchByAssoc($result);
58 |
59 | if(empty($user))
60 | {
61 | return array("success" => false, "error" => array("code" => "KEY_NOT_FOUND", "message" => "Key not found"));
62 | }
63 |
64 | $user_id = $user["id"];
65 |
66 | //Simulate a login for the user
67 | $this->loginAsUser($user_id);
68 |
69 | //Generate a token
70 | return $this->generateToken($user_id);
71 | }
72 |
73 | protected function loginAsUser($user_id)
74 | {
75 | global $current_user;
76 |
77 | //On simule que l'utilisateur se connecte
78 | $user = CRMEntity::getInstance('Users');
79 | $current_user = $user->retrieveCurrentUserInfoFromFile($user_id);
80 | }
81 |
82 | protected function generateToken($user_id)
83 | {
84 | $db = PearDatabase::getInstance();
85 |
86 | //Generate a new token
87 | $token = md5($_SERVER["REMOTE_ADDR"]."-".mktime());
88 |
89 | //Save the token
90 | $focus = new RestfulApi();
91 | $focus->mode = '';
92 | $focus->column_fields["token"] = $token;
93 | $focus->column_fields["assigned_user_id"] = $user_id;
94 | $focus->column_fields["ip"] = $_SERVER["REMOTE_ADDR"];
95 | $focus->column_fields["calls"] = 0;
96 | $focus->column_fields["expiration_date"] = date("Y-m-d H:i:s", mktime()+$this->expirationTimeout);
97 | $focus->column_fields["user_id"] = $user_id;
98 | $focus->save("RestfulApi");
99 |
100 | $tokenId = $focus->id;
101 |
102 | if(is_int($tokenId) && $tokenId > -1)
103 | {
104 | return $token;
105 | }
106 |
107 | return false;
108 | }
109 |
110 | public function checkToken($token)
111 | {
112 | $ip = $_SERVER["REMOTE_ADDR"];
113 | $expirationDate = date("Y-m-d H:i:s");
114 |
115 |
116 | $db = PearDatabase::getInstance();
117 | $query = "SELECT API.*, API.restfulapiid AS id
118 | FROM vtiger_restfulapi API
119 | INNER JOIN vtiger_crmentity CE ON CE.crmid = API.restfulapiid AND CE.deleted = 0
120 | WHERE API.token = ?
121 | AND API.expiration_date >= ?
122 | AND API.ip = ?
123 | LIMIT 1";
124 | $result = $db->pquery($query, array($token, $expirationDate, $ip));
125 |
126 | $row = $db->fetchByAssoc($result);
127 |
128 | if(!empty($row["id"]))
129 | {
130 | $this->loginAsUser($row["user_id"]);
131 |
132 | $focus = new RestfulApi();
133 | $focus->retrieve_entity_info($row["id"], 'RestfulApi');
134 | $focus->id = $row["id"];
135 |
136 | }
137 |
138 | if(empty($focus))
139 | {
140 | return array("success" => false, "error" => array("code" => "INVALID_TOKEN", "message" => "Invalid token"));
141 | }
142 |
143 | //Update token
144 | $focus->mode = 'edit';
145 | $focus->column_fields["calls"] = $focus->column_fields["calls"] + 1;
146 | $focus->column_fields["expiration_date"] = date("Y-m-d H:i:s", mktime()+$this->expirationTimeout);
147 | $focus->save("RestfulApi");
148 |
149 | return array("success" => true, "user_id" => $row["user_id"]);
150 | }
151 | }
--------------------------------------------------------------------------------
/src/modules/RestfulApi/index.php:
--------------------------------------------------------------------------------
1 | process($request);
--------------------------------------------------------------------------------
/src/modules/RestfulApi/models/Rest.php:
--------------------------------------------------------------------------------
1 | inputs();
16 | }
17 |
18 | public function get_referer(){
19 | return $_SERVER['HTTP_REFERER'];
20 | }
21 |
22 | public function response($data,$status){
23 | $this->_code = ($status)?$status:200;
24 | $this->set_headers();
25 | echo $data;
26 | exit;
27 | }
28 |
29 | private function get_status_message(){
30 | $status = array(
31 | 100 => 'Continue',
32 | 101 => 'Switching Protocols',
33 | 200 => 'OK',
34 | 201 => 'Created',
35 | 202 => 'Accepted',
36 | 203 => 'Non-Authoritative Information',
37 | 204 => 'No Content',
38 | 205 => 'Reset Content',
39 | 206 => 'Partial Content',
40 | 300 => 'Multiple Choices',
41 | 301 => 'Moved Permanently',
42 | 302 => 'Found',
43 | 303 => 'See Other',
44 | 304 => 'Not Modified',
45 | 305 => 'Use Proxy',
46 | 306 => '(Unused)',
47 | 307 => 'Temporary Redirect',
48 | 400 => 'Bad Request',
49 | 401 => 'Unauthorized',
50 | 402 => 'Payment Required',
51 | 403 => 'Forbidden',
52 | 404 => 'Not Found',
53 | 405 => 'Method Not Allowed',
54 | 406 => 'Not Acceptable',
55 | 407 => 'Proxy Authentication Required',
56 | 408 => 'Request Timeout',
57 | 409 => 'Conflict',
58 | 410 => 'Gone',
59 | 411 => 'Length Required',
60 | 412 => 'Precondition Failed',
61 | 413 => 'Request Entity Too Large',
62 | 414 => 'Request-URI Too Long',
63 | 415 => 'Unsupported Media Type',
64 | 416 => 'Requested Range Not Satisfiable',
65 | 417 => 'Expectation Failed',
66 | 500 => 'Internal Server Error',
67 | 501 => 'Not Implemented',
68 | 502 => 'Bad Gateway',
69 | 503 => 'Service Unavailable',
70 | 504 => 'Gateway Timeout',
71 | 505 => 'HTTP Version Not Supported');
72 | return ($status[$this->_code])?$status[$this->_code]:$status[500];
73 | }
74 |
75 | public function get_request_method(){
76 | return $_SERVER['REQUEST_METHOD'];
77 | }
78 |
79 | private function inputs(){
80 | switch($this->get_request_method()){
81 | case "POST":
82 | if(!empty($_POST))
83 | {
84 | $this->_request = $this->cleanInputs($_POST);
85 | }
86 | else
87 | {
88 | $data = get_object_vars(json_decode(file_get_contents("php://input")));
89 | $this->_request = $this->cleanInputs($data);
90 | }
91 | break;
92 | case "GET":
93 | case "DELETE":
94 | $this->_request = $this->cleanInputs($_GET);
95 | break;
96 | case "PUT":
97 | parse_str(file_get_contents("php://input"),$this->_request);
98 | $this->_request = $this->cleanInputs($this->_request);
99 | break;
100 | default:
101 | $this->response('',406);
102 | break;
103 | }
104 | }
105 |
106 | private function cleanInputs($data){
107 | $clean_input = array();
108 | if(is_array($data)){
109 | foreach($data as $k => $v){
110 | $clean_input[$k] = $this->cleanInputs($v);
111 | }
112 | }else{
113 | if(get_magic_quotes_gpc()){
114 | $data = trim(stripslashes($data));
115 | }
116 | $data = strip_tags($data);
117 | $clean_input = trim($data);
118 | }
119 | return $clean_input;
120 | }
121 |
122 | protected function set_headers(){
123 | header("HTTP/1.1 ".$this->_code." ".$this->get_status_message());
124 | header("Content-Type:".$this->_content_type);
125 | }
126 | }
127 | ?>
--------------------------------------------------------------------------------
/src/modules/RestfulApi/schema.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | vtiger_restfulapi
6 |
14 |
15 |
16 | vtiger_restfulapicf
17 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------