Implemented attachment edit.
[timetracker.git] / WEB-INF / lib / ttFileHelper.class.php
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Anuko Time Tracker
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
6 // +----------------------------------------------------------------------+
7 // | LIBERAL FREEWARE LICENSE: This source code document may be used
8 // | by anyone for any purpose, and freely redistributed alone or in
9 // | combination with other software, provided that the license is obeyed.
10 // |
11 // | There are only two ways to violate the license:
12 // |
13 // | 1. To redistribute this code in source form, with the copyright
14 // |    notice or license removed or altered. (Distributing in compiled
15 // |    forms without embedded copyright notices is permitted).
16 // |
17 // | 2. To redistribute modified versions of this code in *any* form
18 // |    that bears insufficient indications that the modifications are
19 // |    not the work of the original author(s).
20 // |
21 // | This license applies to this document only, not any other software
22 // | that it may be combined with.
23 // |
24 // +----------------------------------------------------------------------+
25 // | Contributors:
26 // | https://www.anuko.com/time_tracker/credits.htm
27 // +----------------------------------------------------------------------+
28
29 // ttFileHelper class is used for attachment handling.
30 class ttFileHelper {
31   var $errors = null;       // Errors go here. Set in constructor by reference.
32   var $storage_uri = null;  // Location of file storage facility.
33   var $register_uri = null; // URI to register with file storage facility.
34   var $putfile_uri = null;  // URI to put file in file storage.
35   var $getfile_uri = null;  // URI to get file from file storage.
36   var $site_id = null;      // Site id for file storage.
37   var $site_key = null;     // Site key for file storage.
38
39   // Constructor.
40   function __construct(&$errors) {
41     $this->errors = &$errors;
42
43     if (defined('FILE_STORAGE_URI')) {
44       $this->storage_uri = FILE_STORAGE_URI;
45       $this->register_uri = $this->storage_uri.'register';
46       $this->putfile_uri = $this->storage_uri.'putfile';
47       $this->deletefile_uri = $this->storage_uri.'deletefile';
48       $this->getfile_uri = $this->storage_uri.'getfile';
49       $this->checkSiteRegistration();
50     }
51   }
52
53   // checkSiteRegistration - obtains site id and key from local database.
54   // If not found, it tries to register with file storage facility.
55   function checkSiteRegistration() {
56
57     global $i18n;
58     $mdb2 = getConnection();
59
60     // Obtain site id.
61     $sql = "select param_value as id from tt_site_config where param_name = 'locker_id'";
62     $res = $mdb2->query($sql);
63     $val = $res->fetchRow();
64     if (!$val) {
65       // No site id found, need to register.
66       $fields = array('name' => urlencode('time tracker'),
67         'origin' => urlencode('time tracker source'));
68
69       // Urlify the data for the POST.
70       foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
71       $fields_string = rtrim($fields_string, '&');
72
73       // Open connection.
74       $ch = curl_init();
75
76       // Set the url, number of POST vars, POST data.
77       curl_setopt($ch, CURLOPT_URL, $this->register_uri);
78       curl_setopt($ch, CURLOPT_POST, count($fields));
79       curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
80       curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
81
82       // Execute a post request.
83       $result = curl_exec($ch);
84
85       // Close connection.
86       curl_close($ch);
87
88       $result_array = json_decode($result, true);
89       if (!$result_array) {
90         $this->errors->add($i18n->get('error.file_storage'));
91       }
92       else if ($result_array['error']) {
93         // Add an error from file storage facility if we have it.
94         $this->errors->add($result_array['error']);
95       }
96       else if ($result_array['id'] && $result_array['key']) {
97         $this->site_id = $result_array['id'];
98         $this->site_key = $result_array['key'];
99
100         // Registration successful. Store id and key locally for future use.
101         $sql = "insert into tt_site_config values('locker_id', $this->site_id, now(), null)";
102         $mdb2->exec($sql);
103         $sql = "insert into tt_site_config values('locker_key', ".$mdb2->quote($this->site_key).", now(), null)";
104         $mdb2->exec($sql);
105       } else {
106         $this->errors->add($i18n->get('error.file_storage'));
107       }
108     } else {
109       // Site id found.
110       $this->site_id = $val['id'];
111
112       // Obtain site key.
113       $sql = "select param_value as site_key from tt_site_config where param_name = 'locker_key'";
114       $res = $mdb2->query($sql);
115       $val = $res->fetchRow();
116       $this->site_key = $val['site_key'];
117     }
118   }
119
120   // putFile - puts uploaded file in remote storage.
121   function putFile($fields) {
122     global $i18n;
123     global $user;
124     $mdb2 = getConnection();
125
126     $group_id = $user->getGroup();
127     $org_id = $user->org_id;
128
129     $curl_fields = array('site_id' => urlencode($this->site_id),
130       'site_key' => urlencode($this->site_key),
131       'org_id' => urlencode($org_id),
132       'org_key' => urlencode($this->getOrgKey()),
133       'group_id' => urlencode($group_id),
134       'group_key' => urlencode($this->getGroupKey()),
135       'user_id' => urlencode($fields['user_id']),   // May be null.
136       'user_key' => urlencode($fields['user_key']), // May be null.
137       'file_name' => urlencode($fields['file_name']),
138       'description' => urlencode($fields['description']),
139       'content' => urlencode(file_get_contents($_FILES['newfile']['tmp_name']))
140     );
141
142     // url-ify the data for the POST.
143     foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
144     $fields_string = rtrim($fields_string, '&');
145
146     // Open connection.
147     $ch = curl_init();
148
149     // Set the url, number of POST vars, POST data.
150     curl_setopt($ch, CURLOPT_URL, $this->putfile_uri);
151     curl_setopt($ch, CURLOPT_POST, count($fields));
152     curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
153     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
154
155     // Execute a post request.
156     $result = curl_exec($ch);
157
158     // Close connection.
159     curl_close($ch);
160
161     // Delete uploaded file.
162     unlink($_FILES['newfile']['tmp_name']);
163
164     if (!$result) {
165       $this->errors->add($i18n->get('error.file_storage'));
166       return false;
167     }
168
169     $result_array = json_decode($result, true);
170     $file_id = (int) $result_array['file_id'];
171     $file_key = $result_array['file_key'];
172     $error = $result_array['error'];
173
174     if ($error || !$file_id || !$file_key) {
175       if ($error) {
176         // Add an error from file storage facility if we have it.
177         $this->errors->add($error);
178       }
179       return false;
180     }
181
182     // File put was successful. Store file attributes locally.
183     $file_key = $mdb2->quote($file_key);
184     $entity_type = $mdb2->quote($fields['entity_type']);
185     $entity_id = (int) $fields['entity_id'];
186     $file_name = $mdb2->quote($fields['file_name']);
187     $description = $mdb2->quote($fields['description']);
188     $created = 'now()';
189     $created_ip = $mdb2->quote($_SERVER['REMOTE_ADDR']);
190     $created_by = $user->id;
191
192     $columns = '(group_id, org_id, remote_id, file_key, entity_type, entity_id, file_name, description, created, created_ip, created_by)';
193     $values = "values($group_id, $org_id, $file_id, $file_key, $entity_type, $entity_id, $file_name, $description, $created, $created_ip, $created_by)";
194     $sql = "insert into tt_files $columns $values";
195     $affected = $mdb2->exec($sql);
196     return (!is_a($affected, 'PEAR_Error'));
197   }
198
199   // deleteFile - deletes a file from remote storage and its details from local database.
200   function deleteFile($fields) {
201     global $i18n;
202     global $user;
203     $mdb2 = getConnection();
204
205     $group_id = $user->getGroup();
206     $org_id = $user->org_id;
207
208     $curl_fields = array('site_id' => urlencode($this->site_id),
209       'site_key' => urlencode($this->site_key),
210       'org_id' => urlencode($org_id),
211       'org_key' => urlencode($this->getOrgKey()),
212       'group_id' => urlencode($group_id),
213       'group_key' => urlencode($this->getGroupKey()),
214       'user_id' => urlencode($fields['user_id']),   // May be null.
215       'user_key' => urlencode($fields['user_key']), // May be null.
216       'file_id' => urlencode($fields['remote_id']),
217       'file_key' => urlencode($fields['file_key']),
218       'file_name' => urlencode($fields['file_name']));
219
220     // url-ify the data for the POST.
221     foreach($curl_fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
222     $fields_string = rtrim($fields_string, '&');
223
224     // Open connection.
225     $ch = curl_init();
226
227     // Set the url, number of POST vars, POST data.
228     curl_setopt($ch, CURLOPT_URL, $this->deletefile_uri);
229     curl_setopt($ch, CURLOPT_POST, count($fields));
230     curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
231     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
232
233     // Execute a post request.
234     $result = curl_exec($ch);
235
236     // Close connection.
237     curl_close($ch);
238
239     if (!$result) {
240       $this->errors->add($i18n->get('error.file_storage'));
241       return false;
242     }
243
244     $result_array = json_decode($result, true);
245     $status = (int) $result_array['status'];
246     $error = $result_array['error'];
247
248     if ($error) {
249       // Add an error from file storage facility if we have it.
250       $this->errors->add($error);
251       return false;
252     }
253     if ($status != 1) {
254       // There is no explicit error message, but still something not right.
255       $this->errors->add($i18n->get('error.file_storage'));
256       return false;
257     }
258
259     // Delete file reference from database.
260     $file_id = $fields['id'];
261     $sql = "delete from tt_files".
262       " where id = $file_id and org_id = $org_id and group_id = $group_id";
263     $affected = $mdb2->exec($sql);
264     if (is_a($affected, 'PEAR_Error')) {
265       $this->errors->add($i18n->get('error.db'));
266       return false;
267     }
268
269     // File successfully deleted from both file storage and database.
270     return true;
271   }
272
273   // getOrgKey obtains organization key from the database.
274   private function getOrgKey() {
275     global $user;
276     $mdb2 = getConnection();
277
278     $org_id = $user->org_id;
279     $sql = "select group_key from tt_groups where id = $org_id and status = 1";
280     $res = $mdb2->query($sql);
281     $val = $res->fetchRow();
282     return $val['group_key'];
283   }
284
285   // getGrtoupKey obtains group key from the database.
286   private function getGroupKey() {
287     global $user;
288     $mdb2 = getConnection();
289
290     $group_id = $user->getGroup();
291     $org_id = $user->org_id;
292
293     $sql = "select group_key from tt_groups where id = $group_id and org_id = $org_id and status = 1";
294     $res = $mdb2->query($sql);
295     $val = $res->fetchRow();
296     return $val['group_key'];
297   }
298
299   // getProjectFiles obtains a list of files for a project.
300   static function getProjectFiles($project_id) {
301     global $user;
302     $mdb2 = getConnection();
303
304     $group_id = $user->getGroup();
305     $org_id = $user->org_id;
306
307     $result = array();
308     $sql = "select id, remote_id, file_name as name, description from tt_files".
309       " where entity_type = 'project' and entity_id = $project_id".
310       " and group_id = $group_id and org_id = $org_id and status = 1 order by id";
311     $res = $mdb2->query($sql);
312     if (!is_a($res, 'PEAR_Error')) {
313       while ($val = $res->fetchRow()) {
314         $result[] = $val;
315       }
316     }
317     return $result;
318   }
319
320   // get - obtains file details from local database. 
321   static function get($id) {
322     global $user;
323     $mdb2 = getConnection();
324
325     $group_id = $user->getGroup();
326     $org_id = $user->org_id;
327
328     $sql = "select id, remote_id, file_key, entity_type, entity_id, file_name, description, status from tt_files".
329       " where id = $id and group_id = $group_id and org_id = $org_id and (status = 0 or status = 1)";
330     $res = $mdb2->query($sql);
331     if (!is_a($res, 'PEAR_Error')) {
332       $val = $res->fetchRow();
333       if ($val && $val['id'])
334         return $val;
335     }
336     return false;
337   }
338
339   // update - updates file details in local database.
340   static function update($fields) {
341     global $user;
342     $mdb2 = getConnection();
343
344     $group_id = $user->getGroup();
345     $org_id = $user->org_id;
346
347     $file_id = (int) $fields['id'];
348     $description = $mdb2->quote($fields['description']);
349
350     $sql = "update tt_files set description = $description where id = $file_id".
351       " and group_id = $group_id and org_id = $org_id and (status = 0 or status = 1)";
352     $affected = $mdb2->exec($sql);
353     return !is_a($affected, 'PEAR_Error');
354   }
355 }