A bit more refactoring for admin.
[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'))
69       return false;
70
71     // Mark favorite reports as deleted.
72     $sql = "update tt_fav_reports set status = NULL where user_id = $user_id";
73     $affected = $mdb2->exec($sql);
74     if (is_a($affected, 'PEAR_Error'))
75       return false;
76
77     // Mark user as deleted.
78     global $user;
79     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
80     $sql = "update tt_users set status = NULL $modified_part where id = $user_id";
81     $affected = $mdb2->exec($sql);
82     if (is_a($affected, 'PEAR_Error'))
83       return false;
84
85     return true;
86   }
87
88   // The markTasksDeleted deletes task binds and marks the tasks as deleted for a group.
89   function markTasksDeleted($group_id) {
90     $mdb2 = getConnection();
91     $sql = "select id from tt_tasks where team_id = $group_id";
92     $res = $mdb2->query($sql);
93     if (is_a($res, 'PEAR_Error')) return false;
94
95     while ($val = $res->fetchRow()) {
96       // Delete task binds.
97       $task_id = $val['id'];
98       $sql = "delete from tt_project_task_binds where task_id = $task_id";
99       $affected = $mdb2->exec($sql);
100       if (is_a($affected, 'PEAR_Error')) return false;
101
102       // Mark task as deleted.
103       $sql = "update tt_tasks set status = NULL where id = $task_id";
104       $affected = $mdb2->exec($sql);
105       if (is_a($affected, 'PEAR_Error')) return false;
106     }
107     return true;
108   }
109
110   // markGroupDeleted marks the group and everything in it as deleted.
111   function markGroupDeleted($group_id) {
112
113     // Keep the logic simple by returning false on first error.
114
115     // Obtain subgroups and call self recursively on them.
116     $subgroups = $this->getSubgroups();
117     foreach($subgroups as $subgroup) {
118       if (!$this->markGroupDeleted($subgroup['id']))
119         return false;
120     }
121
122     // Now that we are done with subgroups, handle this group.
123     $users = $this->getUsers($group_id);
124
125     // Iterate through team users and mark them as deleted.
126     foreach ($users as $one_user) {
127       if (!$this->markUserDeleted($one_user['id']))
128           return false;
129     }
130
131     // Mark tasks deleted.
132     if (!$this->markTasksDeleted($group_id)) return false;
133
134     $mdb2 = getConnection();
135
136     // Mark roles deleted.
137     $sql = "update tt_roles set status = NULL where team_id = $group_id";
138     $affected = $mdb2->exec($sql);
139     if (is_a($affected, 'PEAR_Error')) return false;
140
141     // Mark projects deleted.
142     $sql = "update tt_projects set status = NULL where team_id = $group_id";
143     $affected = $mdb2->exec($sql);
144     if (is_a($affected, 'PEAR_Error')) return false;
145
146     // Mark clients deleted.
147     $sql = "update tt_clients set status = NULL where team_id = $group_id";
148     $affected = $mdb2->exec($sql);
149     if (is_a($affected, 'PEAR_Error')) return false;
150
151     // Mark invoices deleted.
152     $sql = "update tt_invoices set status = NULL where team_id = $group_id";
153     $affected = $mdb2->exec($sql);
154     if (is_a($affected, 'PEAR_Error')) return false;
155
156     // Mark custom fields deleted.
157     $sql = "update tt_custom_fields set status = NULL where team_id = $group_id";
158     $affected = $mdb2->exec($sql);
159     if (is_a($affected, 'PEAR_Error')) return false;
160
161     // Mark notifications deleted.
162     $sql = "update tt_cron set status = NULL where team_id = $group_id";
163     $affected = $mdb2->exec($sql);
164     if (is_a($affected, 'PEAR_Error')) return false;
165
166     // Note: we don't mark tt_log or tt_expense_items deleted here.
167     // Reasoning is:
168     //
169     // 1) Users may mark some of them deleted during their work.
170     // If we mark all of them deleted here, we can't recover nicely
171     // as we'll lose track of what was deleted by user.
172     //
173     // 2) DB maintenance script (Clean up DB from inactive teams) should
174     // get rid of these items permanently eventually.
175
176     // Mark group deleted.
177     global $user;
178     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
179     $sql = "update tt_teams set status = NULL $modified_part where id = $group_id";
180     $affected = $mdb2->exec($sql);
181     if (is_a($affected, 'PEAR_Error')) return false;
182
183     return true;
184   }
185
186   // validateTeamInfo validates team information entered by user.
187   function validateTeamInfo($fields) {
188     global $i18n;
189     global $auth;
190
191     $result = true;
192
193     if (!ttValidString($fields['group_name'], true)) {
194       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.team_name'));
195       $result = false;
196     }
197     if (!ttValidString($fields['user_name'])) {
198       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.manager_name'));
199       $result = false;
200     }
201     if (!ttValidString($fields['new_login'])) {
202       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.manager_login'));
203       $result = false;
204     }
205
206     // If we change login, it must be unique.
207     if ($fields['new_login'] != $fields['old_login']) {
208       if (ttUserHelper::getUserByLogin($fields['new_login'])) {
209         $this->err->add($i18n->getKey('error.user_exists'));
210         $result = false;
211       }
212     }
213
214     if (!$auth->isPasswordExternal() && ($fields['password1'] || $fields['password2'])) {
215       if (!ttValidString($fields['password1'])) {
216         $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.password'));
217         $result = false;
218       }
219       if (!ttValidString($fields['password2'])) {
220         $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.confirm_password'));
221         $result = false;
222       }
223       if ($fields['password1'] !== $fields['password2']) {
224         $this->err->add($i18n->getKey('error.not_equal'), $i18n->getKey('label.password'), $i18n->getKey('label.confirm_password'));
225         $result = false;
226       }
227     }
228     if (!ttValidEmail($fields['email'], true)) {
229       $this->err->add($i18n->getKey('error.field'), $i18n->getKey('label.email'));
230       $result = false;
231     }
232
233     return $result;
234   }
235
236   // updateTeam validates user input and updates the team with new information.
237   function updateTeam($team_id, $fields) {
238     if (!$this->validateTeamInfo($fields)) return false; // Can't continue as user input is invalid.
239
240     global $user;
241     $mdb2 = getConnection();
242
243     // Update group name if it changed.
244     if ($fields['old_group_name'] != $fields['new_group_name']) {
245       $name_part = 'name = '.$mdb2->quote($fields['new_group_name']);
246       $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
247       $sql = 'update tt_teams set '.$name_part.$modified_part.' where id = '.$team_id;
248       $affected = $mdb2->exec($sql);
249       if (is_a($affected, 'PEAR_Error')) return false;
250     }
251
252     // Update group manager.
253     $user_id = $fields['user_id'];
254     $login_part = 'login = '.$mdb2->quote($fields['new_login']);
255     if ($fields['password1'])
256       $password_part = ', password = md5('.$mdb2->quote($fields['password1']).')';
257     $name_part = ', name = '.$mdb2->quote($fields['user_name']);
258     $email_part = ', email = '.$mdb2->quote($fields['email']);
259     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
260     $sql = 'update tt_users set '.$login_part.$password_part.$name_part.$email_part.$modified_part.'where id = '.$user_id;
261     $affected = $mdb2->exec($sql);
262     if (is_a($affected, 'PEAR_Error')) return false;
263
264     return true;
265   }
266
267   // setCreatedByAdmin sets created_by field for both group and its top manager to admin account.
268   function setCreatedByAdmin($team_id, $user_id) {
269     global $user;
270     $mdb2 = getConnection();
271
272     // Update created_by for group.
273     $sql = "update tt_teams set created_by = $user->id where id = $team_id";
274     $affected = $mdb2->exec($sql);
275     if (is_a($affected, 'PEAR_Error')) {
276       $this->err->add($i18n->getKey('error.db'));
277       return false;
278     }
279
280     // Update created_by for top manager.
281     $sql = "update tt_users set created_by = $user->id where id = $user_id";
282     $affected = $mdb2->exec($sql);
283     if (is_a($affected, 'PEAR_Error')) {
284       $this->err->add($i18n->getKey('error.db'));
285       return false;
286     }
287
288     return true;
289   }
290 }