+    if (!$this->isPluginEnabled('lk'))
+      return false; // Locking feature is disabled.
+
+    if (!$this->lock_spec)
+      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->lock_spec, 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.name';
+    if (isset($options['include_login'])) $select_part .= ', u.login';
+    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.
+      }
+      $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;