/**
 * Handle on_dashboard_sections event
 *
 * @param NamedList $sections
 * @param User $user
 * @return null
 */
function system_handle_on_dashboard_sections(&$sections, &$user)
{
    $sections->add('recent_activities', array('text' => lang('Recent Activities'), 'url' => assemble_url('recent_activities')));
    $sections->add('active_projects', array('text' => lang('Active Projects'), 'url' => assemble_url('active_projects')));
    $count_new = ProjectObjects::countNew($user);
    if ($count_new > 0) {
        $sections->add('new_updated', array('text' => lang('New / Updated <span class="slip">:count</span>', array('count' => $count_new)), 'url' => assemble_url('new_since_last_visit')));
    }
    // if
    $count_late_today = ProjectObjects::countLateAndToday($user, null, get_completable_project_object_types());
    if ($count_late_today > 0) {
        $sections->add('late_today', array('text' => lang('Late / Today <span class="slip">:count</span>', array('count' => $count_late_today)), 'url' => assemble_url('late_today')));
    }
    // if
}
 /**
  * Return project day data
  *
  * @param User $user
  * @param Project $project
  * @param DateValue $day
  * @return array
  */
 function getProjectDayData($user, $project, $day)
 {
     $types = get_completable_project_object_types();
     $filter = ProjectUsers::getVisibleTypesFilterByproject($user, $project, $types);
     if ($filter) {
         $filter .= db_prepare_string(' AND (state >= ? AND visibility >= ?)', array(STATE_VISIBLE, $user->getVisibility()));
         return Calendar::getDayData($day, $filter);
     } else {
         return null;
     }
     // if
 }
 /**
  * Return number of open completable objects in a given project
  *
  * @param Project $project
  * @param integer $min_state
  * @param integer $min_visibility
  * @return integer
  */
 function countOpenCompletableByProject($project, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL)
 {
     $completable = get_completable_project_object_types();
     if (is_array($completable)) {
         return ProjectObjects::count(array('project_id = ? AND type IN (?) AND state >= ? AND visibility >= ? AND completed_on IS NULL', $project->getId(), $completable, $min_state, $min_visibility));
     } else {
         return 0;
     }
     // if
 }
 /**
  * Serve iCal data
  *
  * @param void
  * @return null
  */
 function ical()
 {
     if ($this->active_project->isNew()) {
         $this->httpError(HTTP_ERR_NOT_FOUND);
     }
     // if
     $filter = ProjectUsers::getVisibleTypesFilterByProject($this->logged_user, $this->active_project, get_completable_project_object_types());
     if ($filter) {
         $objects = ProjectObjects::find(array('conditions' => array($filter . ' AND completed_on IS NULL AND state >= ? AND visibility >= ?', STATE_VISIBLE, $this->logged_user->getVisibility()), 'order' => 'priority DESC'));
         render_icalendar($this->active_project->getName() . ' ' . lang('calendar'), $objects);
         die;
     } else {
         $this->httpError(HTTP_ERR_NOT_FOUND);
     }
     // if
 }
 /**
  * Reschedule this milestone
  *
  * @param DateValue $new_start_on
  * @param DateValue $new_due_on
  * @param boolean $reschedule_tasks
  * @return boolean
  */
 function reschedule($new_start_on, $new_due_on, $reschedule_tasks = false)
 {
     $errors = new ValidationErrors();
     if (!instance_of($new_start_on, 'DateValue')) {
         $errors->addError(lang('Start date is not valid'), 'start_on');
     }
     // if
     if (!instance_of($new_due_on, 'DateValue')) {
         $errors->addError(lang('Due date is not valid'), 'start_on');
     }
     // if
     if ($errors->hasErrors()) {
         return $errors;
     }
     // if
     $old_start_on = $this->getStartOn();
     $old_due_on = $this->getDueOn();
     //$this->setStartOn($new_start_on);
     $this->setStartOn(empty($new_start_on->timestamp) ? '' : $new_start_on);
     $this->setDueOn(empty($new_due_on->timestamp) ? '' : $new_due_on);
     $save = $this->save();
     if ($save && !is_error($save)) {
         if ($reschedule_tasks) {
             $diff_days = (int) ceil(($new_due_on->getTimestamp() - $old_due_on->getTimestamp()) / 86400);
             if ($diff_days != 0) {
                 $project_objects_table = TABLE_PREFIX . 'project_objects';
                 $completable_types = get_completable_project_object_types();
                 $rows = db_execute_all("SELECT id FROM {$project_objects_table} WHERE milestone_id = ? AND type IN (?)", $this->getId(), $completable_types);
                 if (is_foreachable($rows)) {
                     $related_object_ids = array();
                     foreach ($rows as $row) {
                         $related_object_ids[] = (int) $row['id'];
                     }
                     // foreach
                     db_execute("UPDATE {$project_objects_table} SET due_on = DATE_ADD(due_on, INTERVAL {$diff_days} DAY) WHERE (id IN (?) OR parent_id IN (?)) AND type IN (?)", $related_object_ids, $related_object_ids, $completable_types);
                 }
                 // if
             }
             // if
         }
         // if
     }
     // if
     return $save;
 }
 /**
  * Copy project items into a destination project
  *
  * @param Project $to
  * @return null
  */
 function copyItems(&$to)
 {
     // Prepare time diff
     $source_starts_on = $this->getStartsOn();
     if (!instance_of($source_starts_on, 'DateValue')) {
         $source_starts_on = $this->getCreatedOn();
     }
     // if
     $target_starts_on = $to->getStartsOn();
     if (!instance_of($target_starts_on, 'DateValue')) {
         $target_starts_on = $to->getCreatedOn();
     }
     // if
     $diff = $target_starts_on->getTimestamp() - $source_starts_on->getTimestamp();
     // Migrate project users
     $project_users = ProjectUsers::findByProject($this);
     if (is_foreachable($project_users)) {
         foreach ($project_users as $project_user) {
             if ($to->getLeaderId() != $project_user->getUserId()) {
                 $user = $project_user->getUser();
                 if (instance_of($user, 'User')) {
                     $to->addUser($user, $project_user->getRole(), $project_user->getPermissions());
                 }
                 // if
             }
             // if
         }
         // foreach
     }
     // if
     // We need to move milestones in order to get milestones map
     $milestones_map = null;
     $milestones = Milestones::findAllByProject($this, VISIBILITY_PRIVATE);
     if (is_foreachable($milestones)) {
         $milestones_map = array();
         foreach ($milestones as $milestone) {
             $copied_milestone = $milestone->copyToProject($to);
             if (instance_of($copied_milestone, 'Milestone')) {
                 $copied_milestone->advance($diff, true);
                 $milestones_map[$milestone->getId()] = $copied_milestone;
             }
             // if
         }
         // foreach
     }
     // if
     // Now move categories
     $categories_map = null;
     $categories = Categories::findByProject($this);
     if (is_foreachable($categories)) {
         foreach ($categories as $category) {
             $copied_category = $category->copyToProject($to, null, null, false);
             if (instance_of($copied_category, 'Category')) {
                 $categories_map[$category->getId()] = $copied_category;
             }
             // if
         }
         // foreach
     }
     // if
     // Let the modules to their thing
     event_trigger('on_copy_project_items', array(&$this, &$to, $milestones_map, $categories_map));
     // Now, lets update due dates
     $completable_types = get_completable_project_object_types();
     if (is_foreachable($completable_types)) {
         foreach ($completable_types as $k => $type) {
             if (strtolower($type) == 'milestone') {
                 unset($completable_types[$k]);
             }
             // if
         }
         // foreach
         if (count($completable_types) > 0) {
             $rows = db_execute_all('SELECT id, due_on FROM ' . TABLE_PREFIX . 'project_objects WHERE project_id = ? AND type IN (?) AND due_on IS NOT NULL', $to->getId(), $completable_types);
             if (is_foreachable($rows)) {
                 foreach ($rows as $row) {
                     $id = (int) $row['id'];
                     $new_date = date(DATE_MYSQL, strtotime($row['due_on']) + $diff);
                     db_execute('UPDATE ' . TABLE_PREFIX . 'project_objects SET due_on = ? WHERE id = ?', $new_date, $id);
                     cache_remove("acx_project_objects_id_{$id}");
                 }
                 // foreach
             }
             // if
         }
         // if
     }
     // if
     // Refresh tasks count, just in case...
     $to->refreshTasksCount();
 }
 /**
  * Prepare conditions based on filter settings
  *
  * @param User $user
  * @return string
  */
 function prepareConditions($user)
 {
     $project_objects_table = TABLE_PREFIX . 'project_objects';
     $assignments_table = TABLE_PREFIX . 'assignments';
     $completable_types = get_completable_project_object_types();
     $conditions = array();
     // Selected projects filter
     if ($this->getProjectFilter() == PROJECT_FILTER_SELECTED) {
         $project_ids = $this->getProjectFilterData();
         if ($project_ids) {
             $conditions[] = db_prepare_string("({$project_objects_table}.project_id IN (?))", array($project_ids));
             $types_filter = ProjectUsers::getVisibleTypesFilter($user, null, $completable_types);
             if ($types_filter) {
                 $conditions[] = $types_filter;
             } else {
                 return false;
                 // No access to any of the projects?
             }
             // if
         }
         // if
     }
     // if
     // All projects
     if (count($conditions) == 0) {
         $types_filter = ProjectUsers::getVisibleTypesFilter($user, array(PROJECT_STATUS_ACTIVE), $completable_types);
         if ($types_filter) {
             $conditions[] = $types_filter;
         } else {
             return false;
             // No access to any of the projects?
         }
         // if
     }
     // if
     // User filter
     switch ($this->getUserFilter()) {
         // Not assigned to anyone
         case USER_FILTER_NOT_ASSIGNED:
             $user_id = $user->getId();
             $conditions[] = "({$assignments_table}.user_id IS NULL)";
             break;
             // Logged user
         // Logged user
         case USER_FILTER_LOGGED_USER:
             $user_id = $user->getId();
             $conditions[] = "({$assignments_table}.user_id = {$user_id})";
             break;
             // Logged user is responsible
         // Logged user is responsible
         case USER_FILTER_LOGGED_USER_RESPONSIBLE:
             $user_id = $user->getId();
             $conditions[] = "({$assignments_table}.user_id = {$user_id}) AND ({$assignments_table}.is_owner = 1)";
             break;
             // All members of a specific company
         // All members of a specific company
         case USER_FILTER_COMPANY:
             $visible_user_ids = $user->visibleUserIds();
             if (!is_foreachable($visible_user_ids)) {
                 return false;
             }
             // if
             $company_id = $this->getUserFilterData();
             if ($company_id) {
                 $company = Companies::findById($company_id);
                 if (instance_of($company, 'Company')) {
                     $user_ids = Users::findUserIdsByCompany($company);
                     if (is_foreachable($user_ids)) {
                         foreach ($user_ids as $k => $v) {
                             if (!in_array($v, $visible_user_ids)) {
                                 unset($user_ids[$k]);
                             }
                             // if
                         }
                         // if
                         if (count($user_ids) > 0) {
                             $imploded = implode(', ', $user_ids);
                             $conditions[] = "({$assignments_table}.user_id IN ({$imploded}))";
                         } else {
                             return false;
                         }
                         // if
                     }
                     // if
                 }
                 // if
             }
             // if
             break;
             // Selected users
         // Selected users
         case USER_FILTER_SELECTED:
             $visible_user_ids = $user->visibleUserIds();
             if (!is_foreachable($visible_user_ids)) {
                 return false;
             }
             // if
             $user_ids = $this->getUserFilterData();
             if (is_foreachable($user_ids)) {
                 foreach ($user_ids as $k => $v) {
                     if (!in_array($v, $visible_user_ids)) {
                         unset($user_ids[$k]);
                     }
                     // if
                 }
                 // foreach
                 if (count($user_ids) > 0) {
                     $imploded = implode(', ', $user_ids);
                     $conditions[] = "({$assignments_table}.user_id IN ({$imploded}))";
                 } else {
                     return false;
                 }
                 // if
             }
             // if
             break;
     }
     // switch
     $today = new DateValue(time() + get_user_gmt_offset($user));
     // Calculate user timezone when determining today
     switch ($this->getDateFilter()) {
         // List late assignments
         case DATE_FILTER_LATE:
             $today_str = db_escape($today->toMySQL());
             $conditions[] = "({$project_objects_table}.due_on < {$today_str})";
             break;
             // List today assignments
         // List today assignments
         case DATE_FILTER_TODAY:
             $today_str = db_escape($today->toMySQL());
             $conditions[] = "({$project_objects_table}.due_on = {$today_str})";
             break;
             // List tomorrow assignments
         // List tomorrow assignments
         case DATE_FILTER_TOMORROW:
             $tomorrow = $today->advance(86400, false);
             $tomorrow_str = db_escape($tomorrow->toMySQL());
             $conditions[] = "({$project_objects_table}.due_on = {$tomorrow_str})";
             break;
             // List this week assignments
         // List this week assignments
         case DATE_FILTER_THIS_WEEK:
             $first_day_sunday = UserConfigOptions::getValue('time_first_week_day', $user) == 0;
             $week_start = $today->beginningOfWeek($first_day_sunday);
             $week_end = $today->endOfWeek($first_day_sunday);
             $week_start_str = db_escape($week_start->toMySQL());
             $week_end_str = db_escape($week_end->toMySQL());
             $conditions[] = "({$project_objects_table}.due_on >= {$week_start_str} AND {$project_objects_table}.due_on <= {$week_end_str})";
             break;
             // List next week assignments
         // List next week assignments
         case DATE_FILTER_NEXT_WEEK:
             $first_day_sunday = UserConfigOptions::getValue('time_first_week_day', $user) == 0;
             $next_week = $today->advance(604800, false);
             $week_start = $next_week->beginningOfWeek($first_day_sunday);
             $week_end = $next_week->endOfWeek($first_day_sunday);
             $week_start_str = db_escape($week_start->toMySQL());
             $week_end_str = db_escape($week_end->toMySQL());
             $conditions[] = "({$project_objects_table}.due_on >= {$week_start_str} AND {$project_objects_table}.due_on <= {$week_end_str})";
             break;
             // List this month assignments
         // List this month assignments
         case DATE_FILTER_THIS_MONTH:
             $month_start = DateTimeValue::beginningOfMonth($today->getMonth(), $today->getYear());
             $month_end = DateTimeValue::endOfMonth($today->getMonth(), $today->getYear());
             $month_start_str = db_escape($month_start->toMySQL());
             $month_end_str = db_escape($month_end->toMySQL());
             $conditions[] = "({$project_objects_table}.due_on >= {$month_start_str} AND {$project_objects_table}.due_on <= {$month_end_str})";
             break;
             // List next month assignments
         // List next month assignments
         case DATE_FILTER_NEXT_MONTH:
             $month = $today->getMonth() + 1;
             $year = $today->getYear();
             if ($month == 13) {
                 $month = 1;
                 $year += 1;
             }
             // if
             $month_start = DateTimeValue::beginningOfMonth($month, $year);
             $month_end = DateTimeValue::endOfMonth($month, $year);
             $month_start_str = db_escape($month_start->toMySQL());
             $month_end_str = db_escape($month_end->toMySQL());
             $conditions[] = "({$project_objects_table}.due_on >= {$month_start_str} AND {$project_objects_table}.due_on <= {$month_end_str})";
             break;
             // Specific date
         // Specific date
         case DATE_FILTER_SELECTED_DATE:
             $date_from = $this->getDateFrom();
             if (instance_of($date_from, 'DateTimeValue')) {
                 $date_from_str = db_escape($date_from->toMySql());
                 $conditions[] = "({$project_objects_table}.due_on = {$date_from_str})";
             }
             // if
             break;
             // Specific range
         // Specific range
         case DATE_FILTER_SELECTED_RANGE:
             $date_from = $this->getDateFrom();
             $date_to = $this->getDateTo();
             if (instance_of($date_from, 'DateValue') && instance_of($date_to, 'DateValue')) {
                 $date_from_str = db_escape($date_from->toMySQL());
                 $date_to_str = db_escape($date_to->toMySQL());
                 $conditions[] = "({$project_objects_table}.due_on >= {$date_from_str} AND {$project_objects_table}.due_on <= {$date_to_str})";
             }
             // if
             break;
     }
     // switch
     // Status filter
     switch ($this->getStatusFilter()) {
         case STATUS_FILTER_ACTIVE:
             $conditions[] = "({$project_objects_table}.completed_on IS NULL)";
             break;
         case STATUS_FILTER_COMPLETED:
             $conditions[] = "({$project_objects_table}.completed_on IS NOT NULL)";
             break;
     }
     // if
     // Additional filters
     $state = STATE_VISIBLE;
     $visibility = $user->getVisibility();
     $conditions[] = "({$project_objects_table}.state >= {$state} AND {$project_objects_table}.visibility >= {$visibility})";
     return implode(' AND ', $conditions);
 }
 /**
  * Render global iCalendar feed
  *
  * @param void
  * @return null
  */
 function ical()
 {
     $filter = ProjectUsers::getVisibleTypesFilter($this->logged_user, array(PROJECT_STATUS_ACTIVE), get_completable_project_object_types());
     if ($filter) {
         $objects = ProjectObjects::find(array('conditions' => array($filter . ' AND completed_on IS NULL AND state >= ? AND visibility >= ?', STATE_VISIBLE, $this->logged_user->getVisibility()), 'order' => 'priority DESC'));
         render_icalendar(lang('Global Calendar'), $objects, true);
         die;
     } elseif ($this->request->get('subscribe')) {
         flash_error(lang('You are not able to download .ics file because you are not participating in any of the active projects at the moment'));
         $this->redirectTo('ical_subscribe');
     } else {
         $this->httpError(HTTP_ERR_NOT_FOUND);
     }
     // if
 }