2213 lines
116 KiB
PHP
2213 lines
116 KiB
PHP
<?php
|
|
|
|
use app\services\projects\Gantt;
|
|
use app\services\projects\AllProjectsGantt;
|
|
use app\services\projects\HoursOverviewChart;
|
|
|
|
defined('BASEPATH') or exit('No direct script access allowed');
|
|
|
|
class Projects_model extends App_Model {
|
|
private $project_settings;
|
|
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$project_settings = ['available_features', 'view_tasks', 'create_tasks', 'edit_tasks', 'comment_on_tasks', 'view_task_comments', 'view_task_attachments', 'view_task_checklist_items', 'upload_on_tasks', 'view_task_total_logged_time', 'view_finance_overview', 'upload_files', 'open_discussions', 'view_milestones', 'view_gantt', 'view_timesheets', 'view_activity_log', 'view_team_members', 'hide_tasks_on_main_tasks_table', ];
|
|
|
|
$this->project_settings = hooks()->apply_filters('project_settings', $project_settings);
|
|
}
|
|
|
|
public function get_project_statuses() {
|
|
$statuses = hooks()->apply_filters('before_get_project_statuses', [
|
|
['id' => 1, 'color' => '#475569', 'name' => _l('project_status_1'), 'order' => 1, 'filter_default' => true, ],
|
|
['id' => 2, 'color' => '#2563eb', 'name' => _l('project_status_2'), 'order' => 2, 'filter_default' => true, ],
|
|
['id' => 3, 'color' => '#f97316', 'name' => _l('project_status_3'), 'order' => 3, 'filter_default' => true, ],
|
|
['id' => 4, 'color' => '#16a34a', 'name' => _l('project_status_4'), 'order' => 100, 'filter_default' => false, ],
|
|
['id' => 5, 'color' => '#94a3b8', 'name' => _l('project_status_5'), 'order' => 4, 'filter_default' => false, ],
|
|
]);
|
|
usort($statuses, function ($a, $b) {
|
|
return $a['order'] - $b['order'];
|
|
});
|
|
return $statuses;
|
|
}
|
|
|
|
public function get_distinct_tasks_timesheets_staff($project_id, $playground = false) {
|
|
return $this->db->query('SELECT DISTINCT staff_id FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'taskstimers LEFT JOIN ' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks ON ' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'taskstimers.task_id WHERE rel_type="project" AND rel_id=' . $this->db->escape_str($project_id))->result_array();
|
|
}
|
|
|
|
public function get_distinct_projects_members($playground = false) {
|
|
return $this->db->query('SELECT staff_id, firstname, lastname FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'project_members JOIN ' . db_prefix() . ($playground ? 'playground_' : '') . 'staff ON ' . db_prefix() . ($playground ? 'playground_' : '') . 'staff.staffid=' . db_prefix() . ($playground ? 'playground_' : '') . 'project_members.staff_id GROUP by staff_id order by firstname ASC')->result_array();
|
|
}
|
|
|
|
public function get_most_used_billing_type($playground = false) {
|
|
return $this->db->query('SELECT billing_type, COUNT(*) AS total_usage
|
|
FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'projects
|
|
GROUP BY billing_type
|
|
ORDER BY total_usage DESC
|
|
LIMIT 1')->row();
|
|
}
|
|
|
|
public function timers_started_for_project($project_id, $where = [], $task_timers_where = [], $playground = false) {
|
|
$this->db->where($where);
|
|
$this->db->where('end_time IS NULL');
|
|
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tasks.rel_id', $project_id);
|
|
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tasks.rel_type', 'project');
|
|
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', db_prefix() . ($playground ? 'playground_' : '') . 'tasks.id=' . db_prefix() . ($playground ? 'playground_' : '') . 'taskstimers.task_id');
|
|
$total = $this->db->count_all_results(db_prefix() . ($playground ? 'playground_' : '') . 'taskstimers');
|
|
return $total > 0 ? true : false;
|
|
}
|
|
|
|
public function pin_action($id, $playground = false) {
|
|
if (total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'pinned_projects', ['staff_id' => get_staff_user_id(), 'project_id' => $id, ]) == 0) {
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'pinned_projects', ['staff_id' => get_staff_user_id(), 'project_id' => $id, ]);
|
|
return true;
|
|
}
|
|
$this->db->where('project_id', $id);
|
|
$this->db->where('staff_id', get_staff_user_id());
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'pinned_projects');
|
|
return true;
|
|
}
|
|
|
|
public function get_currency($id, $playground = false) {
|
|
$this->load->model('currencies_model');
|
|
$this->load->model('clients_model');
|
|
$customer_currency = $this->clients_model->get_customer_default_currency(get_client_id_by_project_id($id, $playground), $playground);
|
|
if ($customer_currency != 0) {
|
|
$currency = $this->currencies_model->get($customer_currency, $playground);
|
|
} else {
|
|
$currency = $this->currencies_model->get_base_currency($playground);
|
|
}
|
|
return $currency;
|
|
}
|
|
|
|
public function calc_progress($id, $playground = false) {
|
|
$this->db->select('progress_from_tasks,progress,status');
|
|
$this->db->where('id', $id);
|
|
$project = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->row();
|
|
if ($project->status == 4) {
|
|
return 100;
|
|
}
|
|
if ($project->progress_from_tasks == 1) {
|
|
return $this->calc_progress_by_tasks($id, $playground);
|
|
}
|
|
return $project->progress;
|
|
}
|
|
|
|
public function calc_progress_by_tasks($id, $playground = false) {
|
|
$total_project_tasks = total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['rel_type' => 'project', 'rel_id' => $id, ]);
|
|
$total_finished_tasks = total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['rel_type' => 'project', 'rel_id' => $id, 'status' => 5, ]);
|
|
$percent = 0;
|
|
if ($total_finished_tasks >= floatval($total_project_tasks)) {
|
|
$percent = 100;
|
|
} else {
|
|
if ($total_project_tasks !== 0) {
|
|
$percent = number_format(($total_finished_tasks * 100) / $total_project_tasks, 2);
|
|
}
|
|
}
|
|
return $percent;
|
|
}
|
|
|
|
public function get_last_project_settings($playground = false) {
|
|
$this->db->select('id');
|
|
$this->db->order_by('id', 'DESC');
|
|
$this->db->limit(1);
|
|
$last_project = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->row();
|
|
if ($last_project) {
|
|
return $this->get_project_settings($last_project->id, $playground);
|
|
}
|
|
return [];
|
|
}
|
|
|
|
public function get_settings() {
|
|
return $this->project_settings;
|
|
}
|
|
|
|
public function get($id = '', $where = [], $playground = false) {
|
|
$this->load->model('clients_model');
|
|
$this->db->where($where);
|
|
if (is_numeric($id)) {
|
|
$this->db->where('id', $id);
|
|
$project = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->row();
|
|
if ($project) {
|
|
$project->shared_vault_entries = $this->clients_model->get_vault_entries($project->clientid, ['share_in_projects' => 1]);
|
|
$settings = $this->get_project_settings($id, $playground);
|
|
// SYNC NEW TABS
|
|
$tabs = get_project_tabs_admin();
|
|
$tabs_flatten = [];
|
|
$settings_available_features = [];
|
|
$available_features_index = false;
|
|
foreach ($settings as $key => $setting) {
|
|
if ($setting['name'] == 'available_features') {
|
|
$available_features_index = $key;
|
|
$available_features = unserialize($setting['value']);
|
|
if (is_array($available_features)) {
|
|
foreach ($available_features as $name => $avf) {
|
|
$settings_available_features[] = $name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach ($tabs as $tab) {
|
|
if (isset($tab['collapse'])) {
|
|
foreach ($tab['children'] as $d) {
|
|
$tabs_flatten[] = $d['slug'];
|
|
}
|
|
} else {
|
|
$tabs_flatten[] = $tab['slug'];
|
|
}
|
|
}
|
|
if (count($settings_available_features) != $tabs_flatten) {
|
|
foreach ($tabs_flatten as $tab) {
|
|
if (!in_array($tab, $settings_available_features)) {
|
|
if ($available_features_index) {
|
|
$current_available_features_settings = $settings[$available_features_index];
|
|
$tmp = unserialize($current_available_features_settings['value']);
|
|
$tmp[$tab] = 1;
|
|
$this->db->where('id', $current_available_features_settings['id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'project_settings', ['value' => serialize($tmp) ]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$project->settings = new StdClass();
|
|
foreach ($settings as $setting) {
|
|
$project->settings->{$setting['name']} = $setting['value'];
|
|
}
|
|
$project->client_data = new StdClass();
|
|
$project->client_data = $this->clients_model->get($project->clientid, $playground);
|
|
$project = hooks()->apply_filters('project_get', $project);
|
|
$GLOBALS['project'] = $project;
|
|
return $project;
|
|
}
|
|
return null;
|
|
}
|
|
$this->db->select('*,' . $this->clients_model->get_sql_select_client_company('company', $playground));
|
|
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'clients', db_prefix() . ($playground ? 'playground_' : '') . 'clients.userid=' . db_prefix() . ($playground ? 'playground_' : '') . 'projects.clientid');
|
|
$this->db->order_by('id', 'desc');
|
|
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->result_array();
|
|
}
|
|
|
|
public function get_project($id = '', $playground = false) {
|
|
if (is_numeric($id)) {
|
|
return $this->get($id, [], $playground);
|
|
} else {
|
|
$result = [];
|
|
$projects = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->result_array();
|
|
foreach ($projects as $project) {
|
|
$result[] = (array)$this->get($project['id'], [], $playground);
|
|
}
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
public function calculate_total_by_project_hourly_rate($seconds, $hourly_rate, $playground = false) {
|
|
$hours = seconds_to_time_format($seconds);
|
|
$decimal = sec2qty($seconds);
|
|
$total_money = 0;
|
|
$total_money+= ($decimal * $hourly_rate);
|
|
return ['hours' => $hours, 'total_money' => $total_money, ];
|
|
}
|
|
|
|
public function calculate_total_by_task_hourly_rate($tasks, $playground = false) {
|
|
$total_money = 0;
|
|
$_total_seconds = 0;
|
|
foreach ($tasks as $task) {
|
|
$seconds = $task['total_logged_time'];
|
|
$_total_seconds+= $seconds;
|
|
$total_money+= sec2qty($seconds) * $task['hourly_rate'];
|
|
}
|
|
return ['total_money' => $total_money, 'total_seconds' => $_total_seconds, ];
|
|
}
|
|
|
|
public function get_tasks($id, $where = [], $apply_restrictions = false, $count = false, $callback = null, $playground = false) {
|
|
$has_permission = staff_can('view', 'tasks');
|
|
$show_all_tasks_for_project_member = get_option('show_all_tasks_for_project_member');
|
|
$select = implode(', ', prefixed_table_fields_array(db_prefix() . ($playground ? 'playground_' : '') . 'tasks')) . ',' . db_prefix() . ($playground ? 'playground_' : '') . 'milestones.name as milestone_name,
|
|
(SELECT SUM(CASE
|
|
WHEN end_time is NULL THEN ' . time() . '-start_time
|
|
ELSE end_time-start_time
|
|
END) FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'taskstimers WHERE task_id=' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks.id) as total_logged_time,
|
|
' . get_sql_select_task_assignees_ids($playground) . ' as assignees_ids
|
|
';
|
|
if (!is_client_logged_in() && is_staff_logged_in()) {
|
|
$select.= ',(SELECT staffid FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'task_assigned WHERE taskid=' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks.id AND staffid=' . get_staff_user_id() . ') as current_user_is_assigned';
|
|
}
|
|
if (is_client_logged_in()) {
|
|
$this->db->where('visible_to_client', 1);
|
|
}
|
|
$this->db->select($select);
|
|
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'milestones', db_prefix() . ($playground ? 'playground_' : '') . 'milestones.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks.milestone', 'left');
|
|
$this->db->where('rel_id', $id);
|
|
$this->db->where('rel_type', 'project');
|
|
if ($apply_restrictions == true) {
|
|
if (!is_client_logged_in() && !$has_permission && $show_all_tasks_for_project_member == 0) {
|
|
$this->db->where('(
|
|
' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks.id IN (SELECT taskid FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'task_assigned WHERE staffid=' . get_staff_user_id() . ')
|
|
OR ' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks.id IN(SELECT taskid FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'task_followers WHERE staffid=' . get_staff_user_id() . ')
|
|
OR is_public = 1
|
|
OR (addedfrom =' . get_staff_user_id() . ' AND is_added_from_contact = 0)
|
|
)');
|
|
}
|
|
}
|
|
if (isset($where[db_prefix() . ($playground ? 'playground_' : '') . 'milestones.hide_from_customer'])) {
|
|
$this->db->group_start();
|
|
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'milestones.hide_from_customer', $where[db_prefix() . ($playground ? 'playground_' : '') . 'milestones.hide_from_customer']);
|
|
$this->db->or_where(db_prefix() . ($playground ? 'playground_' : '') . 'tasks.milestone', 0);
|
|
$this->db->group_end();
|
|
unset($where[db_prefix() . ($playground ? 'playground_' : '') . 'milestones.hide_from_customer']);
|
|
}
|
|
$this->db->where($where);
|
|
// Milestones kanban order
|
|
// Request is admin/projects/milestones_kanban
|
|
if ($this->uri->segment(3) == 'milestones_kanban' | $this->uri->segment(3) == 'milestones_kanban_load_more') {
|
|
$this->db->order_by('milestone_order', 'asc');
|
|
} else {
|
|
$orderByString = hooks()->apply_filters('project_tasks_array_default_order', 'FIELD(status, 5), duedate IS NULL ASC, duedate');
|
|
$this->db->order_by($orderByString, '', false);
|
|
}
|
|
if ($callback) {
|
|
$callback();
|
|
}
|
|
if ($count == false) {
|
|
$tasks = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tasks')->result_array();
|
|
} else {
|
|
$tasks = $this->db->count_all_results(db_prefix() . ($playground ? 'playground_' : '') . 'tasks');
|
|
}
|
|
$tasks = hooks()->apply_filters('get_projects_tasks', $tasks, ['project_id' => $id, 'where' => $where, 'count' => $count, ]);
|
|
return $tasks;
|
|
}
|
|
|
|
public function cancel_recurring_tasks($id, $playground = false) {
|
|
$this->db->where('rel_type', 'project');
|
|
$this->db->where('rel_id', $id);
|
|
$this->db->where('recurring', 1);
|
|
$this->db->where('(cycles != total_cycles OR cycles=0)');
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['recurring_type' => null, 'repeat_every' => 0, 'cycles' => 0, 'recurring' => 0, 'custom_recurring' => 0, 'last_recurring_date' => null, ]);
|
|
}
|
|
|
|
public function do_milestones_kanban_query($milestone_id, $project_id, $page = 1, $where = [], $count = false, $playground = false) {
|
|
$where['milestone'] = $milestone_id;
|
|
$limit = get_option('tasks_kanban_limit');
|
|
$tasks = $this->get_tasks($project_id, $where, true, $count, function () use ($count, $page, $limit) {
|
|
if ($count == false) {
|
|
if ($page > 1) {
|
|
$position = (($page - 1) * $limit);
|
|
$this->db->limit($limit, $position);
|
|
} else {
|
|
$this->db->limit($limit);
|
|
}
|
|
}
|
|
});
|
|
return $tasks;
|
|
}
|
|
|
|
public function get_files($project_id, $playground = false) {
|
|
if (is_client_logged_in()) {
|
|
$this->db->where('visible_to_customer', 1);
|
|
}
|
|
$this->db->where('project_id', $project_id);
|
|
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_files')->result_array();
|
|
}
|
|
|
|
public function get_file($id, $project_id = false, $playground = false) {
|
|
if (is_client_logged_in()) {
|
|
$this->db->where('visible_to_customer', 1);
|
|
}
|
|
$this->db->where('id', $id);
|
|
$file = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_files')->row();
|
|
if ($file && $project_id) {
|
|
if ($file->project_id != $project_id) {
|
|
return false;
|
|
}
|
|
}
|
|
return $file;
|
|
}
|
|
|
|
public function update_file_data($data, $playground = false) {
|
|
$this->db->where('id', $data['id']);
|
|
unset($data['id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'project_files', $data);
|
|
}
|
|
|
|
public function change_file_visibility($id, $visible, $playground = false) {
|
|
$this->db->where('id', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'project_files', ['visible_to_customer' => $visible, ]);
|
|
}
|
|
|
|
public function change_activity_visibility($id, $visible, $playground = false) {
|
|
$this->db->where('id', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'project_activity', ['visible_to_customer' => $visible, ]);
|
|
}
|
|
|
|
public function remove_file($id, $logActivity = true, $playground = false) {
|
|
hooks()->do_action('before_remove_project_file', $id);
|
|
$this->db->where('id', $id);
|
|
$file = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_files')->row();
|
|
$this->load->model('misc_model');
|
|
if ($file) {
|
|
if (empty($file->external)) {
|
|
$path = $this->misc_model->get_upload_path_by_type('project', $playground) . $file->project_id . '/';
|
|
$fullPath = $path . $file->file_name;
|
|
if (file_exists($fullPath)) {
|
|
unlink($fullPath);
|
|
$fname = pathinfo($fullPath, PATHINFO_FILENAME);
|
|
$fext = pathinfo($fullPath, PATHINFO_EXTENSION);
|
|
$thumbPath = $path . $fname . '_thumb.' . $fext;
|
|
if (file_exists($thumbPath)) {
|
|
unlink($thumbPath);
|
|
}
|
|
}
|
|
}
|
|
$this->db->where('id', $id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'project_files');
|
|
if ($logActivity) {
|
|
$this->log_activity($file->project_id, 'project_activity_project_file_removed', $file->file_name, $file->visible_to_customer, $playground);
|
|
}
|
|
// Delete discussion comments
|
|
$this->_delete_discussion_comments($id, 'file', $playground);
|
|
if (is_dir($this->misc_model->get_upload_path_by_type('project', $playground) . $file->project_id)) {
|
|
// Check if no attachments left, so we can delete the folder also
|
|
$other_attachments = list_files($this->misc_model->get_upload_path_by_type('project', $playground) . $file->project_id);
|
|
if (count($other_attachments) == 0) {
|
|
delete_dir($this->misc_model->get_upload_path_by_type('project', $playground) . $file->project_id);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function calc_milestone_logged_time($project_id, $id, $playground = false) {
|
|
$total = [];
|
|
$tasks = $this->get_tasks($project_id, ['milestone' => $id, ], false, false, null, $playground);
|
|
foreach ($tasks as $task) {
|
|
$total[] = $task['total_logged_time'];
|
|
}
|
|
return array_sum($total);
|
|
}
|
|
|
|
public function total_logged_time($id, $playground = false) {
|
|
$q = $this->db->query('
|
|
SELECT SUM(CASE
|
|
WHEN end_time is NULL THEN ' . time() . '-start_time
|
|
ELSE end_time-start_time
|
|
END) as total_logged_time
|
|
FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'taskstimers
|
|
WHERE task_id IN (SELECT id FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks WHERE rel_type="project" AND rel_id=' . $this->db->escape_str($id) . ')')->row();
|
|
return $q->total_logged_time;
|
|
}
|
|
|
|
public function get_milestones($project_id, $where = [], $playground = false) {
|
|
$this->db->select('*, (SELECT COUNT(id) FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks WHERE rel_type="project" AND rel_id=' . $this->db->escape_str($project_id) . ' and milestone=' . db_prefix() . ($playground ? 'playground_' : '') . 'milestones.id) as total_tasks, (SELECT COUNT(id) FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks WHERE rel_type="project" AND rel_id=' . $this->db->escape_str($project_id) . ' and milestone=' . db_prefix() . ($playground ? 'playground_' : '') . 'milestones.id AND status=5) as total_finished_tasks');
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->order_by('milestone_order', 'ASC');
|
|
$this->db->where($where);
|
|
$milestones = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'milestones')->result_array();
|
|
$i = 0;
|
|
foreach ($milestones as $milestone) {
|
|
$milestones[$i]['total_logged_time'] = $this->calc_milestone_logged_time($project_id, $milestone['id'], $playground);
|
|
$i++;
|
|
}
|
|
return $milestones;
|
|
}
|
|
|
|
public function add_milestone($data, $playground = false) {
|
|
$data['due_date'] = to_sql_date($data['due_date']);
|
|
$data['start_date'] = to_sql_date($data['start_date']);
|
|
$data['datecreated'] = date('Y-m-d');
|
|
$data['description'] = nl2br($data['description']);
|
|
$data['description_visible_to_customer'] = isset($data['description_visible_to_customer']) ? 1 : 0;
|
|
$data['hide_from_customer'] = isset($data['hide_from_customer']) ? 1 : 0;
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'milestones', $data);
|
|
$insert_id = $this->db->insert_id();
|
|
if ($insert_id) {
|
|
$this->db->where('id', $insert_id);
|
|
$milestone = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'milestones')->row();
|
|
$project = $this->get($milestone->project_id);
|
|
if ($project->settings->view_milestones == 1) {
|
|
$show_to_customer = 1;
|
|
} else {
|
|
$show_to_customer = 0;
|
|
}
|
|
$this->log_activity($milestone->project_id, 'project_activity_created_milestone', $milestone->name, $show_to_customer);
|
|
log_activity('Project Milestone Created [ID:' . $insert_id . ']');
|
|
return $insert_id;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function update_milestone($data, $id, $playground = false) {
|
|
$this->db->where('id', $id);
|
|
$milestone = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'milestones')->row();
|
|
$data['due_date'] = to_sql_date($data['due_date']);
|
|
$data['start_date'] = to_sql_date($data['start_date']);
|
|
$data['description'] = nl2br($data['description']);
|
|
$data['description_visible_to_customer'] = isset($data['description_visible_to_customer']) ? 1 : 0;
|
|
$data['hide_from_customer'] = isset($data['hide_from_customer']) ? 1 : 0;
|
|
$this->db->where('id', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'milestones', $data);
|
|
if ($this->db->affected_rows() > 0) {
|
|
$project = $this->get($milestone->project_id);
|
|
if ($project->settings->view_milestones == 1) {
|
|
$show_to_customer = 1;
|
|
} else {
|
|
$show_to_customer = 0;
|
|
}
|
|
$this->log_activity($milestone->project_id, 'project_activity_updated_milestone', $milestone->name, $show_to_customer, $playground);
|
|
log_activity('Project Milestone Updated [ID:' . $id . ']');
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function update_task_milestone($data, $playground = false) {
|
|
$this->db->where('id', $data['task_id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['milestone' => $data['milestone_id'], ]);
|
|
foreach ($data['order'] as $order) {
|
|
$this->db->where('id', $order[0]);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['milestone_order' => $order[1], ]);
|
|
}
|
|
}
|
|
|
|
public function update_milestones_order($data, $playground = false) {
|
|
foreach ($data['order'] as $status) {
|
|
$this->db->where('id', $status[0]);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'milestones', ['milestone_order' => $status[1], ]);
|
|
}
|
|
}
|
|
|
|
public function update_milestone_color($data, $playground = false) {
|
|
$this->db->where('id', $data['milestone_id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'milestones', ['color' => $data['color'], ]);
|
|
}
|
|
|
|
public function delete_milestone($id, $playground = false) {
|
|
$this->db->where('id', $id);
|
|
$milestone = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'milestones')->row();
|
|
$this->db->where('id', $id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'milestones');
|
|
if ($this->db->affected_rows() > 0) {
|
|
$project = $this->get($milestone->project_id, [], $playground);
|
|
if ($project->settings->view_milestones == 1) {
|
|
$show_to_customer = 1;
|
|
} else {
|
|
$show_to_customer = 0;
|
|
}
|
|
$this->log_activity($milestone->project_id, 'project_activity_deleted_milestone', $milestone->name, $show_to_customer, $playground);
|
|
$this->db->where('milestone', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['milestone' => 0, ], $playground);
|
|
log_activity('Project Milestone Deleted [' . $id . ']');
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function add($data, $playground = false) {
|
|
if (isset($data['notify_project_members_status_change'])) {
|
|
unset($data['notify_project_members_status_change']);
|
|
}
|
|
$send_created_email = false;
|
|
if (isset($data['send_created_email'])) {
|
|
unset($data['send_created_email']);
|
|
$send_created_email = true;
|
|
}
|
|
$send_project_marked_as_finished_email_to_contacts = false;
|
|
if (isset($data['project_marked_as_finished_email_to_contacts'])) {
|
|
unset($data['project_marked_as_finished_email_to_contacts']);
|
|
$send_project_marked_as_finished_email_to_contacts = true;
|
|
}
|
|
if (isset($data['settings'])) {
|
|
$project_settings = $data['settings'];
|
|
unset($data['settings']);
|
|
}
|
|
if (isset($data['custom_fields'])) {
|
|
$custom_fields = $data['custom_fields'];
|
|
unset($data['custom_fields']);
|
|
}
|
|
if (isset($data['progress_from_tasks'])) {
|
|
$data['progress_from_tasks'] = 1;
|
|
} else {
|
|
$data['progress_from_tasks'] = 0;
|
|
}
|
|
if (isset($data['contact_notification'])) {
|
|
if ($data['contact_notification'] == 2) {
|
|
$data['notify_contacts'] = serialize($data['notify_contacts']);
|
|
} else {
|
|
$data['notify_contacts'] = serialize([]);
|
|
}
|
|
}
|
|
$data['project_cost'] = !empty($data['project_cost']) ? $data['project_cost'] : null;
|
|
$data['estimated_hours'] = !empty($data['estimated_hours']) ? $data['estimated_hours'] : null;
|
|
$data['start_date'] = to_sql_date($data['start_date']);
|
|
if (!empty($data['deadline'])) {
|
|
$data['deadline'] = to_sql_date($data['deadline']);
|
|
} else {
|
|
unset($data['deadline']);
|
|
}
|
|
$data['project_created'] = date('Y-m-d');
|
|
if (isset($data['project_members'])) {
|
|
$project_members = $data['project_members'];
|
|
unset($data['project_members']);
|
|
}
|
|
if ($data['billing_type'] == 1) {
|
|
$data['project_rate_per_hour'] = 0;
|
|
} else if ($data['billing_type'] == 2) {
|
|
$data['project_cost'] = 0;
|
|
} else {
|
|
$data['project_rate_per_hour'] = 0;
|
|
$data['project_cost'] = 0;
|
|
}
|
|
$data['addedfrom'] = get_staff_user_id();
|
|
$items_to_convert = false;
|
|
if (isset($data['items'])) {
|
|
$items_to_convert = $data['items'];
|
|
$estimate_id = $data['estimate_id'];
|
|
$items_assignees = $data['items_assignee'];
|
|
unset($data['items'], $data['estimate_id'], $data['items_assignee']);
|
|
}
|
|
$data = hooks()->apply_filters('before_add_project', $data);
|
|
$tags = '';
|
|
if (isset($data['tags'])) {
|
|
$tags = $data['tags'];
|
|
unset($data['tags']);
|
|
}
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'projects', $data);
|
|
$insert_id = $this->db->insert_id();
|
|
if ($insert_id) {
|
|
$this->load->model('misc_model');
|
|
$this->misc_model->handle_tags_save($tags, $insert_id, 'project');
|
|
if (isset($custom_fields)) {
|
|
$this->load->model('custom_fields_model');
|
|
$this->custom_fields_model->handle_custom_fields_post($insert_id, $custom_fields, false, $playground);
|
|
}
|
|
if (isset($project_members)) {
|
|
$_pm['project_members'] = $project_members;
|
|
$this->add_edit_members($_pm, $insert_id, $playground);
|
|
}
|
|
$original_settings = $this->get_settings();
|
|
if (isset($project_settings)) {
|
|
$_settings = [];
|
|
$_values = [];
|
|
foreach ($project_settings as $name => $val) {
|
|
array_push($_settings, $name);
|
|
$_values[$name] = $val;
|
|
}
|
|
foreach ($original_settings as $setting) {
|
|
if ($setting != 'available_features') {
|
|
if (in_array($setting, $_settings)) {
|
|
$value_setting = 1;
|
|
} else {
|
|
$value_setting = 0;
|
|
}
|
|
} else {
|
|
$tabs = get_project_tabs_admin();
|
|
$tab_settings = [];
|
|
foreach ($_values[$setting] as $tab) {
|
|
$tab_settings[$tab] = 1;
|
|
}
|
|
foreach ($tabs as $tab) {
|
|
if (!isset($tab['collapse'])) {
|
|
if (!in_array($tab['slug'], $_values[$setting])) {
|
|
$tab_settings[$tab['slug']] = 0;
|
|
}
|
|
} else {
|
|
foreach ($tab['children'] as $tab_dropdown) {
|
|
if (!in_array($tab_dropdown['slug'], $_values[$setting])) {
|
|
$tab_settings[$tab_dropdown['slug']] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$value_setting = serialize($tab_settings);
|
|
}
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_settings', ['project_id' => $insert_id, 'name' => $setting, 'value' => $value_setting, ]);
|
|
}
|
|
} else {
|
|
foreach ($original_settings as $setting) {
|
|
$value_setting = 0;
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_settings', ['project_id' => $insert_id, 'name' => $setting, 'value' => $value_setting, ]);
|
|
}
|
|
}
|
|
if ($items_to_convert && is_numeric($estimate_id)) {
|
|
$this->convert_estimate_items_to_tasks($insert_id, $items_to_convert, $items_assignees, $data, $project_settings, $playground);
|
|
$this->db->where('id', $estimate_id);
|
|
$this->db->set('project_id', $insert_id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'estimates');
|
|
}
|
|
$this->log_activity($insert_id, 'project_activity_created', $playground);
|
|
if ($send_created_email == true) {
|
|
$this->send_project_customer_email($insert_id, 'project_created_to_customer', $playground);
|
|
}
|
|
if ($send_project_marked_as_finished_email_to_contacts == true) {
|
|
$this->send_project_customer_email($insert_id, 'project_marked_as_finished_to_customer', $playground);
|
|
}
|
|
hooks()->do_action('after_add_project', $insert_id);
|
|
log_activity('New Project Created [ID: ' . $insert_id . ']');
|
|
return $insert_id;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function update($data, $id, $playground = false) {
|
|
$this->db->select('status');
|
|
$this->db->where('id', $id);
|
|
$old_status = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->row()->status;
|
|
$send_created_email = false;
|
|
if (isset($data['send_created_email'])) {
|
|
unset($data['send_created_email']);
|
|
$send_created_email = true;
|
|
}
|
|
$send_project_marked_as_finished_email_to_contacts = false;
|
|
if (isset($data['project_marked_as_finished_email_to_contacts'])) {
|
|
unset($data['project_marked_as_finished_email_to_contacts']);
|
|
$send_project_marked_as_finished_email_to_contacts = true;
|
|
}
|
|
$original_project = $this->get($id, [], $playground);
|
|
if (isset($data['notify_project_members_status_change'])) {
|
|
$notify_project_members_status_change = true;
|
|
unset($data['notify_project_members_status_change']);
|
|
}
|
|
$affectedRows = 0;
|
|
if (!isset($data['settings'])) {
|
|
$this->db->where('project_id', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'project_settings', ['value' => 0, ]);
|
|
if ($this->db->affected_rows() > 0) {
|
|
$affectedRows++;
|
|
}
|
|
} else {
|
|
$_settings = [];
|
|
$_values = [];
|
|
foreach ($data['settings'] as $name => $val) {
|
|
array_push($_settings, $name);
|
|
$_values[$name] = $val;
|
|
}
|
|
unset($data['settings']);
|
|
$original_settings = $this->get_project_settings($id, $playground);
|
|
foreach ($original_settings as $setting) {
|
|
if ($setting['name'] != 'available_features') {
|
|
if (in_array($setting['name'], $_settings)) {
|
|
$value_setting = 1;
|
|
} else {
|
|
$value_setting = 0;
|
|
}
|
|
} else {
|
|
$tabs = get_project_tabs_admin();
|
|
$tab_settings = [];
|
|
foreach ($_values[$setting['name']] as $tab) {
|
|
$tab_settings[$tab] = 1;
|
|
}
|
|
foreach ($tabs as $tab) {
|
|
if (!isset($tab['collapse'])) {
|
|
if (!in_array($tab['slug'], $_values[$setting['name']])) {
|
|
$tab_settings[$tab['slug']] = 0;
|
|
}
|
|
} else {
|
|
foreach ($tab['children'] as $tab_dropdown) {
|
|
if (!in_array($tab_dropdown['slug'], $_values[$setting['name']])) {
|
|
$tab_settings[$tab_dropdown['slug']] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$value_setting = serialize($tab_settings);
|
|
}
|
|
$this->db->where('project_id', $id);
|
|
$this->db->where('name', $setting['name']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'project_settings', ['value' => $value_setting, ]);
|
|
if ($this->db->affected_rows() > 0) {
|
|
$affectedRows++;
|
|
}
|
|
}
|
|
}
|
|
$data['project_cost'] = !empty($data['project_cost']) ? $data['project_cost'] : null;
|
|
$data['estimated_hours'] = !empty($data['estimated_hours']) ? $data['estimated_hours'] : null;
|
|
if ($old_status == 4 && $data['status'] != 4) {
|
|
$data['date_finished'] = null;
|
|
} else if (isset($data['date_finished'])) {
|
|
$data['date_finished'] = to_sql_date($data['date_finished'], true);
|
|
}
|
|
if (isset($data['progress_from_tasks'])) {
|
|
$data['progress_from_tasks'] = 1;
|
|
} else {
|
|
$data['progress_from_tasks'] = 0;
|
|
}
|
|
if (isset($data['custom_fields'])) {
|
|
$custom_fields = $data['custom_fields'];
|
|
$this->load->model('custom_fields_model');
|
|
if ($this->custom_fields_model->handle_custom_fields_post($id, $custom_fields, false, $playground)) {
|
|
$affectedRows++;
|
|
}
|
|
unset($data['custom_fields']);
|
|
}
|
|
if (!empty($data['deadline'])) {
|
|
$data['deadline'] = to_sql_date($data['deadline']);
|
|
} else {
|
|
$data['deadline'] = null;
|
|
}
|
|
$data['start_date'] = to_sql_date($data['start_date']);
|
|
if ($data['billing_type'] == 1) {
|
|
$data['project_rate_per_hour'] = 0;
|
|
} else if ($data['billing_type'] == 2) {
|
|
$data['project_cost'] = 0;
|
|
} else {
|
|
$data['project_rate_per_hour'] = 0;
|
|
$data['project_cost'] = 0;
|
|
}
|
|
if (isset($data['project_members'])) {
|
|
$project_members = $data['project_members'];
|
|
unset($data['project_members']);
|
|
}
|
|
$_pm = [];
|
|
if (isset($project_members)) {
|
|
$_pm['project_members'] = $project_members;
|
|
}
|
|
if ($this->add_edit_members($_pm, $id, $playground)) {
|
|
$affectedRows++;
|
|
}
|
|
if (isset($data['mark_all_tasks_as_completed'])) {
|
|
$mark_all_tasks_as_completed = true;
|
|
unset($data['mark_all_tasks_as_completed']);
|
|
}
|
|
if (isset($data['tags'])) {
|
|
$this->load->model('misc_model');
|
|
if ($this->misc_model->handle_tags_save($data['tags'], $id, 'project')) {
|
|
$affectedRows++;
|
|
}
|
|
unset($data['tags']);
|
|
}
|
|
if (isset($data['cancel_recurring_tasks'])) {
|
|
unset($data['cancel_recurring_tasks']);
|
|
$this->cancel_recurring_tasks($id);
|
|
}
|
|
if (isset($data['contact_notification'])) {
|
|
if ($data['contact_notification'] == 2) {
|
|
$data['notify_contacts'] = serialize($data['notify_contacts']);
|
|
} else {
|
|
$data['notify_contacts'] = serialize([]);
|
|
}
|
|
}
|
|
$data = hooks()->apply_filters('before_update_project', $data, $id);
|
|
$this->db->where('id', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'projects', $data);
|
|
if ($this->db->affected_rows() > 0) {
|
|
if (isset($mark_all_tasks_as_completed)) {
|
|
$this->_mark_all_project_tasks_as_completed($id, $playground);
|
|
}
|
|
$affectedRows++;
|
|
}
|
|
if ($send_created_email == true) {
|
|
if ($this->send_project_customer_email($id, 'project_created_to_customer', $playground)) {
|
|
$affectedRows++;
|
|
}
|
|
}
|
|
if ($send_project_marked_as_finished_email_to_contacts == true) {
|
|
if ($this->send_project_customer_email($id, 'project_marked_as_finished_to_customer', $playground)) {
|
|
$affectedRows++;
|
|
}
|
|
}
|
|
if ($affectedRows > 0) {
|
|
$this->log_activity($id, 'project_activity_updated', $playground);
|
|
log_activity('Project Updated [ID: ' . $id . ']');
|
|
if ($original_project->status != $data['status']) {
|
|
hooks()->do_action('project_status_changed', ['status' => $data['status'], 'project_id' => $id, ]);
|
|
// Give space this log to be on top
|
|
sleep(1);
|
|
if ($data['status'] == 4) {
|
|
$this->log_activity($id, 'project_marked_as_finished');
|
|
$this->db->where('id', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'projects', ['date_finished' => date('Y-m-d H:i:s') ]);
|
|
} else {
|
|
$this->log_activity($id, 'project_status_updated', '<b><lang>project_status_' . $data['status'] . '</lang></b>');
|
|
}
|
|
if (isset($notify_project_members_status_change)) {
|
|
$this->_notify_project_members_status_change($id, $original_project->status, $data['status'], $playground);
|
|
}
|
|
}
|
|
hooks()->do_action('after_update_project', $id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Simplified function to send non complicated email templates for project contacts
|
|
* @param mixed $id project id
|
|
* @return boolean
|
|
*/
|
|
public function send_project_customer_email($id, $template, $playground = false) {
|
|
$sent = false;
|
|
$this->load->model('clients_model');
|
|
$contacts = $this->clients_model->get_contacts_for_project_notifications($id, 'project_emails', $playground);
|
|
foreach ($contacts as $contact) {
|
|
if (send_mail_template($template, $id, $contact['userid'], $contact)) {
|
|
$sent = true;
|
|
}
|
|
}
|
|
if ($sent) {
|
|
hooks()->do_action('after_project_customer_email_sent', ['project_id' => $id, 'email_template' => $template, ]);
|
|
}
|
|
return $sent;
|
|
}
|
|
|
|
public function mark_as($data, $playground = false) {
|
|
$this->db->select('status');
|
|
$this->db->where('id', $data['project_id']);
|
|
$old_status = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->row()->status;
|
|
$this->db->where('id', $data['project_id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'projects', ['status' => $data['status_id'], ]);
|
|
if ($this->db->affected_rows() > 0) {
|
|
hooks()->do_action('project_status_changed', ['status' => $data['status_id'], 'project_id' => $data['project_id'], ]);
|
|
if ($data['status_id'] == 4) {
|
|
$this->log_activity($data['project_id'], 'project_marked_as_finished');
|
|
$this->db->where('id', $data['project_id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'projects', ['date_finished' => date('Y-m-d H:i:s') ]);
|
|
} else {
|
|
$this->log_activity($data['project_id'], 'project_status_updated', '<b><lang>project_status_' . $data['status_id'] . '</lang></b>');
|
|
if ($old_status == 4) {
|
|
$this->db->where('id', $data['project_id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'projects', ['date_finished' => null]);
|
|
}
|
|
}
|
|
if ($data['notify_project_members_status_change'] == 1) {
|
|
$this->_notify_project_members_status_change($data['project_id'], $old_status, $data['status_id'], $playground);
|
|
}
|
|
if ($data['mark_all_tasks_as_completed'] == 1) {
|
|
$this->_mark_all_project_tasks_as_completed($data['project_id'], $playground);
|
|
}
|
|
if (isset($data['cancel_recurring_tasks']) && $data['cancel_recurring_tasks'] == 'true') {
|
|
$this->cancel_recurring_tasks($data['project_id'], $playground);
|
|
}
|
|
if (isset($data['send_project_marked_as_finished_email_to_contacts']) && $data['send_project_marked_as_finished_email_to_contacts'] == 1) {
|
|
$this->send_project_customer_email($data['project_id'], 'project_marked_as_finished_to_customer', $playground);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private function _notify_project_members_status_change($id, $old_status, $new_status, $playground = false) {
|
|
$members = $this->get_project_members($id, false, $playground);
|
|
$notifiedUsers = [];
|
|
foreach ($members as $member) {
|
|
if ($member['staff_id'] != get_staff_user_id()) {
|
|
$notified = add_notification(['fromuserid' => get_staff_user_id(), 'description' => 'not_project_status_updated', 'link' => 'projects/view/' . $id, 'touserid' => $member['staff_id'], 'additional_data' => serialize(['<lang>project_status_' . $old_status . '</lang>', '<lang>project_status_' . $new_status . '</lang>', ]), ]);
|
|
if ($notified) {
|
|
array_push($notifiedUsers, $member['staff_id']);
|
|
}
|
|
}
|
|
}
|
|
pusher_trigger_notification($notifiedUsers);
|
|
}
|
|
|
|
private function _mark_all_project_tasks_as_completed($id, $playground = false) {
|
|
$this->db->where('rel_type', 'project');
|
|
$this->db->where('rel_id', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['status' => 5, 'datefinished' => date('Y-m-d H:i:s'), ]);
|
|
$tasks = $this->get_tasks($id, [], false, false, null, $playground);
|
|
foreach ($tasks as $task) {
|
|
$this->db->where('task_id', $task['id']);
|
|
$this->db->where('end_time IS NULL');
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'taskstimers', ['end_time' => time(), ]);
|
|
}
|
|
$this->log_activity($id, 'project_activity_marked_all_tasks_as_complete', $playground);
|
|
}
|
|
|
|
public function add_edit_members($data, $id, $playground = false) {
|
|
$affectedRows = 0;
|
|
if (isset($data['project_members'])) {
|
|
$project_members = $data['project_members'];
|
|
}
|
|
$new_project_members_to_receive_email = [];
|
|
$this->db->select('name,clientid');
|
|
$this->db->where('id', $id);
|
|
$project = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->row();
|
|
$project_name = $project->name;
|
|
$client_id = $project->clientid;
|
|
$project_members_in = $this->get_project_members($id, false, $playground);
|
|
if (sizeof($project_members_in) > 0) {
|
|
foreach ($project_members_in as $project_member) {
|
|
if (isset($project_members)) {
|
|
if (!in_array($project_member['staff_id'], $project_members)) {
|
|
$this->db->where('project_id', $id);
|
|
$this->db->where('staff_id', $project_member['staff_id']);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'project_members');
|
|
if ($this->db->affected_rows() > 0) {
|
|
$this->db->where('staff_id', $project_member['staff_id']);
|
|
$this->db->where('project_id', $id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'pinned_projects');
|
|
$this->load->model('staff_model');
|
|
$this->log_activity($id, 'project_activity_removed_team_member', $this->staff_model->get_staff_full_name($project_member['staff_id'], $playground));
|
|
$affectedRows++;
|
|
}
|
|
}
|
|
} else {
|
|
$this->db->where('project_id', $id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'project_members');
|
|
if ($this->db->affected_rows() > 0) {
|
|
$affectedRows++;
|
|
}
|
|
}
|
|
}
|
|
if (isset($project_members)) {
|
|
$notifiedUsers = [];
|
|
foreach ($project_members as $staff_id) {
|
|
$this->db->where('project_id', $id);
|
|
$this->db->where('staff_id', $staff_id);
|
|
$_exists = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_members')->row();
|
|
if (!$_exists) {
|
|
if (empty($staff_id)) {
|
|
continue;
|
|
}
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_members', ['project_id' => $id, 'staff_id' => $staff_id, ]);
|
|
if ($this->db->affected_rows() > 0) {
|
|
if ($staff_id != get_staff_user_id()) {
|
|
$notified = add_notification(['fromuserid' => get_staff_user_id(), 'description' => 'not_staff_added_as_project_member', 'link' => 'projects/view/' . $id, 'touserid' => $staff_id, 'additional_data' => serialize([$project_name, ]), ]);
|
|
array_push($new_project_members_to_receive_email, $staff_id);
|
|
if ($notified) {
|
|
array_push($notifiedUsers, $staff_id);
|
|
}
|
|
}
|
|
$this->load->model('staff_model');
|
|
$this->log_activity($id, 'project_activity_added_team_member', $this->staff_model->get_staff_full_name($staff_id, $playground));
|
|
$affectedRows++;
|
|
}
|
|
}
|
|
}
|
|
pusher_trigger_notification($notifiedUsers);
|
|
}
|
|
} else {
|
|
if (isset($project_members)) {
|
|
$notifiedUsers = [];
|
|
foreach ($project_members as $staff_id) {
|
|
if (empty($staff_id)) {
|
|
continue;
|
|
}
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_members', ['project_id' => $id, 'staff_id' => $staff_id, ]);
|
|
if ($this->db->affected_rows() > 0) {
|
|
if ($staff_id != get_staff_user_id()) {
|
|
$notified = add_notification(['fromuserid' => get_staff_user_id(), 'description' => 'not_staff_added_as_project_member', 'link' => 'projects/view/' . $id, 'touserid' => $staff_id, 'additional_data' => serialize([$project_name, ]), ]);
|
|
array_push($new_project_members_to_receive_email, $staff_id);
|
|
if ($notifiedUsers) {
|
|
array_push($notifiedUsers, $staff_id);
|
|
}
|
|
}
|
|
$this->load->model('staff_model');
|
|
$this->log_activity($id, 'project_activity_added_team_member', $this->staff_model->get_staff_full_name($staff_id, $playground));
|
|
$affectedRows++;
|
|
}
|
|
}
|
|
pusher_trigger_notification($notifiedUsers);
|
|
}
|
|
}
|
|
if (count($new_project_members_to_receive_email) > 0) {
|
|
$all_members = $this->get_project_members($id, false, $playground);
|
|
foreach ($all_members as $data) {
|
|
if (in_array($data['staff_id'], $new_project_members_to_receive_email)) {
|
|
send_mail_template('project_staff_added_as_member', $data, $id, $client_id, $playground);
|
|
}
|
|
}
|
|
hooks()->do_action('after_project_staff_added_as_member', ['project_id' => $id, 'new_project_members_to_receive_email' => $new_project_members_to_receive_email, ]);
|
|
}
|
|
if ($affectedRows > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function is_member($project_id, $staff_id = '', $playground = false) {
|
|
if (!is_numeric($staff_id)) {
|
|
$staff_id = get_staff_user_id();
|
|
}
|
|
$member = total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'project_members', ['staff_id' => $staff_id, 'project_id' => $project_id, ]);
|
|
if ($member > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function get_projects_for_ticket($client_id, $playground = false) {
|
|
return $this->get('', ['clientid' => $client_id, ], $playground);
|
|
}
|
|
|
|
public function get_project_settings($project_id, $playground = false) {
|
|
$this->db->where('project_id', $project_id);
|
|
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_settings')->result_array();
|
|
}
|
|
|
|
public function get_project_members($id, $with_name = false, $playground = false) {
|
|
if ($with_name) {
|
|
$this->db->select('firstname,lastname,email,project_id,staff_id');
|
|
} else {
|
|
$this->db->select('email,project_id,staff_id');
|
|
}
|
|
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'staff', db_prefix() . ($playground ? 'playground_' : '') . 'staff.staffid=' . db_prefix() . ($playground ? 'playground_' : '') . 'project_members.staff_id');
|
|
$this->db->where('project_id', $id);
|
|
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_members')->result_array();
|
|
}
|
|
|
|
public function remove_team_member($project_id, $staff_id, $playground = false) {
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->where('staff_id', $staff_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'project_members');
|
|
if ($this->db->affected_rows() > 0) {
|
|
$this->load->model('staff_model');
|
|
// Remove member from tasks where is assigned
|
|
$this->db->where('staffid', $staff_id);
|
|
$this->db->where('taskid IN (SELECT id FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'tasks WHERE rel_type="project" AND rel_id="' . $this->db->escape_str($project_id) . '")');
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'task_assigned');
|
|
$this->log_activity($project_id, 'project_activity_removed_team_member', $this->staff_model->get_staff_full_name($staff_id, $playground), $playground);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function get_timesheets($project_id, $tasks_ids = [], $playground = false) {
|
|
if (count($tasks_ids) == 0) {
|
|
$tasks = $this->get_tasks($project_id, [], false, false, null, $playground);
|
|
$tasks_ids = [];
|
|
foreach ($tasks as $task) {
|
|
array_push($tasks_ids, $task['id']);
|
|
}
|
|
}
|
|
if (count($tasks_ids) > 0) {
|
|
$this->db->where('task_id IN(' . implode(', ', $tasks_ids) . ')');
|
|
$timesheets = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'taskstimers')->result_array();
|
|
$i = 0;
|
|
$this->load->model('tasks_model');
|
|
$this->load->model('staff_model');
|
|
foreach ($timesheets as $t) {
|
|
$task = $this->tasks_model->get($t['task_id'], [], $playground);
|
|
$timesheets[$i]['task_data'] = $task;
|
|
$timesheets[$i]['staff_name'] = $this->staff_model->get_staff_full_name($t['staff_id'], $playground);
|
|
if (!is_null($t['end_time'])) {
|
|
$timesheets[$i]['total_spent'] = $t['end_time'] - $t['start_time'];
|
|
} else {
|
|
$timesheets[$i]['total_spent'] = time() - $t['start_time'];
|
|
}
|
|
$i++;
|
|
}
|
|
return $timesheets;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
public function get_discussion($id, $project_id = '', $playground = false) {
|
|
if ($project_id != '') {
|
|
$this->db->where('project_id', $project_id);
|
|
}
|
|
$this->db->where('id', $id);
|
|
if (is_client_logged_in()) {
|
|
$this->db->where('show_to_customer', 1);
|
|
$this->db->where('project_id IN (SELECT id FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'projects WHERE clientid=' . get_client_user_id() . ')');
|
|
}
|
|
$discussion = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussions')->row();
|
|
if ($discussion) {
|
|
return $discussion;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function get_discussion_comment($id, $playground = false) {
|
|
$this->db->where('id', $id);
|
|
$comment = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments')->row();
|
|
if ($comment->contact_id != 0) {
|
|
if (is_client_logged_in()) {
|
|
if ($comment->contact_id == get_contact_user_id()) {
|
|
$comment->created_by_current_user = true;
|
|
} else {
|
|
$comment->created_by_current_user = false;
|
|
}
|
|
} else {
|
|
$comment->created_by_current_user = false;
|
|
}
|
|
$comment->profile_picture_url = contact_profile_image_url($comment->contact_id, $playground);
|
|
} else {
|
|
if (is_client_logged_in()) {
|
|
$comment->created_by_current_user = false;
|
|
} else {
|
|
if (is_staff_logged_in()) {
|
|
if ($comment->staff_id == get_staff_user_id()) {
|
|
$comment->created_by_current_user = true;
|
|
} else {
|
|
$comment->created_by_current_user = false;
|
|
}
|
|
} else {
|
|
$comment->created_by_current_user = false;
|
|
}
|
|
}
|
|
if (is_admin($comment->staff_id)) {
|
|
$comment->created_by_admin = true;
|
|
} else {
|
|
$comment->created_by_admin = false;
|
|
}
|
|
$comment->profile_picture_url = staff_profile_image_url($comment->staff_id, $playground);
|
|
}
|
|
$comment->created = (strtotime($comment->created) * 1000);
|
|
if (!empty($comment->modified)) {
|
|
$comment->modified = (strtotime($comment->modified) * 1000);
|
|
}
|
|
if (!is_null($comment->file_name)) {
|
|
$comment->file_url = site_url('uploads/discussions/' . $comment->discussion_id . '/' . $comment->file_name);
|
|
}
|
|
return $comment;
|
|
}
|
|
|
|
public function get_discussion_comments($id, $type, $playground = false) {
|
|
$this->db->where('discussion_id', $id);
|
|
$this->db->where('discussion_type', $type);
|
|
$comments = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments')->result_array();
|
|
$i = 0;
|
|
$allCommentsIDS = [];
|
|
$allCommentsParentIDS = [];
|
|
foreach ($comments as $comment) {
|
|
$allCommentsIDS[] = $comment['id'];
|
|
if (!empty($comment['parent'])) {
|
|
$allCommentsParentIDS[] = $comment['parent'];
|
|
}
|
|
if ($comment['contact_id'] != 0) {
|
|
if (is_client_logged_in()) {
|
|
if ($comment['contact_id'] == get_contact_user_id()) {
|
|
$comments[$i]['created_by_current_user'] = true;
|
|
} else {
|
|
$comments[$i]['created_by_current_user'] = false;
|
|
}
|
|
} else {
|
|
$comments[$i]['created_by_current_user'] = false;
|
|
}
|
|
$comments[$i]['profile_picture_url'] = contact_profile_image_url($comment['contact_id']);
|
|
} else {
|
|
if (is_client_logged_in()) {
|
|
$comments[$i]['created_by_current_user'] = false;
|
|
} else {
|
|
if (is_staff_logged_in()) {
|
|
if ($comment['staff_id'] == get_staff_user_id()) {
|
|
$comments[$i]['created_by_current_user'] = true;
|
|
} else {
|
|
$comments[$i]['created_by_current_user'] = false;
|
|
}
|
|
} else {
|
|
$comments[$i]['created_by_current_user'] = false;
|
|
}
|
|
}
|
|
if (is_admin($comment['staff_id'])) {
|
|
$comments[$i]['created_by_admin'] = true;
|
|
} else {
|
|
$comments[$i]['created_by_admin'] = false;
|
|
}
|
|
$comments[$i]['profile_picture_url'] = staff_profile_image_url($comment['staff_id'], $playground);
|
|
}
|
|
if (!is_null($comment['file_name'])) {
|
|
$comments[$i]['file_url'] = site_url('uploads/discussions/' . $id . '/' . $comment['file_name']);
|
|
}
|
|
$comments[$i]['created'] = (strtotime($comment['created']) * 1000);
|
|
if (!empty($comment['modified'])) {
|
|
$comments[$i]['modified'] = (strtotime($comment['modified']) * 1000);
|
|
}
|
|
$i++;
|
|
}
|
|
// Ticket #5471
|
|
foreach ($allCommentsParentIDS as $parent_id) {
|
|
if (!in_array($parent_id, $allCommentsIDS)) {
|
|
foreach ($comments as $key => $comment) {
|
|
if ($comment['parent'] == $parent_id) {
|
|
$comments[$key]['parent'] = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $comments;
|
|
}
|
|
|
|
public function get_discussions($project_id, $playground = false) {
|
|
$this->db->where('project_id', $project_id);
|
|
if (is_client_logged_in()) {
|
|
$this->db->where('show_to_customer', 1);
|
|
}
|
|
$discussions = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussions')->result_array();
|
|
$i = 0;
|
|
foreach ($discussions as $discussion) {
|
|
$discussions[$i]['total_comments'] = total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments', ['discussion_id' => $discussion['id'], 'discussion_type' => 'regular', ]);
|
|
$i++;
|
|
}
|
|
return $discussions;
|
|
}
|
|
|
|
public function add_discussion_comment($data, $discussion_id, $type, $playground = false) {
|
|
$discussion = $this->get_discussion($discussion_id, '', $playground);
|
|
$_data['discussion_id'] = $discussion_id;
|
|
$_data['discussion_type'] = $type;
|
|
if (isset($data['content'])) {
|
|
$_data['content'] = $data['content'];
|
|
}
|
|
if (isset($data['parent']) && $data['parent'] != null) {
|
|
$_data['parent'] = $data['parent'];
|
|
}
|
|
$this->load->model('staff_model');
|
|
if (is_client_logged_in()) {
|
|
$_data['contact_id'] = get_contact_user_id();
|
|
$_data['fullname'] = get_contact_full_name($_data['contact_id'], $playground);
|
|
$_data['staff_id'] = 0;
|
|
} else {
|
|
$_data['contact_id'] = 0;
|
|
$_data['staff_id'] = get_staff_user_id();
|
|
$_data['fullname'] = $this->staff_model->get_staff_full_name($_data['staff_id'], $playground);
|
|
}
|
|
$_data = $this->handle_project_discussion_comment_attachments($discussion_id, $data, $_data, $playground);
|
|
$_data['created'] = date('Y-m-d H:i:s');
|
|
$_data = hooks()->apply_filters('before_add_project_discussion_comment', $_data, $discussion_id);
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments', $_data);
|
|
$insert_id = $this->db->insert_id();
|
|
if ($insert_id) {
|
|
if ($type == 'regular') {
|
|
$discussion = $this->get_discussion($discussion_id, '', $playground);
|
|
$not_link = 'projects/view/' . $discussion->project_id . '?group=project_discussions&discussion_id=' . $discussion_id;
|
|
} else {
|
|
$discussion = $this->get_file($discussion_id, $playground);
|
|
$not_link = 'projects/view/' . $discussion->project_id . '?group=project_files&file_id=' . $discussion_id;
|
|
$discussion->show_to_customer = $discussion->visible_to_customer;
|
|
}
|
|
$emailTemplateData = ['staff' => ['discussion_id' => $discussion_id, 'discussion_comment_id' => $insert_id, 'discussion_type' => $type, ], 'customers' => ['customer_template' => true, 'discussion_id' => $discussion_id, 'discussion_comment_id' => $insert_id, 'discussion_type' => $type, ], ];
|
|
if (isset($_data['file_name'])) {
|
|
$emailTemplateData['attachments'] = [['attachment' => PROJECT_DISCUSSION_ATTACHMENT_FOLDER . $discussion_id . '/' . $_data['file_name'], 'filename' => $_data['file_name'], 'type' => $_data['file_mime_type'], 'read' => true, ], ];
|
|
}
|
|
$notification_data = ['description' => 'not_commented_on_project_discussion', 'link' => $not_link, ];
|
|
if (is_client_logged_in()) {
|
|
$notification_data['fromclientid'] = get_contact_user_id();
|
|
} else {
|
|
$notification_data['fromuserid'] = get_staff_user_id();
|
|
}
|
|
$notifiedUsers = [];
|
|
$regex = "/data\-mention\-id\=\"(\d+)\"/";
|
|
if (isset($data['content']) && preg_match_all($regex, $data['content'], $mentionedStaff, PREG_PATTERN_ORDER)) {
|
|
$members = array_unique($mentionedStaff[1], SORT_NUMERIC);
|
|
$this->send_project_email_mentioned_users($discussion->project_id, 'project_new_discussion_comment_to_staff', $members, $emailTemplateData, $playground);
|
|
foreach ($members as $memberId) {
|
|
if ($memberId == get_staff_user_id() && !is_client_logged_in()) {
|
|
continue;
|
|
}
|
|
$notification_data['touserid'] = $memberId;
|
|
if (add_notification($notification_data)) {
|
|
array_push($notifiedUsers, $memberId);
|
|
}
|
|
}
|
|
} else {
|
|
$this->send_project_email_template($discussion->project_id, 'project_new_discussion_comment_to_staff', 'project_new_discussion_comment_to_customer', $discussion->show_to_customer, $emailTemplateData, $playground);
|
|
$members = $this->get_project_members($discussion->project_id, false, $playground);
|
|
foreach ($members as $member) {
|
|
if ($member['staff_id'] == get_staff_user_id() && !is_client_logged_in()) {
|
|
continue;
|
|
}
|
|
$notification_data['touserid'] = $member['staff_id'];
|
|
if (add_notification($notification_data)) {
|
|
array_push($notifiedUsers, $member['staff_id']);
|
|
}
|
|
}
|
|
}
|
|
$this->log_activity($discussion->project_id, 'project_activity_commented_on_discussion', $discussion->subject, $discussion->show_to_customer, $playground);
|
|
pusher_trigger_notification($notifiedUsers);
|
|
$this->_update_discussion_last_activity($discussion_id, $type, $playground);
|
|
hooks()->do_action('after_add_discussion_comment', $insert_id);
|
|
return $this->get_discussion_comment($insert_id, $playground);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function update_discussion_comment($data, $playground = false) {
|
|
$comment = $this->get_discussion_comment($data['id'], $playground);
|
|
$this->db->where('id', $data['id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments', ['modified' => date('Y-m-d H:i:s'), 'content' => $data['content'], ]);
|
|
if ($this->db->affected_rows() > 0) {
|
|
$this->_update_discussion_last_activity($comment->discussion_id, $comment->discussion_type, $playground);
|
|
}
|
|
return $this->get_discussion_comment($data['id'], $playground);
|
|
}
|
|
|
|
public function delete_discussion_comment($id, $logActivity = true, $playground = false) {
|
|
$comment = $this->get_discussion_comment($id, $playground);
|
|
$this->db->where('id', $id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments');
|
|
if ($this->db->affected_rows() > 0) {
|
|
$this->delete_discussion_comment_attachment($comment->file_name, $comment->discussion_id, $playground);
|
|
if ($logActivity) {
|
|
$additional_data = '';
|
|
if ($comment->discussion_type == 'regular') {
|
|
$discussion = $this->get_discussion($comment->discussion_id, $playground);
|
|
$not = 'project_activity_deleted_discussion_comment';
|
|
$additional_data.= $discussion->subject . '<br />' . $comment->content;
|
|
} else {
|
|
$discussion = $this->get_file($comment->discussion_id, $playground);
|
|
$not = 'project_activity_deleted_file_discussion_comment';
|
|
$additional_data.= $discussion->subject . '<br />' . $comment->content;
|
|
}
|
|
if (!is_null($comment->file_name)) {
|
|
$additional_data.= $comment->file_name;
|
|
}
|
|
$this->log_activity($discussion->project_id, $not, $additional_data, $playground);
|
|
}
|
|
}
|
|
$this->db->where('parent', $id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments', ['parent' => null, ]);
|
|
if ($this->db->affected_rows() > 0 && $logActivity) {
|
|
$this->_update_discussion_last_activity($comment->discussion_id, $comment->discussion_type, $playground);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function delete_discussion_comment_attachment($file_name, $discussion_id) {
|
|
$path = PROJECT_DISCUSSION_ATTACHMENT_FOLDER . $discussion_id;
|
|
if (!is_null($file_name)) {
|
|
if (file_exists($path . '/' . $file_name)) {
|
|
unlink($path . '/' . $file_name);
|
|
}
|
|
}
|
|
if (is_dir($path)) {
|
|
// Check if no attachments left, so we can delete the folder also
|
|
$other_attachments = list_files($path);
|
|
if (count($other_attachments) == 0) {
|
|
delete_dir($path);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function add_discussion($data, $playground = false) {
|
|
if (is_client_logged_in()) {
|
|
$data['contact_id'] = get_contact_user_id();
|
|
$data['staff_id'] = 0;
|
|
$data['show_to_customer'] = 1;
|
|
} else {
|
|
$data['staff_id'] = get_staff_user_id();
|
|
$data['contact_id'] = 0;
|
|
if (isset($data['show_to_customer'])) {
|
|
$data['show_to_customer'] = 1;
|
|
} else {
|
|
$data['show_to_customer'] = 0;
|
|
}
|
|
}
|
|
$data['datecreated'] = date('Y-m-d H:i:s');
|
|
$data['description'] = nl2br($data['description']);
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussions', $data);
|
|
$insert_id = $this->db->insert_id();
|
|
if ($insert_id) {
|
|
$members = $this->get_project_members($data['project_id'], false, $playground);
|
|
$notification_data = ['description' => 'not_created_new_project_discussion', 'link' => 'projects/view/' . $data['project_id'] . '?group=project_discussions&discussion_id=' . $insert_id, ];
|
|
if (is_client_logged_in()) {
|
|
$notification_data['fromclientid'] = get_contact_user_id();
|
|
} else {
|
|
$notification_data['fromuserid'] = get_staff_user_id();
|
|
}
|
|
$notifiedUsers = [];
|
|
foreach ($members as $member) {
|
|
if ($member['staff_id'] == get_staff_user_id() && !is_client_logged_in()) {
|
|
continue;
|
|
}
|
|
$notification_data['touserid'] = $member['staff_id'];
|
|
if (add_notification($notification_data)) {
|
|
array_push($notifiedUsers, $member['staff_id']);
|
|
}
|
|
}
|
|
pusher_trigger_notification($notifiedUsers);
|
|
$this->send_project_email_template($data['project_id'], 'project_discussion_created_to_staff', 'project_discussion_created_to_customer', $data['show_to_customer'], ['staff' => ['discussion_id' => $insert_id, 'discussion_type' => 'regular', ], 'customers' => ['customer_template' => true, 'discussion_id' => $insert_id, 'discussion_type' => 'regular', ], ]);
|
|
$this->log_activity($data['project_id'], 'project_activity_created_discussion', $data['subject'], $data['show_to_customer']);
|
|
return $insert_id;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function edit_discussion($data, $id, $playground = false) {
|
|
$this->db->where('id', $id);
|
|
if (isset($data['show_to_customer'])) {
|
|
$data['show_to_customer'] = 1;
|
|
} else {
|
|
$data['show_to_customer'] = 0;
|
|
}
|
|
$data['description'] = nl2br($data['description']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussions', $data);
|
|
if ($this->db->affected_rows() > 0) {
|
|
$this->log_activity($data['project_id'], 'project_activity_updated_discussion', $data['subject'], $data['show_to_customer']);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function delete_discussion($id, $logActivity = true, $playground = false) {
|
|
$discussion = $this->get_discussion($id, '', $playground);
|
|
$this->db->where('id', $id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussions');
|
|
if ($this->db->affected_rows() > 0) {
|
|
if ($logActivity) {
|
|
$this->log_activity($discussion->project_id, 'project_activity_deleted_discussion', $discussion->subject, $discussion->show_to_customer);
|
|
}
|
|
$this->_delete_discussion_comments($id, 'regular', $playground);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function copy($project_id, $data, $playground = false) {
|
|
$_new_data = [];
|
|
$project = $this->get($project_id, [], $playground);
|
|
$settings = $this->get_project_settings($project_id, $playground);
|
|
$fields = $this->db->list_fields(db_prefix() . ($playground ? 'playground_' : '') . 'projects');
|
|
foreach ($fields as $field) {
|
|
if (isset($project->$field)) {
|
|
$_new_data[$field] = $project->$field;
|
|
}
|
|
}
|
|
unset($_new_data['id']);
|
|
$_new_data['clientid'] = $data['clientid_copy_project'];
|
|
unset($_new_data['clientid_copy_project']);
|
|
$_new_data['start_date'] = to_sql_date($data['start_date']);
|
|
if ($_new_data['start_date'] > date('Y-m-d')) {
|
|
$_new_data['status'] = 1;
|
|
} else {
|
|
$_new_data['status'] = 2;
|
|
}
|
|
if ($data['deadline']) {
|
|
$_new_data['deadline'] = to_sql_date($data['deadline']);
|
|
} else {
|
|
$_new_data['deadline'] = null;
|
|
}
|
|
if ($data['name']) {
|
|
$_new_data['name'] = $data['name'];
|
|
}
|
|
$_new_data['project_created'] = date('Y-m-d H:i:s');
|
|
$_new_data['addedfrom'] = get_staff_user_id();
|
|
$_new_data['date_finished'] = null;
|
|
if ($project->contact_notification == 2) {
|
|
$this->load->model('clients_model');
|
|
$contacts = $this->clients_model->get_contacts($_new_data['clientid'], ['active' => 1, 'project_emails' => 1], $playground);
|
|
$_new_data['notify_contacts'] = serialize(array_column($contacts, 'id'));
|
|
}
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'projects', $_new_data);
|
|
$id = $this->db->insert_id();
|
|
if ($id) {
|
|
$tags = get_tags_in($project_id, 'project');
|
|
$this->load->model('misc_model');
|
|
$this->misc_model->handle_tags_save($tags, $id, 'project');
|
|
foreach ($settings as $setting) {
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_settings', ['project_id' => $id, 'name' => $setting['name'], 'value' => $setting['value'], ]);
|
|
}
|
|
$added_tasks = [];
|
|
$tasks = $this->get_tasks($project_id, [], false, false, null, $playground);
|
|
$this->load->model('tasks_model');
|
|
if (isset($data['tasks'])) {
|
|
foreach ($tasks as $task) {
|
|
if (isset($data['task_include_followers'])) {
|
|
$copy_task_data['copy_task_followers'] = 'true';
|
|
}
|
|
if (isset($data['task_include_assignees'])) {
|
|
$copy_task_data['copy_task_assignees'] = 'true';
|
|
}
|
|
if (isset($data['tasks_include_checklist_items'])) {
|
|
$copy_task_data['copy_task_checklist_items'] = 'true';
|
|
}
|
|
$copy_task_data['copy_from'] = $task['id'];
|
|
// For new task start date, we will find the difference in days between
|
|
// the old project start and and the old task start date and then
|
|
// based on the new project start date, we will add e.q. 15 days to be
|
|
// new task start date to the task
|
|
// e.q. old project start date 2020-04-01, old task start date 2020-04-15 and due date 2020-04-30
|
|
// copy project and set start date 2020-06-01
|
|
// new task start date will be 2020-06-15 and below due date 2020-06-30
|
|
$dStart = new DateTime($project->start_date);
|
|
$dEnd = new DateTime($task['startdate']);
|
|
$dDiff = $dStart->diff($dEnd);
|
|
$startDate = new DateTime($_new_data['start_date']);
|
|
$startDate->modify('+' . $dDiff->days . ' DAY');
|
|
$newTaskStartDate = $startDate->format('Y-m-d');
|
|
$merge = ['rel_id' => $id, 'rel_type' => 'project', 'last_recurring_date' => null, 'startdate' => $newTaskStartDate, 'status' => $data['copy_project_task_status'], ];
|
|
// Calculate the diff in days between the task start and due date
|
|
// then add these days to the new task start date to be used as this task due date
|
|
if ($task['duedate']) {
|
|
$dStart = new DateTime($task['startdate']);
|
|
$dEnd = new DateTime($task['duedate']);
|
|
$dDiff = $dStart->diff($dEnd);
|
|
$dueDate = new DateTime($newTaskStartDate);
|
|
$dueDate->modify('+' . $dDiff->days . ' DAY');
|
|
$merge['duedate'] = $dueDate->format('Y-m-d');
|
|
}
|
|
$task_id = $this->tasks_model->copy($copy_task_data, $merge, $playground);
|
|
if ($task_id) {
|
|
array_push($added_tasks, $task_id);
|
|
}
|
|
}
|
|
}
|
|
if (isset($data['milestones'])) {
|
|
$milestones = $this->get_milestones($project_id, $playground);
|
|
$_added_milestones = [];
|
|
foreach ($milestones as $milestone) {
|
|
$newProjectStartDate = new DateTimeImmutable($_new_data['start_date']);
|
|
$oldProjectStartDate = new DateTime($project->start_date);
|
|
$oldMilestoneStartDate = new DateTime($milestone['start_date']); // assuming that the MySQL column added is start_date
|
|
$diffBetweenOldProjectStartDateAndOldMilesoneStartDate = $oldProjectStartDate->diff($oldMilestoneStartDate);
|
|
$newMilestoneStartDate = $newProjectStartDate->modify('+' . $diffBetweenOldProjectStartDateAndOldMilesoneStartDate->days . ' DAY');
|
|
$oldMilestoneDueDate = new DateTime($milestone['due_date']);
|
|
$diffBetweenOldMilestoneDueDateAndOldMilesoneStartDate = $oldMilestoneStartDate->diff($oldMilestoneDueDate);
|
|
$newMilestoneDueDate = $newMilestoneStartDate->modify('+' . $diffBetweenOldMilestoneDueDateAndOldMilesoneStartDate->days . ' DAY');
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'milestones', ['name' => $milestone['name'], 'project_id' => $id, 'milestone_order' => $milestone['milestone_order'], 'description_visible_to_customer' => $milestone['description_visible_to_customer'], 'description' => $milestone['description'], 'start_date' => $newMilestoneStartDate->format('Y-m-d'), 'due_date' => $newMilestoneDueDate->format('Y-m-d'), 'datecreated' => date('Y-m-d'), 'color' => $milestone['color'], 'hide_from_customer' => $milestone['hide_from_customer'], ]);
|
|
$milestone_id = $this->db->insert_id();
|
|
if ($milestone_id) {
|
|
$_added_milestone_data = [];
|
|
$_added_milestone_data['id'] = $milestone_id;
|
|
$_added_milestone_data['name'] = $milestone['name'];
|
|
$_added_milestones[] = $_added_milestone_data;
|
|
}
|
|
}
|
|
if (isset($data['tasks'])) {
|
|
if (count($added_tasks) > 0) {
|
|
// Original project tasks
|
|
foreach ($tasks as $task) {
|
|
if ($task['milestone'] != 0) {
|
|
$this->db->where('id', $task['milestone']);
|
|
$milestone = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'milestones')->row();
|
|
if ($milestone) {
|
|
foreach ($_added_milestones as $added_milestone) {
|
|
if ($milestone->name == $added_milestone['name']) {
|
|
$this->db->where('id IN (' . implode(', ', $added_tasks) . ')');
|
|
$this->db->where('milestone', $task['milestone']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['milestone' => $added_milestone['id'], ]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// milestones not set
|
|
if (count($added_tasks)) {
|
|
foreach ($added_tasks as $task) {
|
|
$this->db->where('id', $task['id']);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tasks', ['milestone' => 0, ]);
|
|
}
|
|
}
|
|
}
|
|
if (isset($data['members'])) {
|
|
$members = $this->get_project_members($project_id, false, $playground);
|
|
$_members = [];
|
|
foreach ($members as $member) {
|
|
array_push($_members, $member['staff_id']);
|
|
}
|
|
$this->add_edit_members(['project_members' => $_members, ], $id, $playground);
|
|
}
|
|
$this->load->model('custom_fields_model');
|
|
foreach ($this->custom_fields_model->get_custom_fields('projects', [], false, $playground) as $field) {
|
|
$value = $this->custom_fields_model->get_custom_field_value($project_id, $field['id'], 'projects', false, $playground);
|
|
if ($value != '') {
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues', ['relid' => $id, 'fieldid' => $field['id'], 'fieldto' => 'projects', 'value' => $value, ]);
|
|
}
|
|
}
|
|
$this->log_activity($id, 'project_activity_created', $playground);
|
|
log_activity('Project Copied [ID: ' . $project_id . ', NewID: ' . $id . ']');
|
|
hooks()->do_action('project_copied', ['project_id' => $project_id, 'new_project_id' => $id, ]);
|
|
return $id;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function get_staff_notes($project_id, $playground = false) {
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->where('staff_id', get_staff_user_id());
|
|
$notes = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_notes')->row();
|
|
if ($notes) {
|
|
return $notes->content;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
public function save_note($data, $project_id, $playground = false) {
|
|
// Check if the note exists for this project;
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->where('staff_id', get_staff_user_id());
|
|
$notes = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_notes')->row();
|
|
if ($notes) {
|
|
$this->db->where('id', $notes->id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'project_notes', ['content' => $data['content'], ]);
|
|
if ($this->db->affected_rows() > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_notes', ['staff_id' => get_staff_user_id(), 'content' => $data['content'], 'project_id' => $project_id, ]);
|
|
$insert_id = $this->db->insert_id();
|
|
if ($insert_id) {
|
|
return true;
|
|
}
|
|
return false;
|
|
return false;
|
|
}
|
|
|
|
public function delete($project_id, $playground = false) {
|
|
hooks()->do_action('before_project_deleted', $project_id);
|
|
$project_name = get_project_name_by_id($project_id);
|
|
$this->db->where('id', $project_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'projects');
|
|
if ($this->db->affected_rows() > 0) {
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'project_members');
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'project_notes');
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'milestones');
|
|
// Delete the custom field values
|
|
$this->db->where('relid', $project_id);
|
|
$this->db->where('fieldto', 'projects');
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues');
|
|
$this->db->where('rel_id', $project_id);
|
|
$this->db->where('rel_type', 'project');
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'taggables');
|
|
$this->db->where('project_id', $project_id);
|
|
$discussions = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussions')->result_array();
|
|
foreach ($discussions as $discussion) {
|
|
$discussion_comments = $this->get_discussion_comments($discussion['id'], 'regular', $playground);
|
|
foreach ($discussion_comments as $comment) {
|
|
$this->delete_discussion_comment_attachment($comment['file_name'], $discussion['id'], $playground);
|
|
}
|
|
$this->db->where('discussion_id', $discussion['id']);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments');
|
|
}
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussions');
|
|
$files = $this->get_files($project_id, $playground);
|
|
foreach ($files as $file) {
|
|
$this->remove_file($file['id'], $playground);
|
|
}
|
|
$tasks = $this->get_tasks($project_id, [], false, false, null, $playground);
|
|
$this->load->model('tasks_model');
|
|
foreach ($tasks as $task) {
|
|
$this->tasks_model->delete_task($task['id'], false, $playground);
|
|
}
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'project_settings');
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'project_activity');
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'expenses', ['project_id' => 0, ]);
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'contracts', ['project_id' => null, ]);
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'invoices', ['project_id' => 0, ]);
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'creditnotes', ['project_id' => 0, ]);
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'estimates', ['project_id' => 0, ]);
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', ['project_id' => 0, ]);
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'proposals', ['project_id' => null, ]);
|
|
$this->db->where('project_id', $project_id);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'pinned_projects');
|
|
log_activity('Project Deleted [ID: ' . $project_id . ', Name: ' . $project_name . ']');
|
|
hooks()->do_action('after_project_deleted', $project_id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get project status by passed project id
|
|
* @param mixed $id project id
|
|
* @return array
|
|
*/
|
|
public function get_project_status_by_id($id)
|
|
{
|
|
$statuses = $this->get_project_statuses();
|
|
|
|
$status = [
|
|
'id' => 0,
|
|
'color' => '#333',
|
|
'name' => '[Status Not Found]',
|
|
'order' => 1,
|
|
];
|
|
|
|
foreach ($statuses as $s) {
|
|
if ($s['id'] == $id) {
|
|
$status = $s;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $status;
|
|
}
|
|
|
|
public function get_activity($id = '', $limit = '', $only_project_members_activity = false, $playground = false) {
|
|
if (!is_client_logged_in()) {
|
|
$has_permission = staff_can('view', 'projects');
|
|
if (!$has_permission) {
|
|
$this->db->where('project_id IN (SELECT project_id FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'project_members WHERE staff_id=' . get_staff_user_id() . ')');
|
|
}
|
|
}
|
|
if (is_client_logged_in()) {
|
|
$this->db->where('visible_to_customer', 1);
|
|
}
|
|
if (is_numeric($id)) {
|
|
$this->db->where('project_id', $id);
|
|
}
|
|
if (is_numeric($limit)) {
|
|
$this->db->limit($limit);
|
|
}
|
|
$this->db->order_by('dateadded', 'desc');
|
|
$activities = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'project_activity')->result_array();
|
|
$i = 0;
|
|
foreach ($activities as $activity) {
|
|
$seconds = get_string_between($activity['additional_data'], '<seconds>', '</seconds>');
|
|
$other_lang_keys = get_string_between($activity['additional_data'], '<lang>', '</lang>');
|
|
$_additional_data = $activity['additional_data'];
|
|
if ($seconds != '') {
|
|
$_additional_data = str_replace('<seconds>' . $seconds . '</seconds>', seconds_to_time_format($seconds), $_additional_data);
|
|
}
|
|
if ($other_lang_keys != '') {
|
|
$_additional_data = str_replace('<lang>' . $other_lang_keys . '</lang>', _l($other_lang_keys), $_additional_data);
|
|
}
|
|
if (strpos($_additional_data, 'project_status_') !== false) {
|
|
$_additional_data = $this->get_project_status_by_id(strafter($_additional_data, 'project_status_'));
|
|
if (isset($_additional_data['name'])) {
|
|
$_additional_data = $_additional_data['name'];
|
|
}
|
|
}
|
|
$activities[$i]['description'] = _l($activities[$i]['description_key']);
|
|
$activities[$i]['additional_data'] = $_additional_data;
|
|
$activities[$i]['project_name'] = get_project_name_by_id($activity['project_id'], $playground);
|
|
unset($activities[$i]['description_key']);
|
|
$i++;
|
|
}
|
|
return $activities;
|
|
}
|
|
|
|
public function log_activity($project_id, $description_key, $additional_data = '', $visible_to_customer = 1, $playground = false) {
|
|
if (!DEFINED('CRON')) {
|
|
$this->load->model('staff_model');
|
|
if (is_client_logged_in()) {
|
|
$data['contact_id'] = get_contact_user_id();
|
|
$data['staff_id'] = 0;
|
|
$data['fullname'] = get_contact_full_name(get_contact_user_id());
|
|
} else if (is_staff_logged_in()) {
|
|
$data['contact_id'] = 0;
|
|
$data['staff_id'] = get_staff_user_id();
|
|
$data['fullname'] = $this->staff_model->get_staff_full_name(get_staff_user_id(), $playground);
|
|
}
|
|
} else {
|
|
$data['contact_id'] = 0;
|
|
$data['staff_id'] = 0;
|
|
$data['fullname'] = '[CRON]';
|
|
}
|
|
$data['description_key'] = $description_key;
|
|
$data['additional_data'] = $additional_data;
|
|
$data['visible_to_customer'] = $visible_to_customer;
|
|
$data['project_id'] = $project_id;
|
|
$data['dateadded'] = date('Y-m-d H:i:s');
|
|
$data = hooks()->apply_filters('before_log_project_activity', $data);
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_activity', $data);
|
|
}
|
|
|
|
public function new_project_file_notification($file_id, $project_id, $playground = false) {
|
|
$file = $this->get_file($file_id, $playground);
|
|
$additional_data = $file->file_name;
|
|
$this->log_activity($project_id, 'project_activity_uploaded_file', $additional_data, $file->visible_to_customer);
|
|
$members = $this->get_project_members($project_id, false, $playground);
|
|
$notification_data = ['description' => 'not_project_file_uploaded', 'link' => 'projects/view/' . $project_id . '?group=project_files&file_id=' . $file_id, ];
|
|
if (is_client_logged_in()) {
|
|
$notification_data['fromclientid'] = get_contact_user_id();
|
|
} else {
|
|
$notification_data['fromuserid'] = get_staff_user_id();
|
|
}
|
|
$notifiedUsers = [];
|
|
foreach ($members as $member) {
|
|
if ($member['staff_id'] == get_staff_user_id() && !is_client_logged_in()) {
|
|
continue;
|
|
}
|
|
$notification_data['touserid'] = $member['staff_id'];
|
|
if (add_notification($notification_data)) {
|
|
array_push($notifiedUsers, $member['staff_id']);
|
|
}
|
|
}
|
|
pusher_trigger_notification($notifiedUsers);
|
|
$this->send_project_email_template($project_id, 'project_file_to_staff', 'project_file_to_customer', $file->visible_to_customer, ['staff' => ['discussion_id' => $file_id, 'discussion_type' => 'file'], 'customers' => ['customer_template' => true, 'discussion_id' => $file_id, 'discussion_type' => 'file'], ], $playground);
|
|
}
|
|
|
|
public function add_external_file($data, $playground = false) {
|
|
$insert['dateadded'] = date('Y-m-d H:i:s');
|
|
$insert['project_id'] = $data['project_id'];
|
|
$insert['external'] = $data['external'];
|
|
$insert['visible_to_customer'] = $data['visible_to_customer'];
|
|
$insert['file_name'] = $data['files'][0]['name'];
|
|
$insert['subject'] = $data['files'][0]['name'];
|
|
$insert['external_link'] = $data['files'][0]['link'];
|
|
$path_parts = pathinfo($data['files'][0]['name']);
|
|
$insert['filetype'] = get_mime_by_extension('.' . $path_parts['extension']);
|
|
if (isset($data['files'][0]['thumbnailLink'])) {
|
|
$insert['thumbnail_link'] = $data['files'][0]['thumbnailLink'];
|
|
}
|
|
if (isset($data['staffid'])) {
|
|
$insert['staffid'] = $data['staffid'];
|
|
} else if (isset($data['contact_id'])) {
|
|
$insert['contact_id'] = $data['contact_id'];
|
|
}
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_files', $insert);
|
|
$insert_id = $this->db->insert_id();
|
|
if ($insert_id) {
|
|
$this->new_project_file_notification($insert_id, $data['project_id'], $playground);
|
|
return $insert_id;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function send_project_email_template($project_id, $staff_template, $customer_template, $action_visible_to_customer, $additional_data = [], $playground = false) {
|
|
if (count($additional_data) == 0) {
|
|
$additional_data['customers'] = [];
|
|
$additional_data['staff'] = [];
|
|
} else if (count($additional_data) == 1) {
|
|
if (!isset($additional_data['staff'])) {
|
|
$additional_data['staff'] = [];
|
|
} else {
|
|
$additional_data['customers'] = [];
|
|
}
|
|
}
|
|
$project = $this->get($project_id, [], $playground);
|
|
$members = $this->get_project_members($project_id, false, $playground);
|
|
foreach ($members as $member) {
|
|
if (is_staff_logged_in() && $member['staff_id'] == get_staff_user_id()) {
|
|
continue;
|
|
}
|
|
$mailTemplate = mail_template($staff_template, $project, $member, $additional_data['staff']);
|
|
if (isset($additional_data['attachments'])) {
|
|
foreach ($additional_data['attachments'] as $attachment) {
|
|
$mailTemplate->add_attachment($attachment);
|
|
}
|
|
}
|
|
$mailTemplate->send();
|
|
}
|
|
if ($action_visible_to_customer == 1) {
|
|
$this->load->model('clients_model');
|
|
$contacts = $this->clients_model->get_contacts_for_project_notifications($project_id, 'project_emails', $playground);
|
|
foreach ($contacts as $contact) {
|
|
if (is_client_logged_in() && $contact['id'] == get_contact_user_id()) {
|
|
continue;
|
|
}
|
|
$mailTemplate = mail_template($customer_template, $project, $contact, $additional_data['customers']);
|
|
if (isset($additional_data['attachments'])) {
|
|
foreach ($additional_data['attachments'] as $attachment) {
|
|
$mailTemplate->add_attachment($attachment);
|
|
}
|
|
}
|
|
$mailTemplate->send();
|
|
}
|
|
}
|
|
}
|
|
|
|
private function _get_project_billing_data($id, $playground = false) {
|
|
$this->db->select('billing_type,project_rate_per_hour');
|
|
$this->db->where('id', $id);
|
|
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->row();
|
|
}
|
|
|
|
public function total_logged_time_by_billing_type($id, $conditions = [], $playground = false) {
|
|
$project_data = $this->_get_project_billing_data($id, $playground);
|
|
$data = [];
|
|
$this->load->model('projects_model');
|
|
if ($project_data->billing_type == 2) {
|
|
$seconds = $this->total_logged_time($id, $playground);
|
|
$data = $this->projects_model->calculate_total_by_project_hourly_rate($seconds, $project_data->project_rate_per_hour, $playground);
|
|
$data['logged_time'] = $data['hours'];
|
|
} else if ($project_data->billing_type == 3) {
|
|
$data = $this->_get_data_total_logged_time($id, $playground);
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
public function data_billable_time($id, $playground = false) {
|
|
return $this->_get_data_total_logged_time($id, ['billable' => 1, ], $playground);
|
|
}
|
|
|
|
public function data_billed_time($id, $playground = false) {
|
|
return $this->_get_data_total_logged_time($id, ['billable' => 1, 'billed' => 1, ], $playground);
|
|
}
|
|
|
|
public function data_unbilled_time($id, $playground = false) {
|
|
return $this->_get_data_total_logged_time($id, ['billable' => 1, 'billed' => 0, ], $playground);
|
|
}
|
|
|
|
private function _delete_discussion_comments($id, $type, $playground = false) {
|
|
$this->db->where('discussion_id', $id);
|
|
$this->db->where('discussion_type', $type);
|
|
$comments = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments')->result_array();
|
|
foreach ($comments as $comment) {
|
|
$this->delete_discussion_comment_attachment($comment['file_name'], $id, $playground);
|
|
}
|
|
$this->db->where('discussion_id', $id);
|
|
$this->db->where('discussion_type', $type);
|
|
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussioncomments');
|
|
}
|
|
|
|
private function _get_data_total_logged_time($id, $conditions = [], $playground = false) {
|
|
$project_data = $this->_get_project_billing_data($id, $playground);
|
|
$tasks = $this->get_tasks($id, $conditions, false, false, null, $playground);
|
|
if ($project_data->billing_type == 3) {
|
|
$data = $this->calculate_total_by_task_hourly_rate($tasks);
|
|
$data['logged_time'] = seconds_to_time_format($data['total_seconds']);
|
|
} else if ($project_data->billing_type == 2) {
|
|
$seconds = 0;
|
|
foreach ($tasks as $task) {
|
|
$seconds+= $task['total_logged_time'];
|
|
}
|
|
$data = $this->calculate_total_by_project_hourly_rate($seconds, $project_data->project_rate_per_hour);
|
|
$data['logged_time'] = $data['hours'];
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
private function _update_discussion_last_activity($id, $type, $playground = false) {
|
|
if ($type == 'file') {
|
|
$table = db_prefix() . ($playground ? 'playground_' : '') . 'project_files';
|
|
} else if ($type == 'regular') {
|
|
$table = db_prefix() . ($playground ? 'playground_' : '') . 'projectdiscussions';
|
|
}
|
|
$this->db->where('id', $id);
|
|
$this->db->update($table, ['last_activity' => date('Y-m-d H:i:s'), ]);
|
|
}
|
|
|
|
public function send_project_email_mentioned_users($project_id, $staff_template, $staff, $additional_data = [], $playground = false) {
|
|
$this->load->model('staff_model');
|
|
$project = $this->get($project_id, [], $playground);
|
|
foreach ($staff as $staffId) {
|
|
if (is_staff_logged_in() && $staffId == get_staff_user_id()) {
|
|
continue;
|
|
}
|
|
$member = (array)$this->staff_model->get($staffId, [], $playground);
|
|
$member['staff_id'] = $member['staffid'];
|
|
$mailTemplate = mail_template($staff_template, $project, $member, $additional_data['staff']);
|
|
if (isset($additional_data['attachments'])) {
|
|
foreach ($additional_data['attachments'] as $attachment) {
|
|
$mailTemplate->add_attachment($attachment);
|
|
}
|
|
}
|
|
$mailTemplate->send();
|
|
}
|
|
}
|
|
|
|
public function convert_estimate_items_to_tasks($project_id, $items, $assignees, $project_data, $project_settings, $playground = false) {
|
|
$this->load->model('tasks_model');
|
|
foreach ($items as $index => $itemId) {
|
|
$this->db->where('id', $itemId);
|
|
$_item = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'itemable')->row();
|
|
$data = ['billable' => 'on', 'name' => $_item->description, 'description' => $_item->long_description, 'startdate' => _d($project_data['start_date']), 'duedate' => '', 'rel_type' => 'project', 'rel_id' => $project_id, 'hourly_rate' => $project_data['billing_type'] == 3 ? $_item->rate : 0, 'priority' => get_option('default_task_priority'), 'withDefaultAssignee' => false, ];
|
|
if (isset($project_settings->view_tasks)) {
|
|
$data['visible_to_client'] = 'on';
|
|
}
|
|
$task_id = $this->tasks_model->add($data, false, $playground);
|
|
if ($task_id) {
|
|
$staff_id = $assignees[$index];
|
|
$this->tasks_model->add_task_assignees(['taskid' => $task_id, 'assignee' => intval($staff_id), ], false, false, $playground);
|
|
if (!$this->is_member($project_id, $staff_id, $playground)) {
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_members', ['project_id' => $project_id, 'staff_id' => $staff_id, ]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles upload for project files
|
|
* @param mixed $project_id project id
|
|
* @return boolean
|
|
*/
|
|
public function handle_project_file_uploads($project_id, $playground = false) {
|
|
$hookData = hooks()->apply_filters('before_handle_project_file_uploads', [
|
|
'project_id' => $project_id,
|
|
'index_name' => 'file',
|
|
'handled_externally' => false, // e.g. module upload to s3
|
|
'handled_externally_successfully' => false,
|
|
'files' => $_FILES
|
|
]);
|
|
if ($hookData['handled_externally']) {
|
|
return $hookData['handled_externally_successfully'];
|
|
}
|
|
$filesIDS = [];
|
|
$errors = [];
|
|
$misc_model = new Misc_model();
|
|
if (isset($_FILES['file']['name']) && ($_FILES['file']['name'] != '' || is_array($_FILES['file']['name']) && count($_FILES['file']['name']) > 0)) {
|
|
hooks()->do_action('before_upload_project_attachment', $project_id);
|
|
if (!is_array($_FILES['file']['name'])) {
|
|
$_FILES['file']['name'] = [$_FILES['file']['name']];
|
|
$_FILES['file']['type'] = [$_FILES['file']['type']];
|
|
$_FILES['file']['tmp_name'] = [$_FILES['file']['tmp_name']];
|
|
$_FILES['file']['error'] = [$_FILES['file']['error']];
|
|
$_FILES['file']['size'] = [$_FILES['file']['size']];
|
|
}
|
|
$path = $this->misc_model->get_upload_path_by_type('project', $playground) . $project_id . '/';
|
|
for ($i = 0;$i < count($_FILES['file']['name']);$i++) {
|
|
if (_perfex_upload_error($_FILES['file']['error'][$i])) {
|
|
$errors[$_FILES['file']['name'][$i]] = _perfex_upload_error($_FILES['file']['error'][$i]);
|
|
continue;
|
|
}
|
|
// Get the temp file path
|
|
$tmpFilePath = $_FILES['file']['tmp_name'][$i];
|
|
// Make sure we have a filepath
|
|
if (!empty($tmpFilePath) && $tmpFilePath != '') {
|
|
_maybe_create_upload_path($path);
|
|
$originalFilename = unique_filename($path, $_FILES['file']['name'][$i]);
|
|
$filename = app_generate_hash() . '.' . get_file_extension($originalFilename);
|
|
// In case client side validation is bypassed
|
|
if (!_upload_extension_allowed($filename)) {
|
|
continue;
|
|
}
|
|
$newFilePath = $path . $filename;
|
|
// Upload the file into the company uploads dir
|
|
if (move_uploaded_file($tmpFilePath, $newFilePath)) {
|
|
if (is_client_logged_in()) {
|
|
$contact_id = get_contact_user_id();
|
|
$staffid = 0;
|
|
} else {
|
|
$staffid = get_staff_user_id();
|
|
$contact_id = 0;
|
|
}
|
|
$data = ['project_id' => $project_id, 'file_name' => $filename, 'original_file_name' => $originalFilename, 'filetype' => $_FILES['file']['type'][$i], 'dateadded' => date('Y-m-d H:i:s'), 'staffid' => $staffid, 'contact_id' => $contact_id, 'subject' => $originalFilename, ];
|
|
if (is_client_logged_in()) {
|
|
$data['visible_to_customer'] = 1;
|
|
} else {
|
|
$data['visible_to_customer'] = ($this->input->post('visible_to_customer') == 'true' ? 1 : 0);
|
|
}
|
|
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'project_files', $data);
|
|
$insert_id = $this->db->insert_id();
|
|
if ($insert_id) {
|
|
if (is_image($newFilePath)) {
|
|
create_img_thumb($path, $filename);
|
|
}
|
|
array_push($filesIDS, $insert_id);
|
|
} else {
|
|
unlink($newFilePath);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (count($filesIDS) > 0) {
|
|
end($filesIDS);
|
|
$lastFileID = key($filesIDS);
|
|
$this->new_project_file_notification($filesIDS[$lastFileID], $project_id, $playground);
|
|
}
|
|
if (count($errors) > 0) {
|
|
$message = '';
|
|
foreach ($errors as $filename => $error_message) {
|
|
$message.= $filename . ' - ' . $error_message . '<br />';
|
|
}
|
|
header('HTTP/1.0 400 Bad error');
|
|
echo $message;
|
|
die;
|
|
}
|
|
if (count($filesIDS) > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Handle upload for project discussions comment
|
|
* Function for jquery-comment plugin
|
|
* @param mixed $discussion_id discussion id
|
|
* @param mixed $post_data additional post data from the comment
|
|
* @param array $insert_data insert data to be parsed if needed
|
|
* @return array
|
|
*/
|
|
public function handle_project_discussion_comment_attachments($discussion_id, $post_data, $insert_data, $playground = false) {
|
|
if (isset($_FILES['file']['name']) && _perfex_upload_error($_FILES['file']['error'])) {
|
|
header('HTTP/1.0 400 Bad error');
|
|
echo json_encode(['message' => _perfex_upload_error($_FILES['file']['error']) ]);
|
|
die;
|
|
}
|
|
$hookData = hooks()->apply_filters('before_handle_project_discussion_comment_attachment', [
|
|
'discussion_id' => $discussion_id,
|
|
'post_data' => $post_data,
|
|
'insert_data' => $insert_data,
|
|
'index_name' => 'file',
|
|
'handled_externally' => false, // e.g. module upload to s3
|
|
'handled_externally_successfully' => false,
|
|
'files' => $_FILES
|
|
]);
|
|
if ($hookData['handled_externally']) {
|
|
return $insert_data;
|
|
}
|
|
if (isset($_FILES['file']['name'])) {
|
|
hooks()->do_action('before_upload_project_discussion_comment_attachment');
|
|
$path = FCPATH . 'uploads/' . ($playground ? 'playground_' : '') . 'discussions' . $discussion_id . '/';
|
|
// Check for all cases if this extension is allowed
|
|
if (!_upload_extension_allowed($_FILES['file']['name'])) {
|
|
header('HTTP/1.0 400 Bad error');
|
|
echo json_encode(['message' => _l('file_php_extension_blocked') ]);
|
|
die;
|
|
}
|
|
// Get the temp file path
|
|
$tmpFilePath = $_FILES['file']['tmp_name'];
|
|
// Make sure we have a filepath
|
|
if (!empty($tmpFilePath) && $tmpFilePath != '') {
|
|
_maybe_create_upload_path($path);
|
|
$filename = unique_filename($path, $_FILES['file']['name']);
|
|
$newFilePath = $path . $filename;
|
|
// Upload the file into the temp dir
|
|
if (move_uploaded_file($tmpFilePath, $newFilePath)) {
|
|
$insert_data['file_name'] = $filename;
|
|
if (isset($_FILES['file']['type'])) {
|
|
$insert_data['file_mime_type'] = $_FILES['file']['type'];
|
|
} else {
|
|
$insert_data['file_mime_type'] = get_mime_by_extension($filename);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $insert_data;
|
|
}
|
|
|
|
/**
|
|
* Get project billing type
|
|
* @param mixed $project_id
|
|
* @return mixed
|
|
*/
|
|
public function get_project_billing_type($project_id, $playground = false)
|
|
{
|
|
$CI = & get_instance();
|
|
$CI->db->select('billing_type');
|
|
$CI->db->where('id', $project_id);
|
|
$project = $CI->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'projects')->row();
|
|
if ($project) {
|
|
return $project->billing_type;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
*
|
|
* @param int $id
|
|
* @param string $type
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_project_overview_weekly_chart_data($id, $type = 'this_week') {
|
|
_deprecated_function('Projects_model::get_project_overview_weekly_chart_data', '2.9.2', 'HoursOverviewChart class');
|
|
return (new HoursOverviewChart($id, $type))->get();
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
*
|
|
* @param array $filters
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_all_projects_gantt_data($filters = []) {
|
|
_deprecated_function('Projects_model::get_all_projects_gantt_data', '2.9.2', 'AllProjectsGantt class');
|
|
return (new AllProjectsGantt($filters))->get();
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_gantt_data($project_id, $type = 'milestones', $taskStatus = null, $type_where = []) {
|
|
_deprecated_function('Projects_model::get_gantt_data', '2.9.2', 'Gantt class');
|
|
return (new Gantt($project_id, $type))->forTaskStatus($taskStatus)->excludeMilestonesFromCustomer(isset($type_where['hide_from_customer']) && $type_where['hide_from_customer'] == 1)->get();
|
|
}
|
|
}
|