Fixed a problem with deleting teams by supplying a required parameter.
[timetracker.git] / WEB-INF / lib / ttAdmin.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 import('ttUser');
30
31 // ttAdmin class is used to perform admin tasks.
32 class ttAdmin {
33
34   var $err = null; // Error object, passed to us as reference.
35                    // We use it to communicate errors to caller.
36
37   // Constructor.
38   function __construct(&$err = null) {
39     $this->err = $err;
40   }
41
42   // getSubgroups rerurns an array of subgroups for a group.
43   function getSubgroups($group_id) {
44     return array(); // TODO: not yet implemented.
45   }
46
47   // getUsers obtains user ids in a group.
48   function getUsers($group_id) {
49     $mdb2 = getConnection();
50     $sql = "select id from tt_users where team_id = $group_id";
51     $res = $mdb2->query($sql);
52     $users = array();
53     if (!is_a($res, 'PEAR_Error')) {
54       while ($val = $res->fetchRow()) {
55         $users[] = $val;
56       }
57     }
58     return $users;
59   }
60
61   // markUserDeleted marks a user and all things associated with user as deleted.
62   function markUserDeleted($user_id) {
63     $mdb2 = getConnection();
64
65     // Mark user binds as deleted.
66     $sql = "update tt_user_project_binds set status = NULL where user_id = $user_id";
67     $affected = $mdb2->exec($sql);
68     if (is_a($affected, 'PEAR_Error')) return false;
69
70     // Mark favorite reports as deleted.
71     $sql = "update tt_fav_reports set status = NULL where user_id = $user_id";
72     $affected = $mdb2->exec($sql);
73     if (is_a($affected, 'PEAR_Error')) return false;
74
75     // Mark user as deleted.
76     global $user;
77     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
78     $sql = "update tt_users set status = NULL $modified_part where id = $user_id";
79     $affected = $mdb2->exec($sql);
80     if (is_a($affected, 'PEAR_Error')) return false;
81
82     return true;
83   }
84
85   // The markTasksDeleted deletes task binds and marks the tasks as deleted for a group.
86   function markTasksDeleted($group_id) {
87     $mdb2 = getConnection();
88     $sql = "select id from tt_tasks where team_id = $group_id";
89     $res = $mdb2->query($sql);
90     if (is_a($res, 'PEAR_Error')) return false;
91
92     while ($val = $res->fetchRow()) {
93       // Delete task binds.
94       $task_id = $val['id'];
95       $sql = "delete from tt_project_task_binds where task_id = $task_id";
96       $affected = $mdb2->exec($sql);
97       if (is_a($affected, 'PEAR_Error')) return false;
98
99       // Mark task as deleted.
100       $sql = "update tt_tasks set status = NULL where id = $task_id";
101       $affected = $mdb2->exec($sql);
102       if (is_a($affected, 'PEAR_Error')) return false;
103     }
104
105     return true;
106   }
107
108   // markGroupDeleted marks the group and everything in it as deleted.
109   function markGroupDeleted($group_id) {
110
111     // Keep the logic simple by returning false on first error.
112
113     // Obtain subgroups and call self recursively on them.
114     $subgroups = $this->getSubgroups($group_id);
115     foreach($subgroups as $subgroup) {
116       if (!$this->markGroupDeleted($subgroup['id']))
117         return false;
118     }
119
120     // Now that we are done with subgroups, handle this group.
121     $users = $this->getUsers($group_id);
122
123     // Iterate through team users and mark them as deleted.
124     foreach ($users as $one_user) {
125       if (!$this->markUserDeleted($one_user['id']))
126           return false;
127     }
128
129     // Mark tasks deleted.
130     if (!$this->markTasksDeleted($group_id)) return false;
131
132     $mdb2 = getConnection();
133
134     // Mark roles deleted.
135     $sql = "update tt_roles set status = NULL where team_id = $group_id";
136     $affected = $mdb2->exec($sql);
137     if (is_a($affected, 'PEAR_Error')) return false;
138
139     // Mark projects deleted.
140     $sql = "update tt_projects set status = NULL where team_id = $group_id";
141     $affected = $mdb2->exec($sql);
142     if (is_a($affected, 'PEAR_Error')) return false;
143
144     // Mark clients deleted.
145     $sql = "update tt_clients set status = NULL where team_id = $group_id";
146     $affected = $mdb2->exec($sql);
147     if (is_a($affected, 'PEAR_Error')) return false;
148
149     // Mark invoices deleted.
150     $sql = "update tt_invoices set status = NULL where team_id = $group_id";
151     $affected = $mdb2->exec($sql);
152     if (is_a($affected, 'PEAR_Error')) return false;
153
154     // Mark custom fields deleted.
155     $sql = "update tt_custom_fields set status = NULL where team_id = $group_id";
156     $affected = $mdb2->exec($sql);
157     if (is_a($affected, 'PEAR_Error')) return false;
158
159     // Mark notifications deleted.
160     $sql = "update tt_cron set status = NULL where team_id = $group_id";
161     $affected = $mdb2->exec($sql);
162     if (is_a($affected, 'PEAR_Error')) return false;
163
164     // Note: we don't mark tt_log or tt_expense_items deleted here.
165     // Reasoning is:
166     //
167     // 1) Users may mark some of them deleted during their work.
168     // If we mark all of them deleted here, we can't recover nicely
169     // as we'll lose track of what was deleted by user.
170     //
171     // 2) DB maintenance script (Clean up DB from inactive teams) should
172     // get rid of these items permanently eventually.
173
174     // Mark group deleted.
175     global $user;
176     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
177     $sql = "update tt_teams set status = NULL $modified_part where id = $group_id";
178     $affected = $mdb2->exec($sql);
179     if (is_a($affected, 'PEAR_Error')) return false;
180
181     return true;
182   }
183
184   // validateGroupInfo validates group information entered by user.
185   function validateGroupInfo($fields) {
186     global $i18n;
187     global $auth;
188
189     $result = true;
190
191     if (!ttValidString($fields['group_name'], true)) {
192       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.team_name'));
193       $result = false;
194     }
195     if (!ttValidString($fields['user_name'])) {
196       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.manager_name'));
197       $result = false;
198     }
199     if (!ttValidString($fields['new_login'])) {
200       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.manager_login'));
201       $result = false;
202     }
203
204     // If we change login, it must be unique.
205     if ($fields['new_login'] != $fields['old_login']) {
206       if (ttUserHelper::getUserByLogin($fields['new_login'])) {
207         $this->err->add($i18n->getKey('error.user_exists'));
208         $result = false;
209       }
210     }
211
212     if (!$auth->isPasswordExternal() && ($fields['password1'] || $fields['password2'])) {
213       if (!ttValidString($fields['password1'])) {
214         $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.password'));
215         $result = false;
216       }
217       if (!ttValidString($fields['password2'])) {
218         $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.confirm_password'));
219         $result = false;
220       }
221       if ($fields['password1'] !== $fields['password2']) {
222         $this->err->add($i18n->getKey('error.not_equal'), $i18n->getKey('label.password'), $i18n->getKey('label.confirm_password'));
223         $result = false;
224       }
225     }
226     if (!ttValidEmail($fields['email'], true)) {
227       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.email'));
228       $result = false;
229     }
230
231     return $result;
232   }
233
234   // updateGroup validates user input and updates the group with new information.
235   function updateGroup($team_id, $fields) {
236     if (!$this->validateGroupInfo($fields)) return false; // Can't continue as user input is invalid.
237
238     global $user;
239     $mdb2 = getConnection();
240
241     // Update group name if it changed.
242     if ($fields['old_group_name'] != $fields['new_group_name']) {
243       $name_part = 'name = '.$mdb2->quote($fields['new_group_name']);
244       $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
245       $sql = 'update tt_teams set '.$name_part.$modified_part.' where id = '.$team_id;
246       $affected = $mdb2->exec($sql);
247       if (is_a($affected, 'PEAR_Error')) return false;
248     }
249
250     // Update group manager.
251     $user_id = $fields['user_id'];
252     $login_part = 'login = '.$mdb2->quote($fields['new_login']);
253     if ($fields['password1'])
254       $password_part = ', password = md5('.$mdb2->quote($fields['password1']).')';
255     $name_part = ', name = '.$mdb2->quote($fields['user_name']);
256     $email_part = ', email = '.$mdb2->quote($fields['email']);
257     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
258     $sql = 'update tt_users set '.$login_part.$password_part.$name_part.$email_part.$modified_part.'where id = '.$user_id;
259     $affected = $mdb2->exec($sql);
260     if (is_a($affected, 'PEAR_Error')) return false;
261
262     return true;
263   }
264
265   // validateUserInfo validates account information entered by user.
266   function validateUserInfo($fields) {
267     global $i18n;
268     global $user;
269     global $auth;
270
271     $result = true;
272
273     if (!ttValidString($fields['name'])) {
274       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.person_name'));
275       $result = false;
276     }
277     if (!ttValidString($fields['login'])) {
278       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.login'));
279       $result = false;
280     }
281     // If we change login, it must be unique.
282     if ($fields['login'] != $user->login) {
283       if (ttUserHelper::getUserByLogin($fields['login'])) {
284         $this->err->add($i18n->getKey('error.user_exists'));
285         $result = false;
286       }
287     }
288     if (!$auth->isPasswordExternal() && ($fields['password1'] || $fields['password2'])) {
289       if (!ttValidString($fields['password1'])) {
290         $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.password'));
291         $result = false;
292       }
293       if (!ttValidString($fields['password2'])) {
294         $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.confirm_password'));
295         $result = false;
296       }
297       if ($fields['password1'] !== $fields['password2']) {
298         $this->err->add($i18n->getKey('error.not_equal'), $i18n->getKey('label.password'), $i18n->getKey('label.confirm_password'));
299         $result = false;
300       }
301     }
302     if (!ttValidEmail($fields['email'], true)) {
303       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.email'));
304       $result = false;
305     }
306
307     return $result;
308   }
309
310   // updateSelf validates user input and updates admin account with new information.
311   function updateSelf($fields) {
312     if (!$this->validateUserInfo($fields)) return false; // Can't continue as user input is invalid.
313
314     global $user;
315     global $i18n;
316     $mdb2 = getConnection();
317
318     // Update self.
319     $user_id = $user->id;
320     $login_part = 'login = '.$mdb2->quote($fields['login']);
321     if ($fields['password1'])
322       $password_part = ', password = md5('.$mdb2->quote($fields['password1']).')';
323     $name_part = ', name = '.$mdb2->quote($fields['name']);
324     $email_part = ', email = '.$mdb2->quote($fields['email']);
325     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
326     $sql = 'update tt_users set '.$login_part.$password_part.$name_part.$email_part.$modified_part.'where id = '.$user_id;
327     $affected = $mdb2->exec($sql);
328     if (is_a($affected, 'PEAR_Error')) {
329       $this->err->add($i18n->getKey('error.db'));
330       return false;
331     }
332
333     return true;
334   }
335 }