f0ff0a4db3cc8c43d86130cedbfa79a0398cf5e6
[timetracker.git] / plugins / CustomFields.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 class CustomFields {
30
31   // Definitions of custom field types.
32   const ENTITY_TIME = 1;    // Field is associated with time entries.
33   const ENTITY_USER = 2;    // Field is associated with users.
34   const ENTITY_PROJECT = 3; // Field is associated with projects.
35
36   const TYPE_TEXT = 1;      // A text field.
37   const TYPE_DROPDOWN = 2;  // A dropdown field with pre-defined values.
38
39   // TODO: replace $fields with entity-specific arrays: timeFields, userFields, etc.
40   var $fields = array();  // Array of custom fields for group.
41
42   // Refactoring ongoing...
43   var $timeFields = null;
44   var $userFields = null;
45   var $projectFields = null;
46
47   // Constructor.
48   function __construct() {
49     global $user;
50     $mdb2 = getConnection();
51
52     $group_id = $user->getGroup();
53     $org_id = $user->org_id;
54
55     // Get fields.
56     $sql = "select id, entity_type, type, label, required from tt_custom_fields".
57       " where group_id = $group_id and org_id = $org_id and status = 1 and type > 0";
58     $res = $mdb2->query($sql);
59     if (!is_a($res, 'PEAR_Error')) {
60       while ($val = $res->fetchRow()) {
61         $this->fields[] = array('id'=>$val['id'],'type'=>$val['type'],'label'=>$val['label'],'required'=>$val['required'],'value'=>'');
62         if (CustomFields::ENTITY_TIME == $val['entity_type'])
63           $this->timeFields[] = $val;
64         else if (CustomFields::ENTITY_USER == $val['entity_type'])
65           $this->userFields[] = $val;
66         else if (CustomFields::ENTITY_PROJECT == $val['entity_type'])
67           $this->projectFields[] = $val;
68       }
69     }
70   }
71
72   function insert($log_id, $field_id, $option_id, $value) {
73     global $user;
74     $mdb2 = getConnection();
75
76     $group_id = $user->getGroup();
77     $org_id = $user->org_id;
78
79     $sql = "insert into tt_custom_field_log (group_id, org_id, log_id, field_id, option_id, value)".
80       " values($group_id, $org_id, $log_id, $field_id, ".$mdb2->quote($option_id).", ".$mdb2->quote($value).")";
81     $affected = $mdb2->exec($sql);
82     return (!is_a($affected, 'PEAR_Error'));
83   }
84
85   function update($log_id, $field_id, $option_id, $value) {
86     if (!$field_id)
87       return true; // Nothing to update.
88
89     // Remove older custom field values, if any.
90     $res = $this->delete($log_id);
91     if (!$res)
92       return false;
93
94     if (!$value && !$option_id)
95       return true; // Do not insert NULL values.
96
97     return $this->insert($log_id, $field_id, $option_id, $value);
98   }
99
100   function delete($log_id) {
101     global $user;
102     $mdb2 = getConnection();
103
104     $group_id = $user->getGroup();
105     $org_id = $user->org_id;
106
107     $sql = "update tt_custom_field_log set status = null".
108       " where log_id = $log_id and group_id = $group_id and org_id = $org_id";
109     $affected = $mdb2->exec($sql);
110     return (!is_a($affected, 'PEAR_Error'));
111   }
112
113   function get($log_id) {
114     global $user;
115     $mdb2 = getConnection();
116
117     $group_id = $user->getGroup();
118     $org_id = $user->org_id;
119
120     $sql = "select id, field_id, option_id, value from tt_custom_field_log".
121       " where log_id = $log_id and group_id = $group_id and org_id = $org_id and status = 1";
122     $res = $mdb2->query($sql);
123     if (!is_a($res, 'PEAR_Error')) {
124       $fields = array();
125       while ($val = $res->fetchRow()) {
126         $fields[] = $val;
127       }
128       return $fields;
129     }
130     return false;
131   }
132
133   // insertOption adds a new option to a custom field.
134   static function insertOption($field_id, $option_name) {
135     global $user;
136     $mdb2 = getConnection();
137
138     $group_id = $user->getGroup();
139     $org_id = $user->org_id;
140
141     // Check if the option exists.
142     $id = 0;
143     $sql = "select id from tt_custom_field_options".
144       " where field_id = $field_id and group_id = $group_id and org_id = $org_id and value = ".$mdb2->quote($option_name);
145     $res = $mdb2->query($sql);
146     if (is_a($res, 'PEAR_Error'))
147       return false;
148     if ($val = $res->fetchRow()) $id = $val['id'];
149
150     // Insert option.
151     if (!$id) {
152       $sql = "insert into tt_custom_field_options (group_id, org_id, field_id, value)".
153         " values($group_id, $org_id, $field_id, ".$mdb2->quote($option_name).")";
154       $affected = $mdb2->exec($sql);
155       if (is_a($affected, 'PEAR_Error'))
156         return false;
157     }
158     return true;
159   }
160
161   // updateOption updates option name.
162   static function updateOption($id, $option_name) {
163     global $user;
164     $mdb2 = getConnection();
165
166     $group_id = $user->getGroup();
167     $org_id = $user->org_id;
168
169     $sql = "update tt_custom_field_options set value = ".$mdb2->quote($option_name).
170        " where id = $id and group_id = $group_id and org_id = $org_id";
171     $affected = $mdb2->exec($sql);
172     return (!is_a($affected, 'PEAR_Error'));
173   }
174
175   // delete Option deletes an option and all custom field log entries that used it.
176   static function deleteOption($id) {
177     global $user;
178     $mdb2 = getConnection();
179
180     $group_id = $user->getGroup();
181     $org_id = $user->org_id;
182
183     $field_id = CustomFields::getFieldIdForOption($id);
184     if (!$field_id) return false;
185
186     // Delete log entries with this option. TODO: why? Research impact.
187     $sql = "update tt_custom_field_log set status = null".
188       " where field_id = $field_id and group_id = $group_id and org_id = $org_id and value = ".$mdb2->quote($id);
189     $affected = $mdb2->exec($sql);
190     if (is_a($affected, 'PEAR_Error'))
191       return false;
192
193     // Delete the option.
194     $sql = "update tt_custom_field_options set status = null".
195       " where id = $id and group_id = $group_id and org_id = $org_id";
196     $affected = $mdb2->exec($sql);
197     return (!is_a($affected, 'PEAR_Error'));
198   }
199
200   // getOptions returns an array of options for a custom field.
201   static function getOptions($field_id) {
202     global $user;
203     $mdb2 = getConnection();
204
205     $group_id = $user->getGroup();
206     $org_id = $user->org_id;
207
208     // Get options.
209     $sql = "select id, value from tt_custom_field_options".
210       " where field_id = $field_id and group_id = $group_id and org_id = $org_id and status = 1 order by value";
211     $res = $mdb2->query($sql);
212     if (!is_a($res, 'PEAR_Error')) {
213       $options = array();
214       while ($val = $res->fetchRow()) {
215         $options[$val['id']] = $val['value'];
216       }
217       return $options;
218     }
219     return false;
220   }
221
222   // getOptionName returns an option name for a custom field.
223   static function getOptionName($id) {
224     global $user;
225     $mdb2 = getConnection();
226
227     $group_id = $user->getGroup();
228     $org_id = $user->org_id;
229
230     $sql = "select value from tt_custom_field_options".
231       " where id = $id and group_id = $group_id and org_id = $org_id and status = 1";
232     $res = $mdb2->query($sql);
233     if (!is_a($res, 'PEAR_Error')) {
234       $val = $res->fetchRow();
235       $name = $val['value'];
236       return $name;
237     }
238     return false;
239   }
240
241   // getFields returns an array of custom fields for group.
242   static function getFields() {
243     global $user;
244     $mdb2 = getConnection();
245
246     $group_id = $user->getGroup();
247     $org_id = $user->org_id;
248
249     $fields = array();
250     $sql = "select id, entity_type, type, label from tt_custom_fields".
251       " where group_id = $group_id and org_id = $org_id and status = 1 and type > 0";
252     $res = $mdb2->query($sql);
253     if (!is_a($res, 'PEAR_Error')) {
254       while ($val = $res->fetchRow()) {
255         $fields[] = $val;
256       }
257       return $fields;
258     }
259     return false;
260   }
261
262   // getField returns a custom field.
263   static function getField($id) {
264     global $user;
265     $mdb2 = getConnection();
266
267     $group_id = $user->getGroup();
268     $org_id = $user->org_id;
269
270     $sql = "select label, entity_type, type, required from tt_custom_fields".
271       " where id = $id and group_id = $group_id and org_id = $org_id";
272     $res = $mdb2->query($sql);
273     if (!is_a($res, 'PEAR_Error')) {
274       $val = $res->fetchRow();
275       if (!$val)
276         return false;
277       return $val;
278     }
279     return false;
280   }
281
282   // getFieldIdForOption returns field id from an associated option id.
283   static function getFieldIdForOption($option_id) {
284     global $user;
285     $mdb2 = getConnection();
286
287     $group_id = $user->getGroup();
288     $org_id = $user->org_id;
289
290     $sql = "select field_id from tt_custom_field_options".
291       " where id = $option_id and group_id = $group_id and org_id = $org_id";
292     $res = $mdb2->query($sql);
293     if (!is_a($res, 'PEAR_Error')) {
294       $val = $res->fetchRow();
295       $field_id = $val['field_id'];
296       return $field_id;
297     }
298     return false;
299   }
300
301   // The insertField inserts a custom field for group.
302   static function insertField($field_name, $entity_type, $field_type, $required) {
303     global $user;
304     $mdb2 = getConnection();
305
306     $group_id = $user->getGroup();
307     $org_id = $user->org_id;
308
309     $sql = "insert into tt_custom_fields (group_id, org_id, entity_type, type, label, required, status)".
310       " values($group_id, $org_id, $entity_type, $field_type, ".$mdb2->quote($field_name).", $required, 1)";
311     $affected = $mdb2->exec($sql);
312     return (!is_a($affected, 'PEAR_Error'));
313   }
314
315   // The updateField updates custom field for group.
316   static function updateField($id, $name, $type, $required) {
317     global $user;
318     $mdb2 = getConnection();
319
320     $group_id = $user->getGroup();
321     $org_id = $user->org_id;
322
323     $sql = "update tt_custom_fields set label = ".$mdb2->quote($name).", type = $type, required = $required".
324       " where id = $id and group_id = $group_id and org_id = $org_id";
325     $affected = $mdb2->exec($sql);
326     return (!is_a($affected, 'PEAR_Error'));
327   }
328
329   // The deleteField deletes a custom field, its options and log entries for group.
330   static function deleteField($field_id) {
331     global $user;
332     $mdb2 = getConnection();
333
334     $group_id = $user->getGroup();
335     $org_id = $user->org_id;
336
337     // Mark log entries as deleted. TODO: why are we doing this? Research impact.
338     $sql = "update tt_custom_field_log set status = null".
339       " where field_id = $field_id and group_id = $group_id and org_id = $org_id";
340     $affected = $mdb2->exec($sql);
341     if (is_a($affected, 'PEAR_Error'))
342       return false;
343
344     // Mark field options as deleted.
345     $sql = "update tt_custom_field_options set status = null".
346       " where field_id = $field_id and group_id = $group_id and org_id = $org_id";
347     $affected = $mdb2->exec($sql);
348     if (is_a($affected, 'PEAR_Error'))
349       return false;
350
351     // Mark custom field as deleted.
352     $sql = "update tt_custom_fields set status = null".
353       " where id = $field_id and group_id = $group_id and org_id = $org_id";
354     $affected = $mdb2->exec($sql);
355     return (!is_a($affected, 'PEAR_Error'));
356   }
357
358   // insertEntityFields - inserts entity custom fields into tt_entity_custom_fields.
359   function insertEntityFields($entity_type, $entity_id, $entityFields) {
360     foreach ($entityFields as $entityField) {
361       if (!$this->insertEntityField($entity_type, $entity_id, $entityField))
362         return false;
363     }
364     return true;
365   }
366
367   // insertEntityField - inserts a single entity custom field into tt_entity_custom_fields.
368   function insertEntityField($entity_type, $entity_id, $entityField) {
369     global $user;
370     $mdb2 = getConnection();
371
372     $group_id = $user->getGroup();
373     $org_id = $user->org_id;
374
375     $created = 'now(), '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', '.$user->id;
376
377     $field_id = (int) $entityField['field_id'];
378
379     $option_id = $entityField['type'] == CustomFields::TYPE_DROPDOWN ? (int) $entityField['value'] : null;
380     $value = $entityField['type'] == CustomFields::TYPE_TEXT ? $entityField['value'] : null;
381
382     // TODO: add a jon to protect from bogus option_ids in post.
383     $sql = "insert into tt_entity_custom_fields".
384       " (group_id, org_id, entity_type, entity_id, field_id, option_id, value, created, created_ip, created_by)".
385       " values($group_id, $org_id, $entity_type, $entity_id, $field_id, ".$mdb2->quote($option_id).", ".$mdb2->quote($value).", $created)";
386     $affected = $mdb2->exec($sql);
387     return (!is_a($affected, 'PEAR_Error'));
388   }
389
390   // updateEntityFields - updates entity custom fields in tt_entity_custom_fields table
391   // by doing a delete followed up by an insert.
392   function updateEntityFields($entity_type, $entity_id, $entityFields) {
393     $result =  $this->deleteEntityFields($entity_type, $entity_id);
394     if (!$result) return false;
395
396     return $this->insertEntityFields($entity_type, $entity_id, $entityFields);
397   }
398
399   // deleteEntityFields - deletes entity custom fields (permanently).
400   // Note: deleting, rather than marking fields deleted is on purpose
401   // because we want to keep the table small after multiple entity edits.
402   function deleteEntityFields($entity_type, $entity_id) {
403     global $user;
404     $mdb2 = getConnection();
405
406     $group_id = $user->getGroup();
407     $org_id = $user->org_id;
408
409     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$user->id;
410
411     $sql = "delete from  tt_entity_custom_fields".
412       " where entity_type = $entity_type and entity_id = $entity_id".
413       " and group_id = $group_id and org_id = $org_id";
414     $affected = $mdb2->exec($sql);
415     return (!is_a($affected, 'PEAR_Error'));
416   }
417
418   // getEntityFieldValue - obtains entity custom field value from the database.
419   function getEntityFieldValue($entity_type, $entity_id, $field_id, $type) {
420     global $user;
421     $mdb2 = getConnection();
422
423     $group_id = $user->getGroup();
424     $org_id = $user->org_id;
425
426     $sql = "select option_id, value from tt_entity_custom_fields".
427       " where entity_type = $entity_type and entity_id = $entity_id".
428       " and field_id = $field_id".
429       " and group_id = $group_id and org_id = $org_id and status = 1";
430     $res = $mdb2->query($sql);
431     if (!is_a($res, 'PEAR_Error')) {
432       if ($val = $res->fetchRow()) {
433         if (CustomFields::TYPE_DROPDOWN == $type)
434           return $val['option_id'];
435         if (CustomFields::TYPE_TEXT == $type)
436           return $val['value'];
437       }
438     }
439     return null;
440   }
441 }