+ if (!$this->isPluginEnabled('lk'))
+ return false; // Locking feature is disabled.
+
+ if (!$this->getLockSpec())
+ return false; // There is no lock specification.
+
+ if (!$this->behalf_id && $this->can('override_own_date_lock'))
+ return false; // User is working as self and can override own date lock.
+
+ if ($this->behalf_id && $this->can('override_date_lock'))
+ return false; // User is working on behalf of someone else and can override date lock.
+
+ require_once(LIBRARY_DIR.'/tdcron/class.tdcron.php');
+ require_once(LIBRARY_DIR.'/tdcron/class.tdcron.entry.php');
+
+ // Calculate the last occurrence of a lock.
+ $last = tdCron::getLastOccurrence($this->getLockSpec(), time());
+ $lockdate = new DateAndTime(DB_DATEFORMAT, strftime('%Y-%m-%d', $last));
+ if ($date->before($lockdate))
+ return true;
+
+ return false;
+ }
+
+ // canOverridePunchMode checks whether a user can override punch mode in a situation.
+ function canOverridePunchMode()
+ {
+ if (!$this->behalf_id && !$this->can('override_own_punch_mode'))
+ return false; // User is working as self and cannot override for self.
+
+ if ($this->behalf_id && !$this->can('override_punch_mode'))
+ return false; // User is working on behalf of someone else and cannot override.
+
+ return true;
+ }
+
+ // getUsers obtains users in a group, as specififed by options.
+ function getUsers($options) {
+ $mdb2 = getConnection();
+
+ $group_id = $this->getGroup();
+ $org_id = $this->org_id;
+
+ $skipClients = !isset($options['include_clients']);
+ $includeSelf = isset($options['include_self']);
+
+ $select_part = 'select u.id, u.group_id, u.name';
+ if (isset($options['include_login'])) {
+ $select_part .= ', u.login';
+ // Piggy-back on include_login to see if we must also include quota_percent.
+ $include_quota = $this->isPluginEnabled('mq');
+ if ($include_quota) {
+ $decimal_mark = $this->getDecimalMark();
+ $replaceDecimalMark = ('.' != $decimal_mark);
+ $select_part .= ', u.quota_percent';
+ }
+ }
+ if (!isset($options['include_clients'])) $select_part .= ', r.rights';
+ if (isset($options['include_role'])) $select_part .= ', r.name as role_name, r.rank';
+
+ $from_part = ' from tt_users u';
+
+ $left_joins = null;
+ if (isset($options['max_rank']) || $skipClients || isset($options['include_role']))
+ $left_joins .= ' left join tt_roles r on (u.role_id = r.id)';
+
+ $where_part = " where u.org_id = $org_id and u.group_id = $group_id";
+ if (isset($options['status']))
+ $where_part .= ' and u.status = '.(int)$options['status'];
+ else
+ $where_part .= ' and u.status is not null';
+ if ($includeSelf) {
+ $where_part .= " and (u.id = $this->id || r.rank <= ".(int)$options['max_rank'].')';
+ } else {
+ if (isset($options['max_rank'])) $where_part .= ' and r.rank <= '.(int)$options['max_rank'];
+ }
+
+ $order_part = " order by upper(u.name)";
+
+ $sql = $select_part.$from_part.$left_joins.$where_part.$order_part;
+ $res = $mdb2->query($sql);
+ $user_list = array();
+ if (is_a($res, 'PEAR_Error'))
+ return false;
+
+ while ($val = $res->fetchRow()) {
+ if ($skipClients) {
+ $isClient = in_array('track_own_time', explode(',', $val['rights'])) ? 0 : 1; // Clients do not have track_own_time right.
+ if ($isClient)
+ continue; // Skip adding clients.
+ }
+ if ($include_quota) {
+ $quota = $val['quota_percent'];
+ if (ttEndsWith($quota, '.00'))
+ $quota = substr($quota, 0, strlen($quota)-3); // Trim trailing ".00";
+ elseif ($replaceDecimalMark)
+ $quota = str_replace('.', $decimal_mark, $quota);
+ $val['quota_percent'] = $quota.'%';
+ }
+ $user_list[] = $val;
+ }
+
+ if (isset($options['self_first'])) {
+ // Put own entry at the front.
+ $cnt = count($user_list);
+ for($i = 0; $i < $cnt; $i++) {
+ if ($user_list[$i]['id'] == $this->id) {
+ $self = $user_list[$i]; // Found self.
+ array_unshift($user_list, $self); // Put own entry at the front.
+ array_splice($user_list, $i+1, 1); // Remove duplicate.
+ }
+ }
+ }
+ return $user_list;
+ }
+
+ // getGroupsForDropdown obtains an array of groups to populate "Group" dropdown.
+ // It consists of:
+ // - User home group.
+ // - The entire stack of groups all the way down to current on behalf group.
+ // - All immediate children of the current on behalf group.
+ // This allows user to navigate easily to home group, anything in between, and 1 level below.
+ //
+ // Note 1: group dropdown is, by design, to be placed on all pages where "relevant",
+ // such as users.php, projects.php, tasks.php, etc. But some features may be disabled
+ // in some groups. We should check for feature availability on group change
+ // in post and redirect to feature_disabled.php when this happens.
+ // This will allow us to keep dropdown content consistent on all pages.
+ // Filtering content of the dropdown does not seem right.
+ //
+ // Note 2: Menu should display according to $user home group settings.
+ // Pages, should look according to $user->behalfGroup settings (if set).
+ // For example, if home group allows tasks, menu should display Tasks,
+ // even when we are on behalf of a subgroup without tasks.
+ //
+ // Note 3: Language of all pages should be as in $user home group even when
+ // subgroups have a different language.
+ function getGroupsForDropdown() {
+ $mdb2 = getConnection();
+
+ // Start with subgroups.
+ $groups = array();
+ $group_id = $this->getGroup();
+ $sql = "select id, name from tt_groups where org_id = $this->org_id and parent_id = $group_id and status = 1";
+ $res = $mdb2->query($sql);
+ if (!is_a($res, 'PEAR_Error')) {
+ while ($val = $res->fetchRow()) {
+ $groups[] = $val;
+ }
+ }
+
+ // Add current on behalf group to the beginning of array.
+ $selected_group_id = ($this->behalf_group_id ? $this->behalf_group_id : $this->group_id);
+ $selected_group_name = ($this->behalf_group_id ? $this->behalf_group_name : $this->group_name);
+ array_unshift($groups, array('id'=>$selected_group_id,'name'=>$selected_group_name));
+
+ // Iterate all the way to the home group, starting with selected ("on behalf") group.
+ $current_group_id = $selected_group_id;
+ while ($current_group_id != $this->group_id) {
+ $sql = "select parent_id from tt_groups where org_id = $this->org_id and id = $current_group_id and status = 1";
+ $res = $mdb2->query($sql);
+ if (is_a($res, 'PEAR_Error')) return false;
+
+ $val = $res->fetchRow();
+ $parent_id = $val['parent_id'];
+ if ($parent_id) {
+ // Get parent group name.
+ $sql = "select name from tt_groups where org_id = $this->org_id and id = $parent_id and status = 1";
+ $res = $mdb2->query($sql);
+ if (is_a($res, 'PEAR_Error')) return false;
+ $val = $res->fetchRow();
+ if (!$val) return false;
+ array_unshift($groups, array('id'=>$parent_id,'name'=>$val['name']));
+ $current_group_id = $parent_id;
+ } else {
+ return false;
+ }
+ }
+ return $groups;
+ }
+
+ // getSubgroups obtains a list of immediate subgroups.
+ function getSubgroups($group_id = null) {
+ $mdb2 = getConnection();
+
+ if (!$group_id) $group_id = $this->getGroup();
+
+ $sql = "select id, name, description from tt_groups where org_id = $this->org_id".
+ " and parent_id = $group_id and status is not null order by upper(name)";
+ $res = $mdb2->query($sql);
+ if (!is_a($res, 'PEAR_Error')) {
+ while ($val = $res->fetchRow()) {
+ $groups[] = $val;
+ }
+ }
+ return $groups;
+ }
+
+ // getUserDetails function is used to manage users in group and returns user details.
+ // At the moment, the function is used for user edits and deletes.
+ function getUserDetails($user_id) {
+ if (!$this->can('manage_users')) return false;
+
+ $mdb2 = getConnection();
+ $group_id = $this->getGroup();
+ $org_id = $this->org_id;
+
+ // Determine max rank. If we are searching in on behalf group
+ // then rank restriction does not apply.
+ $max_rank = $this->behalfGroup ? MAX_RANK : $this->rank;
+
+ $sql = "select u.id, u.name, u.login, u.role_id, u.client_id, u.status, u.rate, u.quota_percent, u.email from tt_users u".
+ " left join tt_roles r on (u.role_id = r.id)".
+ " where u.id = $user_id and u.group_id = $group_id and u.org_id = $org_id and u.status is not null".
+ " and (r.rank < $max_rank or (r.rank = $max_rank and u.id = $this->id))"; // Users with lesser roles or self.
+ $res = $mdb2->query($sql);
+ if (!is_a($res, 'PEAR_Error')) {
+ $val = $res->fetchRow();
+ return $val;
+ }
+ return false;
+ }
+
+ // checkBehalfId checks whether behalf_id is appropriate.
+ // On behalf user must be active and have lower rank if the user is from home group,
+ // otherwise:
+ // - subgroup must ve valid;
+ // - user should be a member of it.
+ function checkBehalfId() {
+ if (!$this->behalfGroup) {
+ // Checking user from home group.
+ $options = array('status'=>ACTIVE,'max_rank'=>$this->rank-1);
+ $users = $this->getUsers($options);
+ foreach($users as $one_user) {
+ if ($one_user['id'] == $this->behalf_id)
+ return true;