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.
11 // | There are only two ways to violate the license:
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).
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).
21 // | This license applies to this document only, not any other software
22 // | that it may be combined with.
24 // +----------------------------------------------------------------------+
26 // | https://www.anuko.com/time_tracker/credits.htm
27 // +----------------------------------------------------------------------+
31 // ttAdmin class is used to perform admin tasks.
34 var $err = null; // Error object, passed to us as reference.
35 // We use it to communicate errors to caller.
38 function __construct(&$err = null) {
42 // getSubgroups rerurns an array of subgroups for a group.
43 function getSubgroups($group_id) {
44 return array(); // TODO: not yet implemented.
47 // getUsers obtains user ids in a group.
48 function getUsers($group_id) {
49 $mdb2 = getConnection();
50 $sql = "select id from tt_users where group_id = $group_id";
51 $res = $mdb2->query($sql);
53 if (!is_a($res, 'PEAR_Error')) {
54 while ($val = $res->fetchRow()) {
61 // markUserDeleted marks a user and all things associated with user as deleted.
62 function markUserDeleted($user_id) {
63 $mdb2 = getConnection();
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;
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;
75 // Mark user as deleted.
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;
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 group_id = $group_id";
89 $res = $mdb2->query($sql);
90 if (is_a($res, 'PEAR_Error')) return false;
92 while ($val = $res->fetchRow()) {
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;
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;
108 // markGroupDeleted marks the group and everything in it as deleted.
109 function markGroupDeleted($group_id) {
111 // Keep the logic simple by returning false on first error.
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']))
120 // Now that we are done with subgroups, handle this group.
121 $users = $this->getUsers($group_id);
123 // Iterate through group users and mark them as deleted.
124 foreach ($users as $one_user) {
125 if (!$this->markUserDeleted($one_user['id']))
129 // Mark tasks deleted.
130 if (!$this->markTasksDeleted($group_id)) return false;
132 $mdb2 = getConnection();
134 // Mark roles deleted.
135 $sql = "update tt_roles set status = NULL where group_id = $group_id";
136 $affected = $mdb2->exec($sql);
137 if (is_a($affected, 'PEAR_Error')) return false;
139 // Mark projects deleted.
140 $sql = "update tt_projects set status = NULL where group_id = $group_id";
141 $affected = $mdb2->exec($sql);
142 if (is_a($affected, 'PEAR_Error')) return false;
144 // Mark clients deleted.
145 $sql = "update tt_clients set status = NULL where group_id = $group_id";
146 $affected = $mdb2->exec($sql);
147 if (is_a($affected, 'PEAR_Error')) return false;
149 // Mark invoices deleted.
150 $sql = "update tt_invoices set status = NULL where group_id = $group_id";
151 $affected = $mdb2->exec($sql);
152 if (is_a($affected, 'PEAR_Error')) return false;
154 // Mark custom fields deleted.
155 $sql = "update tt_custom_fields set status = NULL where group_id = $group_id";
156 $affected = $mdb2->exec($sql);
157 if (is_a($affected, 'PEAR_Error')) return false;
159 // Mark notifications deleted.
160 $sql = "update tt_cron set status = NULL where group_id = $group_id";
161 $affected = $mdb2->exec($sql);
162 if (is_a($affected, 'PEAR_Error')) return false;
164 // Note: we don't mark tt_log or tt_expense_items deleted here.
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.
171 // 2) DB maintenance script (Clean up DB from inactive groups) should
172 // get rid of these items permanently eventually.
174 // Mark group deleted.
176 $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
177 $sql = "update tt_groups set status = NULL $modified_part where id = $group_id";
178 $affected = $mdb2->exec($sql);
179 if (is_a($affected, 'PEAR_Error')) return false;
184 // validateGroupInfo validates group information entered by user.
185 function validateGroupInfo($fields) {
191 if (!ttValidString($fields['group_name'], true)) {
192 $this->err->add($i18n->get('error.field'), $i18n->get('label.group_name'));
195 if (!ttValidString($fields['user_name'])) {
196 $this->err->add($i18n->get('error.field'), $i18n->get('label.manager_name'));
199 if (!ttValidString($fields['new_login'])) {
200 $this->err->add($i18n->get('error.field'), $i18n->get('label.manager_login'));
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->get('error.user_exists'));
212 if (!$auth->isPasswordExternal() && ($fields['password1'] || $fields['password2'])) {
213 if (!ttValidString($fields['password1'])) {
214 $this->err->add($i18n->get('error.field'), $i18n->get('label.password'));
217 if (!ttValidString($fields['password2'])) {
218 $this->err->add($i18n->get('error.field'), $i18n->get('label.confirm_password'));
221 if ($fields['password1'] !== $fields['password2']) {
222 $this->err->add($i18n->get('error.not_equal'), $i18n->get('label.password'), $i18n->get('label.confirm_password'));
226 if (!ttValidEmail($fields['email'], true)) {
227 $this->err->add($i18n->get('error.field'), $i18n->get('label.email'));
234 // updateGroup validates user input and updates the group with new information.
235 function updateGroup($group_id, $fields) {
236 if (!$this->validateGroupInfo($fields)) return false; // Can't continue as user input is invalid.
239 $mdb2 = getConnection();
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_groups set '.$name_part.$modified_part.' where id = '.$group_id;
246 $affected = $mdb2->exec($sql);
247 if (is_a($affected, 'PEAR_Error')) return false;
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;
265 // validateUserInfo validates account information entered by user.
266 function validateUserInfo($fields) {
273 if (!ttValidString($fields['name'])) {
274 $this->err->add($i18n->get('error.field'), $i18n->get('label.person_name'));
277 if (!ttValidString($fields['login'])) {
278 $this->err->add($i18n->get('error.field'), $i18n->get('label.login'));
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->get('error.user_exists'));
288 if (!$auth->isPasswordExternal() && ($fields['password1'] || $fields['password2'])) {
289 if (!ttValidString($fields['password1'])) {
290 $this->err->add($i18n->get('error.field'), $i18n->get('label.password'));
293 if (!ttValidString($fields['password2'])) {
294 $this->err->add($i18n->get('error.field'), $i18n->get('label.confirm_password'));
297 if ($fields['password1'] !== $fields['password2']) {
298 $this->err->add($i18n->get('error.not_equal'), $i18n->get('label.password'), $i18n->get('label.confirm_password'));
302 if (!ttValidEmail($fields['email'], true)) {
303 $this->err->add($i18n->get('error.field'), $i18n->get('label.email'));
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.
316 $mdb2 = getConnection();
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->get('error.db'));
336 // getGroupDetails obtains group name and its top manager details.
337 function getGroupDetails($group_id) {
339 $mdb2 = getConnection();
341 $sql = "select g.name as group_name, u.id as manager_id, u.name as manager_name, u.login as manager_login, u.email as manager_email".
343 " inner join tt_users u on (u.group_id = g.id)".
344 " inner join tt_roles r on (r.id = u.role_id and r.rank = 512)".
345 " where g.id = $group_id";
347 $res = $mdb2->query($sql);
348 if (!is_a($res, 'PEAR_Error')) {
349 $val = $res->fetchRow();