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 // +----------------------------------------------------------------------+
 
  29 import('ttUserHelper');
 
  30 import('DateAndTime');
 
  31 import('ttInvoiceHelper');
 
  33 // Class ttTeamHelper - contains helper functions that operate with groups.
 
  36   // The getUsersForClient obtains all active and inactive users in a group that are relevant to a client.
 
  37   static function getUsersForClient() {
 
  39     $mdb2 = getConnection();
 
  41     $sql = "select u.id, u.name from tt_user_project_binds upb".
 
  42       " inner join tt_client_project_binds cpb on (upb.project_id = cpb.project_id and cpb.client_id = $user->client_id)".
 
  43       " inner join tt_users u on (u.id = upb.user_id)".
 
  44       " where (u.status = 1 or u.status = 0)".
 
  46       " order by upper(u.name)";
 
  47     $res = $mdb2->query($sql);
 
  49     if (is_a($res, 'PEAR_Error'))
 
  51     while ($val = $res->fetchRow()) {
 
  57   // The getActiveUsers obtains all active users in a given group.
 
  58   static function getActiveUsers($options = null) {
 
  61     $mdb2 = getConnection();
 
  63     if (isset($options['getAllFields']))
 
  64       $sql = "select u.*, r.name as role_name, r.rank from tt_users u left join tt_roles r on (u.role_id = r.id) where u.group_id = $user->group_id and u.status = 1 order by upper(u.name)";
 
  66       $sql = "select id, name from tt_users where group_id = $user->group_id and status = 1 order by upper(name)";
 
  67     $res = $mdb2->query($sql);
 
  69     if (is_a($res, 'PEAR_Error'))
 
  71     while ($val = $res->fetchRow()) {
 
  72       // Localize top manager role name, as it is not localized in db.
 
  73       if ($val['rank'] == 512)
 
  74         $val['role_name'] = $i18n->get('role.top_manager.label');
 
  78     if (isset($options['putSelfFirst'])) {
 
  79       // Put own entry at the front.
 
  80       $cnt = count($user_list);
 
  81       for($i = 0; $i < $cnt; $i++) {
 
  82         if ($user_list[$i]['id'] == $user->id) {
 
  83           $self = $user_list[$i]; // Found self.
 
  84           array_unshift($user_list, $self); // Put own entry at the front.
 
  85           array_splice($user_list, $i+1, 1); // Remove duplicate.
 
  92   // The swapRolesWith swaps existing user role with that of another user.
 
  93   static function swapRolesWith($user_id) {
 
  95     $mdb2 = getConnection();
 
  97     // Obtain role id for the user we are swapping ourselves with.
 
  98     $sql = "select u.id, u.role_id from tt_users u left join tt_roles r on (u.role_id = r.id) where u.id = $user_id and u.group_id = $user->group_id and u.status = 1 and r.rank < $user->rank";
 
  99     $res = $mdb2->query($sql);
 
 100     if (is_a($res, 'PEAR_Error'))
 
 102     $val = $res->fetchRow();
 
 103     if (!$val['id'] || !$val['role_id'])
 
 106     $modified_part = ', modified = now(), modified_ip = '.$mdb2->quote($_SERVER['REMOTE_ADDR']).', modified_by = '.$mdb2->quote($user->id);
 
 109     $sql = "update tt_users set role_id = $user->role_id".$modified_part." where id = $user_id and group_id = $user->group_id";
 
 110     $affected = $mdb2->exec($sql);
 
 111     if (is_a($affected, 'PEAR_Error')) return false;
 
 114     $role_id = $val['role_id'];
 
 115     $sql = "update tt_users set role_id = $role_id".$modified_part." where id = $user->id and group_id = $user->group_id";
 
 116     $affected = $mdb2->exec($sql);
 
 117     if (is_a($affected, 'PEAR_Error')) return false;
 
 122   // The getUsersForSwap obtains all users a current user can swap roles with.
 
 123   static function getUsersForSwap() {
 
 125     $mdb2 = getConnection();
 
 127     $sql = "select u.id, u.name, r.rank, r.rights from tt_users u left join tt_roles r on (u.role_id = r.id) where u.group_id = $user->group_id and u.status = 1 and r.rank < $user->rank order by upper(u.name)";
 
 128     $res = $mdb2->query($sql);
 
 129     $user_list = array();
 
 130     if (is_a($res, 'PEAR_Error'))
 
 132     while ($val = $res->fetchRow()) {
 
 133       $isClient = in_array('track_own_time', explode(',', $val['rights'])) ? 0 : 1; // Clients do not have track_own_time right.
 
 135         continue; // Skip adding clients.
 
 142   // The getUsers obtains all active and inactive (but not deleted) users in a group.
 
 143   static function getUsers() {
 
 145     $mdb2 = getConnection();
 
 146     $sql = "select id, name from tt_users where group_id = $user->group_id and (status = 1 or status = 0) order by upper(name)";
 
 147     $res = $mdb2->query($sql);
 
 148     $user_list = array();
 
 149     if (is_a($res, 'PEAR_Error'))
 
 151     while ($val = $res->fetchRow()) {
 
 157   // The getInactiveUsers obtains all inactive users in a group.
 
 158   static function getInactiveUsers($group_id, $all_fields = false) {
 
 159     $mdb2 = getConnection();
 
 162       $sql = "select u.*, r.name as role_name from tt_users u left join tt_roles r on (u.role_id = r.id) where u.group_id = $group_id and u.status = 0 order by upper(u.name)";
 
 164       $sql = "select id, name from tt_users where group_id = $group_id and status = 0 order by upper(name)";
 
 165     $res = $mdb2->query($sql);
 
 167     if (!is_a($res, 'PEAR_Error')) {
 
 168       while ($val = $res->fetchRow()) {
 
 176   // getActiveProjects - returns an array of active projects for a group.
 
 177   static function getActiveProjects($group_id)
 
 180     $mdb2 = getConnection();
 
 182     $sql = "select id, name, description, tasks from tt_projects
 
 183       where group_id = $group_id and status = 1 order by upper(name)";
 
 184     $res = $mdb2->query($sql);
 
 186     if (!is_a($res, 'PEAR_Error')) {
 
 187       while ($val = $res->fetchRow()) {
 
 194   // getInactiveProjects - returns an array of inactive projects for a group.
 
 195   static function getInactiveProjects($group_id)
 
 198     $mdb2 = getConnection();
 
 200     $sql = "select id, name, description, tasks from tt_projects
 
 201       where group_id = $group_id and status = 0 order by upper(name)";
 
 202     $res = $mdb2->query($sql);
 
 204     if (!is_a($res, 'PEAR_Error')) {
 
 205       while ($val = $res->fetchRow()) {
 
 212   // The getAllProjects obtains all projects in a group.
 
 213   static function getAllProjects($group_id, $all_fields = false) {
 
 214     $mdb2 = getConnection();
 
 217       $sql = "select * from tt_projects where group_id = $group_id order by status, upper(name)";
 
 219       $sql = "select id, name from tt_projects where group_id = $group_id order by status, upper(name)";
 
 220     $res = $mdb2->query($sql);
 
 222     if (!is_a($res, 'PEAR_Error')) {
 
 223       while ($val = $res->fetchRow()) {
 
 231   // getActiveTasks - returns an array of active tasks for a group.
 
 232   static function getActiveTasks($group_id)
 
 235     $mdb2 = getConnection();
 
 237     $sql = "select id, name, description from tt_tasks where group_id = $group_id and status = 1 order by upper(name)";
 
 238     $res = $mdb2->query($sql);
 
 240     if (!is_a($res, 'PEAR_Error')) {
 
 241       while ($val = $res->fetchRow()) {
 
 248   // getInactiveTasks - returns an array of inactive tasks for a group.
 
 249   static function getInactiveTasks($group_id)
 
 252     $mdb2 = getConnection();
 
 254     $sql = "select id, name, description from tt_tasks
 
 255       where group_id = $group_id and status = 0 order by upper(name)";
 
 256     $res = $mdb2->query($sql);
 
 258     if (!is_a($res, 'PEAR_Error')) {
 
 259       while ($val = $res->fetchRow()) {
 
 266   // The getAllTasks obtains all tasks in a group.
 
 267   static function getAllTasks($group_id, $all_fields = false) {
 
 268     $mdb2 = getConnection();
 
 271       $sql = "select * from tt_tasks where group_id = $group_id order by status, upper(name)";
 
 273       $sql = "select id, name from tt_tasks where group_id = $group_id order by status, upper(name)";
 
 274     $res = $mdb2->query($sql);
 
 276     if (!is_a($res, 'PEAR_Error')) {
 
 277       while ($val = $res->fetchRow()) {
 
 285   // getActiveRolesForUser - returns an array of relevant active roles for user with rank less than self.
 
 286   // "Relevant" means that client roles are filtered out if Client plugin is disabled.
 
 287   static function getActiveRolesForUser()
 
 291     $mdb2 = getConnection();
 
 293     $sql = "select id, name, description, rank, rights from tt_roles where group_id = $user->group_id and rank < $user->rank and status = 1 order by rank";
 
 294     $res = $mdb2->query($sql);
 
 296     if (!is_a($res, 'PEAR_Error')) {
 
 297       while ($val = $res->fetchRow()) {
 
 298         $val['is_client'] = in_array('track_own_time', explode(',', $val['rights'])) ? 0 : 1; // Clients do not have data entry right.
 
 299         if ($val['is_client'] && !$user->isPluginEnabled('cl'))
 
 300           continue; // Skip adding a client role.
 
 307   // getActiveRoles - returns an array of active roles for a group.
 
 308   static function getActiveRoles($group_id)
 
 311     $mdb2 = getConnection();
 
 313     $sql = "select id, name, description, rank, rights from tt_roles where group_id = $group_id and status = 1 order by rank";
 
 314     $res = $mdb2->query($sql);
 
 316     if (!is_a($res, 'PEAR_Error')) {
 
 317       while ($val = $res->fetchRow()) {
 
 318         $val['is_client'] = in_array('track_own_time', explode(',', $val['rights'])) ? 0 : 1; // Clients do not have track_own_time right.
 
 325   // getInactiveRoles - returns an array of inactive roles for a group.
 
 326   static function getInactiveRoles($group_id)
 
 329     $mdb2 = getConnection();
 
 331     $sql = "select id, name, rank, description from tt_roles
 
 332       where group_id = $group_id and status = 0 order by rank";
 
 333     $res = $mdb2->query($sql);
 
 335     if (!is_a($res, 'PEAR_Error')) {
 
 336       while ($val = $res->fetchRow()) {
 
 343   // getInactiveRolesForUser - returns an array of relevant active roles for user with rank less than self.
 
 344   // "Relevant" means that client roles are filtered out if Client plugin is disabled.
 
 345   static function getInactiveRolesForUser()
 
 349     $mdb2 = getConnection();
 
 351     $sql = "select id, name, description, rank, rights from tt_roles where group_id = $user->group_id and rank < $user->rank and status = 0 order by rank";
 
 352     $res = $mdb2->query($sql);
 
 354     if (!is_a($res, 'PEAR_Error')) {
 
 355       while ($val = $res->fetchRow()) {
 
 356         $val['is_client'] = in_array('track_own_time', explode(',', $val['rights'])) ? 0 : 1; // Clients do not have data entry right.
 
 357         if ($val['is_client'] && !$user->isPluginEnabled('cl'))
 
 358           continue; // Skip adding a client role.
 
 365   // The getActiveClients returns an array of active clients for a group.
 
 366   static function getActiveClients($group_id, $all_fields = false)
 
 369     $mdb2 = getConnection();
 
 372       $sql = "select * from tt_clients where group_id = $group_id and status = 1 order by upper(name)";
 
 374       $sql = "select id, name from tt_clients where group_id = $group_id and status = 1 order by upper(name)";
 
 376     $res = $mdb2->query($sql);
 
 378     if (!is_a($res, 'PEAR_Error')) {
 
 379       while ($val = $res->fetchRow()) {
 
 386   // The getInactiveClients returns an array of inactive clients for a group.
 
 387   static function getInactiveClients($group_id, $all_fields = false)
 
 390     $mdb2 = getConnection();
 
 393       $sql = "select * from tt_clients where group_id = $group_id and status = 0 order by upper(name)";
 
 395       $sql = "select id, name from tt_clients where group_id = $group_id and status = 0 order by upper(name)";
 
 397     $res = $mdb2->query($sql);
 
 399     if (!is_a($res, 'PEAR_Error')) {
 
 400       while ($val = $res->fetchRow()) {
 
 407   // The getAllClients obtains all clients in a group.
 
 408   static function getAllClients($group_id, $all_fields = false) {
 
 409     $mdb2 = getConnection();
 
 412       $sql = "select * from tt_clients where group_id = $group_id order by status, upper(name)";
 
 414       $sql = "select id, name from tt_clients where group_id = $group_id order by status, upper(name)";
 
 416     $res = $mdb2->query($sql);
 
 418     if (!is_a($res, 'PEAR_Error')) {
 
 419       while ($val = $res->fetchRow()) {
 
 427   // The getActiveInvoices returns an array of active invoices for a group.
 
 428   static function getActiveInvoices($localizeDates = true)
 
 431     $addPaidStatus = $user->isPluginEnabled('ps');
 
 434     $mdb2 = getConnection();
 
 436     if ($user->isClient())
 
 437       $client_part = " and i.client_id = $user->client_id";
 
 439     $sql = "select i.id, i.name, i.date, i.client_id, i.status, c.name as client_name from tt_invoices i
 
 440       left join tt_clients c on (c.id = i.client_id)
 
 441       where i.status = 1 and i.group_id = $user->group_id $client_part order by i.name";
 
 442     $res = $mdb2->query($sql);
 
 444     if (!is_a($res, 'PEAR_Error')) {
 
 445       $dt = new DateAndTime(DB_DATEFORMAT);
 
 446       while ($val = $res->fetchRow()) {
 
 447         if ($localizeDates) {
 
 448           $dt->parseVal($val['date']);
 
 449           $val['date'] = $dt->toString($user->date_format);
 
 452           $val['paid'] = ttInvoiceHelper::isPaid($val['id']);
 
 459   // The getAllInvoices returns an array of all invoices for a group.
 
 460   static function getAllInvoices()
 
 465     $mdb2 = getConnection();
 
 467     $sql = "select * from tt_invoices where group_id = $user->group_id";
 
 468     $res = $mdb2->query($sql);
 
 470     if (!is_a($res, 'PEAR_Error')) {
 
 471       $dt = new DateAndTime(DB_DATEFORMAT);
 
 472       while ($val = $res->fetchRow()) {
 
 479   // The getRecentInvoices returns an array of recent invoices (max 3) for a client.
 
 480   static function getRecentInvoices($group_id, $client_id)
 
 485     $mdb2 = getConnection();
 
 487     $sql = "select i.id, i.name from tt_invoices i
 
 488       left join tt_clients c on (c.id = i.client_id)
 
 489       where i.group_id = $group_id and i.status = 1 and c.id = $client_id
 
 490       order by i.id desc limit 3";
 
 491     $res = $mdb2->query($sql);
 
 493     if (!is_a($res, 'PEAR_Error')) {
 
 494       $dt = new DateAndTime(DB_DATEFORMAT);
 
 495       while ($val = $res->fetchRow()) {
 
 502   // getUserToProjectBinds - obtains all user to project binds for a group.
 
 503   static function getUserToProjectBinds($group_id) {
 
 504     $mdb2 = getConnection();
 
 507     $sql = "select * from tt_user_project_binds where user_id in (select id from tt_users where group_id = $group_id) order by user_id, status, project_id";
 
 508     $res = $mdb2->query($sql);
 
 510     if (!is_a($res, 'PEAR_Error')) {
 
 511       while ($val = $res->fetchRow()) {
 
 519   // The getAllCustomFields obtains all custom fields in a group.
 
 520   static function getAllCustomFields($group_id) {
 
 521     $mdb2 = getConnection();
 
 523     $sql = "select * from tt_custom_fields where group_id = $group_id order by status";
 
 525     $res = $mdb2->query($sql);
 
 527     if (!is_a($res, 'PEAR_Error')) {
 
 528       while ($val = $res->fetchRow()) {
 
 536   // The getAllCustomFieldOptions obtains all custom field options in a group.
 
 537   static function getAllCustomFieldOptions($group_id) {
 
 538     $mdb2 = getConnection();
 
 540     $sql = "select * from tt_custom_field_options where field_id in (select id from tt_custom_fields where group_id = $group_id) order by id";
 
 542     $res = $mdb2->query($sql);
 
 544     if (!is_a($res, 'PEAR_Error')) {
 
 545       while ($val = $res->fetchRow()) {
 
 553   // The getCustomFieldLog obtains all custom field log entries for a group.
 
 554   static function getCustomFieldLog($group_id) {
 
 555     $mdb2 = getConnection();
 
 557     $sql = "select * from tt_custom_field_log where field_id in (select id from tt_custom_fields where group_id = $group_id) order by id";
 
 559     $res = $mdb2->query($sql);
 
 561     if (!is_a($res, 'PEAR_Error')) {
 
 562       while ($val = $res->fetchRow()) {
 
 570   // getFavReports - obtains all favorite reports for all users in a group.
 
 571   static function getFavReports($group_id) {
 
 572     $mdb2 = getConnection();
 
 575     $sql = "select * from tt_fav_reports where user_id in (select id from tt_users where group_id = $group_id)";
 
 576     $res = $mdb2->query($sql);
 
 578     if (!is_a($res, 'PEAR_Error')) {
 
 579       while ($val = $res->fetchRow()) {
 
 587   // getExpenseItems - obtains all expense items for all users in a group.
 
 588   static function getExpenseItems($group_id) {
 
 589     $mdb2 = getConnection();
 
 592     $sql = "select * from tt_expense_items where user_id in (select id from tt_users where group_id = $group_id)";
 
 593     $res = $mdb2->query($sql);
 
 595     if (!is_a($res, 'PEAR_Error')) {
 
 596       while ($val = $res->fetchRow()) {
 
 604   // getPredefinedExpenses - obtains predefined expenses for a group.
 
 605   static function getPredefinedExpenses($group_id) {
 
 607     $replaceDecimalMark = ('.' != $user->decimal_mark);
 
 609     $mdb2 = getConnection();
 
 612     $sql = "select id, name, cost from tt_predefined_expenses where group_id = $group_id";
 
 613     $res = $mdb2->query($sql);
 
 615     if (!is_a($res, 'PEAR_Error')) {
 
 616       while ($val = $res->fetchRow()) {
 
 617         if ($replaceDecimalMark)
 
 618           $val['cost'] = str_replace('.', $user->decimal_mark, $val['cost']);
 
 626   // getNotifications - obtains notification descriptions for a group.
 
 627   static function getNotifications($group_id) {
 
 628     $mdb2 = getConnection();
 
 631     $sql = "select c.id, c.cron_spec, c.email, c.report_condition, fr.name from tt_cron c
 
 632       left join tt_fav_reports fr on (fr.id = c.report_id)
 
 633       where c.group_id = $group_id and c.status = 1 and fr.status = 1";
 
 634     $res = $mdb2->query($sql);
 
 636     if (!is_a($res, 'PEAR_Error')) {
 
 637       while ($val = $res->fetchRow()) {
 
 645   // getMonthlyQuotas - obtains monthly quotas for a group.
 
 646   static function getMonthlyQuotas($group_id) {
 
 647     $mdb2 = getConnection();
 
 650     $sql = "select year, month, minutes from tt_monthly_quotas where group_id = $group_id";
 
 651     $res = $mdb2->query($sql);
 
 653     if (!is_a($res, 'PEAR_Error')) {
 
 654       while ($val = $res->fetchRow()) {
 
 662   // The getInactiveGroups is a maintenance function that returns an array of inactive group ids (max 100).
 
 663   static function getInactiveGroups() {
 
 664     $inactive_groups = array();
 
 665     $mdb2 = getConnection();
 
 667     // Get all group ids for groups created or modified more than 1 year ago.
 
 668     $ts = $mdb2->quote(date('Y-m-d', strtotime('-1 year')));
 
 669     $sql =  "select id from tt_groups where created < $ts and (modified is null or modified < $ts) order by id";
 
 670     $res = $mdb2->query($sql);
 
 673     if (!is_a($res, 'PEAR_Error')) {
 
 674       while ($val = $res->fetchRow()) {
 
 675         $group_id = $val['id'];
 
 676         if (ttTeamHelper::isGroupActive($group_id) == false) {
 
 678           $inactive_groups[] = $group_id;
 
 679           // Limit the array size for perfomance by allowing this operation on small chunks only.
 
 680           if ($count >= 100) break;
 
 683       return $inactive_groups;
 
 688   // The isGroupActive determines if a group is using Time Tracker or abandoned it.
 
 689   static function isGroupActive($group_id) {
 
 690     $mdb2 = getConnection();
 
 693     $ts = date('Y-m-d', strtotime('-2 years'));
 
 694     $sql = "select count(*) as cnt from tt_log where group_id = $group_id and created > '$ts'";
 
 695     $res = $mdb2->query($sql);
 
 696     if (!is_a($res, 'PEAR_Error')) {
 
 697       if ($val = $res->fetchRow()) {
 
 698         $count = $val['cnt'];
 
 703       return false;  // No time entries for the last 2 years.
 
 706       // We will consider a group inactive if it has 5 or less time entries made more than 1 year ago.
 
 707       $count_last_year = 0;
 
 708       $ts = date('Y-m-d', strtotime('-1 year'));
 
 709       $sql = "select count(*) as cnt from tt_log where group_id = $group_id and created > '$ts'";
 
 710       $res = $mdb2->query($sql);
 
 711       if (!is_a($res, 'PEAR_Error')) {
 
 712         if ($val = $res->fetchRow()) {
 
 713           $count_last_year = $val['cnt'];
 
 715         if ($count_last_year == 0)
 
 716           return false;  // No time entries for the last year and only a few entries before that.
 
 722   // The delete function permanently deletes all data for a group.
 
 723   static function delete($group_id) {
 
 724     $mdb2 = getConnection();
 
 727     $sql = "select id from tt_users where group_id = $group_id";
 
 728     $res = $mdb2->query($sql);
 
 729     if (is_a($res, 'PEAR_Error')) return false;
 
 730     while ($val = $res->fetchRow()) {
 
 731       $user_id = $val['id'];
 
 732       if (!ttUserHelper::delete($user_id)) return false;
 
 736     if (!ttTeamHelper::deleteTasks($group_id)) return false;
 
 738     // Delete client to project binds.
 
 739     $sql = "delete from tt_client_project_binds where client_id in (select id from tt_clients where group_id = $group_id)";
 
 740     $affected = $mdb2->exec($sql);
 
 741     if (is_a($affected, 'PEAR_Error')) return false;
 
 744     $sql = "delete from tt_projects where group_id = $group_id";
 
 745     $affected = $mdb2->exec($sql);
 
 746     if (is_a($affected, 'PEAR_Error')) return false;
 
 749     $sql = "delete from tt_clients where group_id = $group_id";
 
 750     $affected = $mdb2->exec($sql);
 
 751     if (is_a($affected, 'PEAR_Error')) return false;
 
 754     $sql = "delete from tt_invoices where group_id = $group_id";
 
 755     $affected = $mdb2->exec($sql);
 
 756     if (is_a($affected, 'PEAR_Error')) return false;
 
 758     // Delete custom fields.
 
 759     if (!ttTeamHelper::deleteCustomFields($group_id)) return false;
 
 762     $sql = "delete from tt_roles where group_id = $group_id";
 
 763     $affected = $mdb2->exec($sql);
 
 764     if (is_a($affected, 'PEAR_Error')) return false;
 
 767     $sql = "delete from tt_groups where id = $group_id";
 
 768     $affected = $mdb2->exec($sql);
 
 769     if (is_a($affected, 'PEAR_Error')) return false;
 
 774   // The deleteTasks deletes all tasks and task binds for an inactive group.
 
 775   static function deleteTasks($group_id) {
 
 776     $mdb2 = getConnection();
 
 777     $sql = "select id from tt_tasks where group_id = $group_id";
 
 778     $res = $mdb2->query($sql);
 
 779     if (is_a($res, 'PEAR_Error')) return false;
 
 781     while ($val = $res->fetchRow()) {
 
 783       // Delete task binds.
 
 784       $task_id = $val['id'];
 
 785       $sql = "delete from tt_project_task_binds where task_id = $task_id";
 
 786       $affected = $mdb2->exec($sql);
 
 787       if (is_a($affected, 'PEAR_Error')) return false;
 
 790       $sql = "delete from tt_tasks where id = $task_id";
 
 791       $affected = $mdb2->exec($sql);
 
 792       if (is_a($affected, 'PEAR_Error')) return false;
 
 798   // The deleteCustomFields cleans up tt_custom_field_log, tt_custom_field_options and tt_custom_fields tables for an inactive group.
 
 799   static function deleteCustomFields($group_id) {
 
 800     $mdb2 = getConnection();
 
 801     $sql = "select id from tt_custom_fields where group_id = $group_id";
 
 802     $res = $mdb2->query($sql);
 
 803     if (is_a($res, 'PEAR_Error')) return false;
 
 805     while ($val = $res->fetchRow()) {
 
 806       $field_id = $val['id'];
 
 808       // Clean up tt_custom_field_log.
 
 809       $sql = "delete from tt_custom_field_log where field_id = $field_id";
 
 810       $affected = $mdb2->exec($sql);
 
 811       if (is_a($affected, 'PEAR_Error')) return false;
 
 813       // Clean up tt_custom_field_options.
 
 814       $sql = "delete from tt_custom_field_options where field_id = $field_id";
 
 815       $affected = $mdb2->exec($sql);
 
 816       if (is_a($affected, 'PEAR_Error')) return false;
 
 818       // Delete custom field.
 
 819       $sql = "delete from tt_custom_fields where id = $field_id";
 
 820       $affected = $mdb2->exec($sql);
 
 821       if (is_a($affected, 'PEAR_Error')) return false;