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 // +----------------------------------------------------------------------+
30 import('ttRoleHelper');
32 // ttAdmin class is used to perform admin tasks.
35 var $err = null; // Error object, passed to us as reference.
36 // We use it to communicate errors to caller.
39 function __construct(&$err = null) {
43 // getSubgroups rerurns an array of subgroups for a group.
44 static function getSubgroups($group_id) {
45 $mdb2 = getConnection();
48 $sql = "select id from tt_groups where parent_id = $group_id";
49 $res = $mdb2->query($sql);
50 if (!is_a($res, 'PEAR_Error')) {
51 while ($val = $res->fetchRow()) {
58 // markGroupDeleted marks a group and everything in it as deleted.
59 // This function is called in context of a logged on admin who may
60 // operate on any group.
61 static function markGroupDeleted($group_id) {
62 $mdb2 = getConnection();
64 // Keep the logic simple by returning false on first error.
66 // Obtain subgroups and call self recursively on them.
67 $subgroups = ttAdmin::getSubgroups($group_id);
68 foreach($subgroups as $subgroup) {
69 if (!ttAdmin::markGroupDeleted($subgroup['id']))
73 // Now do actual work with all entities.
75 // Some things cannot be marked deleted as we don't have the status field for them.
76 // Just delete such things (until we have a better way to deal with them).
77 $tables_to_delete_from = array(
79 'tt_predefined_expenses',
80 'tt_client_project_binds',
81 'tt_project_task_binds'
83 foreach($tables_to_delete_from as $table) {
84 if (!ttAdmin::deleteGroupEntriesFromTable($group_id, $table))
88 // Now mark status deleted where we can.
89 // Note: we don't mark tt_log, tt_custom_field_lod, or tt_expense_items deleted here.
92 // 1) Users may mark some of them deleted during their work.
93 // If we mark all of them deleted here, we can't recover nicely
94 // as we'll lose track of what was accidentally deleted by user.
96 // 2) DB maintenance script (Clean up DB from inactive groups) should
97 // get rid of these items permanently eventually.
98 $tables_to_mark_deleted_in = array(
101 // 'tt_expense_items',
102 // 'tt_custom_field_log',
103 'tt_custom_field_options',
107 'tt_user_project_binds',
114 foreach($tables_to_mark_deleted_in as $table) {
115 if (!ttAdmin::markGroupDeletedInTable($group_id, $table))
119 // Mark group deleted.
121 $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
122 $sql = "update tt_groups set status = null $modified_part where id = $group_id";
123 $affected = $mdb2->exec($sql);
124 if (is_a($affected, 'PEAR_Error')) return false;
129 // updateGroup updates a (top) group with new information.
130 static function updateGroup($fields) {
131 $group_id = (int)$fields['group_id'];
132 if (!$group_id) return false; // Nothing to update.
134 $mdb2 = getConnection();
136 $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
138 // Update group name if it changed.
139 if ($fields['old_group_name'] != $fields['new_group_name']) {
140 $name_part = 'name = '.$mdb2->quote($fields['new_group_name']);
141 $sql = 'update tt_groups set '.$name_part.$modified_part.' where id = '.$group_id;
142 $affected = $mdb2->exec($sql);
143 if (is_a($affected, 'PEAR_Error')) return false;
146 // Update group manager.
147 $user_id = $fields['user_id'];
148 $login_part = 'login = '.$mdb2->quote($fields['new_login']);
149 if ($fields['password1'])
150 $password_part = ', password = md5('.$mdb2->quote($fields['password1']).')';
151 $name_part = ', name = '.$mdb2->quote($fields['user_name']);
152 $email_part = ', email = '.$mdb2->quote($fields['email']);
153 $sql = 'update tt_users set '.$login_part.$password_part.$name_part.$email_part.$modified_part.'where id = '.$user_id;
154 $affected = $mdb2->exec($sql);
155 if (is_a($affected, 'PEAR_Error')) return false;
160 // validateUserInfo validates account information entered by user.
161 function validateUserInfo($fields) {
168 if (!ttValidString($fields['name'])) {
169 $this->err->add($i18n->get('error.field'), $i18n->get('label.person_name'));
172 if (!ttValidString($fields['login'])) {
173 $this->err->add($i18n->get('error.field'), $i18n->get('label.login'));
176 // If we change login, it must be unique.
177 if ($fields['login'] != $user->login) {
178 if (ttUserHelper::getUserByLogin($fields['login'])) {
179 $this->err->add($i18n->get('error.user_exists'));
183 if (!$auth->isPasswordExternal() && ($fields['password1'] || $fields['password2'])) {
184 if (!ttValidString($fields['password1'])) {
185 $this->err->add($i18n->get('error.field'), $i18n->get('label.password'));
188 if (!ttValidString($fields['password2'])) {
189 $this->err->add($i18n->get('error.field'), $i18n->get('label.confirm_password'));
192 if ($fields['password1'] !== $fields['password2']) {
193 $this->err->add($i18n->get('error.not_equal'), $i18n->get('label.password'), $i18n->get('label.confirm_password'));
197 if (!ttValidEmail($fields['email'], true)) {
198 $this->err->add($i18n->get('error.field'), $i18n->get('label.email'));
205 // updateSelf validates user input and updates admin account with new information.
206 function updateSelf($fields) {
207 if (!$this->validateUserInfo($fields)) return false; // Can't continue as user input is invalid.
211 $mdb2 = getConnection();
214 $user_id = $user->id;
215 $login_part = 'login = '.$mdb2->quote($fields['login']);
216 if ($fields['password1'])
217 $password_part = ', password = md5('.$mdb2->quote($fields['password1']).')';
218 $name_part = ', name = '.$mdb2->quote($fields['name']);
219 $email_part = ', email = '.$mdb2->quote($fields['email']);
220 $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
221 $sql = 'update tt_users set '.$login_part.$password_part.$name_part.$email_part.$modified_part.'where id = '.$user_id;
222 $affected = $mdb2->exec($sql);
223 if (is_a($affected, 'PEAR_Error')) {
224 $this->err->add($i18n->get('error.db'));
231 // getGroupName obtains group name.
232 static function getGroupName($group_id) {
234 $mdb2 = getConnection();
236 $sql = "select name from tt_groups where id = $group_id";
238 $res = $mdb2->query($sql);
239 if (!is_a($res, 'PEAR_Error')) {
240 $val = $res->fetchRow();
247 // getOrgDetails obtains group name and its top manager details.
248 static function getOrgDetails($group_id) {
250 $mdb2 = getConnection();
252 $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".
254 " inner join tt_users u on (u.group_id = g.id)".
255 " inner join tt_roles r on (r.id = u.role_id and r.rank = 512)".
256 " where g.id = $group_id";
258 $res = $mdb2->query($sql);
259 if (!is_a($res, 'PEAR_Error')) {
260 $val = $res->fetchRow();
267 // deleteGroupEntriesFromTable is a generic helper function for markGroupDeleted.
268 // It deletes entries in ONE table belonging to a given group.
269 static function deleteGroupEntriesFromTable($group_id, $table_name) {
270 $mdb2 = getConnection();
272 $sql = "delete from $table_name where group_id = $group_id";
273 $affected = $mdb2->exec($sql);
274 return (!is_a($affected, 'PEAR_Error'));
277 // markGroupDeletedInTable is a generic helper function for markGroupDeleted.
278 // It updates ONE table by setting status to NULL for all records belonging to a group.
279 static function markGroupDeletedInTable($group_id, $table_name) {
280 $mdb2 = getConnection();
282 // Add modified info to sql for some tables, depending on table name.
283 if ($table_name == 'tt_users') {
285 $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
288 $sql = "update $table_name set status = null $modified_part where group_id = $group_id";
289 $affected = $mdb2->exec($sql);
290 return (!is_a($affected, 'PEAR_Error'));
293 // createGroup creates a new top group and returns its id.
294 // It is a helper function for createOrg.
295 static function createGroup($fields) {
297 $mdb2 = getConnection();
299 $name = $mdb2->quote($fields['group_name']);
300 $currency = $mdb2->quote($fields['currency']);
301 $lang = $mdb2->quote($fields['lang']);
303 $created_ip = $mdb2->quote($_SERVER['REMOTE_ADDR']);
304 $created_by = $user->id;
306 $sql = "insert into tt_groups (name, currency, lang, created, created_ip, created_by)".
307 " values($name, $currency, $lang, $created, $created_ip, $created_by)";
308 $affected = $mdb2->exec($sql);
309 if (is_a($affected, 'PEAR_Error')) return false;
311 $group_id = $mdb2->lastInsertID('tt_groups', 'id');
313 // Update org_id with group_id.
314 $sql = "update tt_groups set org_id = $group_id where org_id is NULL and id = $group_id";
315 $affected = $mdb2->exec($sql);
316 if (is_a($affected, 'PEAR_Error')) return false;
321 // createOrgManager creates a new user (top manager role) in a group.
322 // It is a helper function for createOrg.
323 static function createOrgManager($fields) {
325 $mdb2 = getConnection();
327 $role_id = ttRoleHelper::getTopManagerRoleID();
328 $login = $mdb2->quote($fields['login']);
329 $password = 'md5('.$mdb2->quote($fields['password']).')';
330 $name = $mdb2->quote($fields['user_name']);
331 $group_id = (int) $fields['group_id'];
333 $email = $mdb2->quote($fields['email']);
335 $created_ip = $mdb2->quote($_SERVER['REMOTE_ADDR']);
336 $created_by = $user->id;
338 $columns = '(login, password, name, group_id, org_id, role_id, email, created, created_ip, created_by)';
339 $values = "values($login, $password, $name, $group_id, $org_id, $role_id, $email, $created, $created_ip, $created_by)";
341 $sql = "insert into tt_users $columns $values";
342 $affected = $mdb2->exec($sql);
343 return (!is_a($affected, 'PEAR_Error'));
346 // The createOrg function creates an organization in Time Tracker.
347 static function createOrg($fields) {
348 // There are 3 steps that we need to 2 when creating a new organization.
349 // 1. Create a new group with null parent_id.
350 // 2. Create pre-defined roles in it.
351 // 3. Create a top manager account for new group.
353 // Create a new group.
354 $group_id = ttAdmin::createGroup($fields);
355 if (!$group_id) return false;
357 // Create predefined roles.
358 if (!ttRoleHelper::createPredefinedRoles($group_id, $fields['lang']))
362 $fields['group_id'] = $group_id;
363 if (!ttAdmin::createOrgManager($fields))