projects.php

 avatar
unknown
php
2 years ago
158 kB
8
Indexable
<?php



namespace App\Controllers;



class Projects extends Security_Controller {



    protected $Project_settings_model;

    protected $Checklist_items_model;

    protected $Likes_model;

    protected $Pin_comments_model;

    protected $File_category_model;

    protected $Task_priority_model;



    public function __construct() {

        parent::__construct();

        if ($this->has_all_projects_restricted_role()) {

            app_redirect("forbidden");

        }



        $this->Project_settings_model = model('App\Models\Project_settings_model');

        $this->Checklist_items_model = model('App\Models\Checklist_items_model');

        $this->Likes_model = model('App\Models\Likes_model');

        $this->Pin_comments_model = model('App\Models\Pin_comments_model');

        $this->File_category_model = model('App\Models\File_category_model');

        $this->Task_priority_model = model("App\Models\Task_priority_model");

    }



    private function can_delete_projects($project_id = 0) {

        if ($this->login_user->user_type == "staff") {

            if ($this->can_manage_all_projects()) {

                return true;

            }



            $can_delete_projects = get_array_value($this->login_user->permissions, "can_delete_projects");

            $can_delete_only_own_created_projects = get_array_value($this->login_user->permissions, "can_delete_only_own_created_projects");



            if ($can_delete_projects) {

                return true;

            }



            if ($project_id) {

                $project_info = $this->Projects_model->get_one($project_id);

                if ($can_delete_only_own_created_projects && $project_info->created_by === $this->login_user->id) {

                    return true;

                }

            } else if ($can_delete_only_own_created_projects) { //no project given and the user has partial access

                return true;

            }

        }

    }



    private function can_add_remove_project_members() {

        if ($this->login_user->user_type == "staff") {

            if ($this->login_user->is_admin) {

                return true;

            } else {

                if (get_array_value($this->login_user->permissions, "show_assigned_tasks_only") !== "1") {

                    if ($this->can_manage_all_projects()) {

                        return true;

                    } else if (get_array_value($this->login_user->permissions, "can_add_remove_project_members") == "1") {

                        return true;

                    }

                }

            }

        }

    }



    private function can_create_milestones() {

        if ($this->login_user->user_type == "staff") {

            if ($this->can_manage_all_projects()) {

                return true;

            } else if (get_array_value($this->login_user->permissions, "can_create_milestones") == "1") {

                //check is user a project member

                return $this->is_user_a_project_member;

            }

        }

    }



    private function can_edit_milestones() {

        if ($this->login_user->user_type == "staff") {

            if ($this->can_manage_all_projects()) {

                return true;

            } else if (get_array_value($this->login_user->permissions, "can_edit_milestones") == "1") {

                //check is user a project member

                return $this->is_user_a_project_member;

            }

        }

    }



    private function can_delete_milestones() {

        if ($this->login_user->user_type == "staff") {

            if ($this->can_manage_all_projects()) {

                return true;

            } else if (get_array_value($this->login_user->permissions, "can_delete_milestones") == "1") {

                //check is user a project member

                return $this->is_user_a_project_member;

            }

        }

    }



    private function can_delete_files($uploaded_by = 0) {

        if ($this->login_user->user_type == "staff") {

            if ($this->can_manage_all_projects()) {

                return true;

            } else if (get_array_value($this->login_user->permissions, "can_delete_files") == "1") {

                //check is user a project member

                return $this->is_user_a_project_member;

            }

        } else {

            if (get_setting("client_can_delete_own_files_in_project") && $this->login_user->id == $uploaded_by) {

                return true;

            }

        }

    }



    private function can_view_files() {

        if ($this->login_user->user_type == "staff") {

            if ($this->can_manage_all_projects()) {

                return true;

            } else {

                //check is user a project member

                return $this->is_user_a_project_member;

            }

        } else {

            //check settings for client's project permission

            if (get_setting("client_can_view_project_files")) {

                return $this->is_clients_project;

            }

        }

    }



    private function can_add_files() {

        if ($this->login_user->user_type == "staff") {

            if ($this->can_manage_all_projects()) {

                return true;

            } else {

                //check is user a project member

                return $this->is_user_a_project_member;

            }

        } else {

            //check settings for client's project permission

            if (get_setting("client_can_add_project_files")) {

                return $this->is_clients_project;

            }

        }

    }



    private function can_comment_on_files() {

        if ($this->login_user->user_type == "staff") {

            if ($this->can_manage_all_projects()) {

                return true;

            } else {

                //check is user a project member

                return $this->is_user_a_project_member;

            }

        } else {

            //check settings for client's project permission

            if (get_setting("client_can_comment_on_files")) {

                //even the settings allow to create/edit task, the client can only comment on their own project's files

                return $this->is_clients_project;

            }

        }

    }



    private function can_view_gantt() {

        //check gantt module

        if (get_setting("module_gantt")) {

            if ($this->login_user->user_type == "staff") {

                if ($this->can_manage_all_projects()) {

                    return true;

                } else {

                    //check is user a project member

                    return $this->is_user_a_project_member;

                }

            } else {

                //check settings for client's project permission

                if (get_setting("client_can_view_gantt")) {

                    //even the settings allow to view gantt, the client can only view on their own project's gantt

                    return $this->is_clients_project;

                }

            }

        }

    }



    /* load project view */



    function index() {

        app_redirect("projects/all_projects");

    }



    function all_projects($status_id = 0) {

        validate_numeric_value($status_id);

        $view_data['project_labels_dropdown'] = json_encode($this->make_labels_dropdown("project", "", true));



        $view_data["can_create_projects"] = $this->can_create_projects();



        $view_data["custom_field_headers"] = $this->Custom_fields_model->get_custom_field_headers_for_table("projects", $this->login_user->is_admin, $this->login_user->user_type);

        $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("projects", $this->login_user->is_admin, $this->login_user->user_type);



        $view_data["selected_status_id"] = $status_id;

        $view_data['project_statuses'] = $this->Project_status_model->get_details()->getResult();



        if ($this->login_user->user_type === "staff") {

            $view_data["can_edit_projects"] = $this->can_edit_projects();

            $view_data["can_delete_projects"] = $this->can_delete_projects();



            return $this->template->rander("projects/index", $view_data);

        } else {

            $view_data['client_id'] = $this->login_user->client_id;

            $view_data['page_type'] = "full";

            return $this->template->rander("clients/projects/index", $view_data);

        }

    }



    /* load project  add/edit modal */



    function modal_form() {

        $project_id = $this->request->getPost('id');

        $client_id = $this->request->getPost('client_id');



        if ($project_id) {

            if (!$this->can_edit_projects($project_id)) {

                app_redirect("forbidden");

            }

        } else {

            if (!$this->can_create_projects()) {

                app_redirect("forbidden");

            }

        }





        $view_data["client_id"] = $client_id;

        $view_data['model_info'] = $this->Projects_model->get_one($project_id);

        if ($client_id) {

            $view_data['model_info']->client_id = $client_id;

        }



        //check if it's from estimate. if so, then prepare for project

        $estimate_id = $this->request->getPost('estimate_id');

        if ($estimate_id) {

            $view_data['model_info']->estimate_id = $estimate_id;

        }



        //check if it's from order. If so, then prepare for project

        $order_id = $this->request->getPost('order_id');

        if ($order_id) {

            $view_data['model_info']->order_id = $order_id;

        }



        $view_data["custom_fields"] = $this->Custom_fields_model->get_combined_details("projects", $view_data['model_info']->id, $this->login_user->is_admin, $this->login_user->user_type)->getResult();



        $view_data['hide_clients_dropdown'] = false;



        if (!$this->login_user->is_admin && !get_array_value($this->login_user->permissions, "client") && !get_array_value($this->login_user->permissions, "client_specific")) {

            //user can't access clients. don't show clients dropdown

            $view_data['clients_dropdown'] = array();

            $view_data['hide_clients_dropdown'] = true;

        } else {

            $view_data['clients_dropdown'] = $this->_get_clients_dropdown_with_permission();

        }



        $view_data['label_suggestions'] = $this->make_labels_dropdown("project", $view_data['model_info']->labels);

        $view_data['statuses'] = $this->Project_status_model->get_details()->getResult();

        $view_data["can_edit_projects"] = $this->can_edit_projects();



        return $this->template->view('projects/modal_form', $view_data);

    }



    //get clients dropdown

    private function _get_clients_dropdown_with_permission() {

        $clients_dropdown = array();



        if ($this->login_user->is_admin || get_array_value($this->login_user->permissions, "client")) {

            $access_client = $this->get_access_info("client");

            $clients = $this->Clients_model->get_details(array("show_own_clients_only_user_id" => $this->show_own_clients_only_user_id(), "client_groups" => $access_client->allowed_client_groups))->getResult();

            foreach ($clients as $client) {

                $clients_dropdown[$client->id] = $client->company_name;

            }

        }



        return $clients_dropdown;

    }



    /* insert or update a project */



    function save() {



        $id = $this->request->getPost('id');



        if ($id) {

            if (!$this->can_edit_projects($id)) {

                app_redirect("forbidden");

            }

        } else {

            if (!$this->can_create_projects()) {

                app_redirect("forbidden");

            }

        }



        $this->validate_submitted_data(array(

            "title" => "required"

        ));



        $estimate_id = $this->request->getPost('estimate_id');

        $status_id = $this->request->getPost('status_id');

        $order_id = $this->request->getPost('order_id');

        $project_type = $this->request->getPost('project_type');



        $data = array(

            "title" => $this->request->getPost('title'),

            "description" => $this->request->getPost('description'),

            "client_id" => ($project_type === "internal_project") ? 0 : $this->request->getPost('client_id'),

            "start_date" => $this->request->getPost('start_date'),

            "deadline" => $this->request->getPost('deadline'),

            "project_type" => $project_type,

            "price" => unformat_currency($this->request->getPost('price')),

            "labels" => $this->request->getPost('labels'),

            "status_id" => $status_id ? $status_id : 1,

            "estimate_id" => $estimate_id,

            "order_id" => $order_id

        );



        if (!$id) {

            $data["created_date"] = get_current_utc_time();

            $data["created_by"] = $this->login_user->id;

        }





        //created by client? overwrite the client id for safety

        if ($this->login_user->user_type === "clinet") {

            $data["client_id"] = $this->login_user->client_id;

        }





        $data = clean_data($data);



        //set null value after cleaning the data

        if (!$data["start_date"]) {

            $data["start_date"] = NULL;

        }



        if (!$data["deadline"]) {

            $data["deadline"] = NULL;

        }



        $save_id = $this->Projects_model->ci_save($data, $id);

        if ($save_id) {



            save_custom_fields("projects", $save_id, $this->login_user->is_admin, $this->login_user->user_type);



            //send notification

            if ($status_id == 2) {

                log_notification("project_completed", array("project_id" => $save_id));

            }



            if (!$id) {



                if ($this->login_user->user_type === "staff") {

                    //this is a new project and created by team members

                    //add default project member after project creation

                    $data = array(

                        "project_id" => $save_id,

                        "user_id" => $this->login_user->id,

                        "is_leader" => 1

                    );

                    $this->Project_members_model->save_member($data);

                }



                //created from estimate? save the project id

                if ($estimate_id) {

                    $data = array("project_id" => $save_id);

                    $this->Estimates_model->ci_save($data, $estimate_id);

                }



                //created from order? save the project id

                if ($order_id) {

                    $data = array("project_id" => $save_id);

                    $this->Orders_model->ci_save($data, $order_id);

                }



                log_notification("project_created", array("project_id" => $save_id));

            }

            echo json_encode(array("success" => true, "data" => $this->_row_data($save_id), 'id' => $save_id, 'message' => app_lang('record_saved')));

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('error_occurred')));

        }

    }



    /* Show a modal to clone a project */



    function clone_project_modal_form() {



        $project_id = $this->request->getPost('id');



        if (!$this->can_create_projects()) {

            app_redirect("forbidden");

        }





        $view_data['model_info'] = $this->Projects_model->get_one($project_id);



        $view_data['clients_dropdown'] = $this->Clients_model->get_dropdown_list(array("company_name"), "id", array("is_lead" => 0));



        $view_data['label_suggestions'] = $this->make_labels_dropdown("project", $view_data['model_info']->labels);



        $view_data["custom_fields"] = $this->Custom_fields_model->get_combined_details("projects", $view_data['model_info']->id, 1, "staff")->getResult(); //we have to keep this regarding as an admin user because non-admin user also can acquire the access to clone a project



        return $this->template->view('projects/clone_project_modal_form', $view_data);

    }



    /* create a new project from another project */



    function save_cloned_project() {



        ini_set('max_execution_time', 300); //300 seconds 



        $project_id = $this->request->getPost('project_id');

        $project_start_date = $this->request->getPost('start_date');



        if (!$this->can_create_projects()) {

            app_redirect("forbidden");

        }



        $this->validate_submitted_data(array(

            "title" => "required"

        ));



        $copy_same_assignee_and_collaborators = $this->request->getPost("copy_same_assignee_and_collaborators");

        $copy_milestones = $this->request->getPost("copy_milestones");

        $change_the_milestone_dates_based_on_project_start_date = $this->request->getPost("change_the_milestone_dates_based_on_project_start_date");

        $move_all_tasks_to_to_do = $this->request->getPost("move_all_tasks_to_to_do");

        $copy_tasks_start_date_and_deadline = $this->request->getPost("copy_tasks_start_date_and_deadline");

        $change_the_tasks_start_date_and_deadline_based_on_project_start_date = $this->request->getPost("change_the_tasks_start_date_and_deadline_based_on_project_start_date");

        $project_type = $this->request->getPost('project_type');



        //prepare new project data

        $now = get_current_utc_time();

        $data = array(

            "title" => $this->request->getPost('title'),

            "description" => $this->request->getPost('description'),

            "client_id" => ($project_type === "internal_project") ? 0 : $this->request->getPost('client_id'),

            "start_date" => $project_start_date,

            "deadline" => $this->request->getPost('deadline'),

            "project_type" => $project_type,

            "price" => unformat_currency($this->request->getPost('price')),

            "created_date" => $now,

            "created_by" => $this->login_user->id,

            "labels" => $this->request->getPost('labels'),

            "status_id" => 1,

        );



        if (!$data["start_date"]) {

            $data["start_date"] = NULL;

        }



        if (!$data["deadline"]) {

            $data["deadline"] = NULL;

        }





        //add new project

        $new_project_id = $this->Projects_model->ci_save($data);



        //old project info

        $old_project_info = $this->Projects_model->get_one($project_id);



        //add milestones

        //when the new milestones will be created the ids will be different. so, we have to convert the milestone ids. 

        $milestones_array = array();



        if ($copy_milestones) {

            $milestones = $this->Milestones_model->get_all_where(array("project_id" => $project_id, "deleted" => 0))->getResult();

            foreach ($milestones as $milestone) {

                $old_milestone_id = $milestone->id;



                //prepare new milestone data. remove id from existing data

                $milestone->project_id = $new_project_id;

                $milestone_data = (array) $milestone;

                unset($milestone_data["id"]);



                //add new milestone and keep a relation with new id and old id

                $milestones_array[$old_milestone_id] = $this->Milestones_model->ci_save($milestone_data);

            }

        } else if ($change_the_milestone_dates_based_on_project_start_date && $old_project_info->start_date && $project_start_date) {

            $milestones = $this->Milestones_model->get_all_where(array("project_id" => $project_id, "deleted" => 0))->getResult();

            foreach ($milestones as $milestone) {

                $old_milestone_id = $milestone->id;



                //prepare new milestone data. remove id from existing data

                $milestone->project_id = $new_project_id;



                $old_project_start_date = $old_project_info->start_date;

                $old_milestone_due_date = $milestone->due_date;



                $milestone_due_date_day_diff = get_date_difference_in_days($old_milestone_due_date, $old_project_start_date);

                $milestone->due_date = add_period_to_date($project_start_date, $milestone_due_date_day_diff, "days");



                $milestone_data = (array) $milestone;

                unset($milestone_data["id"]);



                //add new milestone and keep a relation with new id and old id

                $milestones_array[$old_milestone_id] = $this->Milestones_model->ci_save($milestone_data);

            }

        }



        //we'll keep all new task ids vs old task ids. by this way, we'll add the checklist easily 

        $task_ids = array();



        //add tasks

        //first, save tasks whose are not sub tasks 

        $tasks = $this->Tasks_model->get_all_where(array("project_id" => $project_id, "deleted" => 0, "parent_task_id" => 0))->getResult();

        foreach ($tasks as $task) {

            $task_data = $this->_prepare_new_task_data_on_cloning_project($new_project_id, $milestones_array, $task, $copy_same_assignee_and_collaborators, $copy_tasks_start_date_and_deadline, $move_all_tasks_to_to_do, $change_the_tasks_start_date_and_deadline_based_on_project_start_date, $old_project_info, $project_start_date);



            //add new task

            $new_taks_id = $this->Tasks_model->ci_save($task_data);



            //bind old id with new

            $task_ids[$task->id] = $new_taks_id;



            //save custom fields of task

            $this->_save_custom_fields_on_cloning_project($task, $new_taks_id);

        }



        //secondly, save sub tasks

        $tasks = $this->Tasks_model->get_all_where(array("project_id" => $project_id, "deleted" => 0, "parent_task_id !=" => 0))->getResult();

        foreach ($tasks as $task) {

            $task_data = $this->_prepare_new_task_data_on_cloning_project($new_project_id, $milestones_array, $task, $copy_same_assignee_and_collaborators, $copy_tasks_start_date_and_deadline, $move_all_tasks_to_to_do, $change_the_tasks_start_date_and_deadline_based_on_project_start_date, $old_project_info, $project_start_date);

            //add parent task

            $task_data["parent_task_id"] = $task_ids[$task->parent_task_id];



            //add new task

            $new_taks_id = $this->Tasks_model->ci_save($task_data);



            //bind old id with new

            $task_ids[$task->id] = $new_taks_id;



            //save custom fields of task

            $this->_save_custom_fields_on_cloning_project($task, $new_taks_id);

        }



        //save task dependencies

        $tasks = $this->Tasks_model->get_all_tasks_where_have_dependency($project_id)->getResult();

        foreach ($tasks as $task) {

            if (array_key_exists($task->id, $task_ids)) {

                //save blocked by tasks 

                if ($task->blocked_by) {

                    //find the newly created tasks

                    $new_blocked_by_tasks = "";

                    $blocked_by_tasks_array = explode(',', $task->blocked_by);

                    foreach ($blocked_by_tasks_array as $blocked_by_task) {

                        if (array_key_exists($blocked_by_task, $task_ids)) {

                            if ($new_blocked_by_tasks) {

                                $new_blocked_by_tasks .= "," . $task_ids[$blocked_by_task];

                            } else {

                                $new_blocked_by_tasks = $task_ids[$blocked_by_task];

                            }

                        }

                    }



                    //update newly created task

                    if ($new_blocked_by_tasks) {

                        $blocked_by_task_data = array("blocked_by" => $new_blocked_by_tasks);

                        $this->Tasks_model->ci_save($blocked_by_task_data, $task_ids[$task->id]);

                    }

                }



                //save blocking tasks 

                if ($task->blocking) {

                    //find the newly created tasks

                    $new_blocking_tasks = "";

                    $blocking_tasks_array = explode(',', $task->blocking);

                    foreach ($blocking_tasks_array as $blocking_task) {

                        if (array_key_exists($blocking_task, $task_ids)) {

                            if ($new_blocking_tasks) {

                                $new_blocking_tasks .= "," . $task_ids[$blocking_task];

                            } else {

                                $new_blocking_tasks = $task_ids[$blocking_task];

                            }

                        }

                    }



                    //update newly created task

                    if ($new_blocking_tasks) {

                        $blocking_task_data = array("blocking" => $new_blocking_tasks);

                        $this->Tasks_model->ci_save($blocking_task_data, $task_ids[$task->id]);

                    }

                }

            }

        }



        //add project members

        $project_members = $this->Project_members_model->get_all_where(array("project_id" => $project_id, "deleted" => 0))->getResult();



        foreach ($project_members as $project_member) {

            //prepare new project member data. remove id from existing data

            $project_member->project_id = $new_project_id;

            $project_member_data = (array) $project_member;

            unset($project_member_data["id"]);



            $project_member_data["user_id"] = $project_member->user_id;



            $this->Project_members_model->save_member($project_member_data);

        }



        //add check lists

        $check_lists = $this->Checklist_items_model->get_all_checklist_of_project($project_id)->getResult();

        foreach ($check_lists as $list) {

            if (array_key_exists($list->task_id, $task_ids)) {

                $checklist_data = array(

                    "title" => $list->title,

                    "task_id" => $task_ids[$list->task_id],

                    "is_checked" => 0

                );



                $this->Checklist_items_model->ci_save($checklist_data);

            }

        }



        $project_settings = $this->Project_settings_model->get_details(array("project_id" => $project_id))->getResult();

        foreach ($project_settings as $project_setting) {

            $setting = $project_setting->setting_name;

            $value = $project_setting->setting_value;

            if (!$value) {

                $value = "";

            }



            $this->Project_settings_model->save_setting($new_project_id, $setting, $value);

        }



        if ($new_project_id) {

            //save custom fields of project

            save_custom_fields("projects", $new_project_id, 1, "staff"); //we have to keep this regarding as an admin user because non-admin user also can acquire the access to clone a project



            log_notification("project_created", array("project_id" => $new_project_id));



            echo json_encode(array("success" => true, 'id' => $new_project_id, 'message' => app_lang('project_cloned_successfully')));

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('error_occurred')));

        }

    }



    private function _prepare_new_task_data_on_cloning_project($new_project_id, $milestones_array, $task, $copy_same_assignee_and_collaborators, $copy_tasks_start_date_and_deadline, $move_all_tasks_to_to_do, $change_the_tasks_start_date_and_deadline_based_on_project_start_date, $old_project_info, $project_start_date) {

        //prepare new task data. 

        $task->project_id = $new_project_id;

        $milestone_id = get_array_value($milestones_array, $task->milestone_id);

        $task->milestone_id = $milestone_id ? $milestone_id : "";

        $task->status = "to_do";



        if (!$copy_same_assignee_and_collaborators) {

            $task->assigned_to = "";

            $task->collaborators = "";

        }



        $task_data = (array) $task;

        unset($task_data["id"]); //remove id from existing data



        if ($move_all_tasks_to_to_do) {

            $task_data["status"] = "to_do";

            $task_data["status_id"] = 1;

        }



        if (!$copy_tasks_start_date_and_deadline && !$change_the_tasks_start_date_and_deadline_based_on_project_start_date) {

            $task->start_date = NULL;

            $task->deadline = NULL;

        } else if ($change_the_tasks_start_date_and_deadline_based_on_project_start_date && $old_project_info->start_date && $project_start_date) {

            $old_project_start_date = $old_project_info->start_date;

            $old_task_start_date = $task->start_date;

            $old_task_end_date = $task->deadline;



            if ($old_task_start_date) {

                $start_date_day_diff = get_date_difference_in_days($old_task_start_date, $old_project_start_date);

                $task_data["start_date"] = add_period_to_date($project_start_date, $start_date_day_diff, "days");

            } else {

                $task_data["start_date"] = NULL;

            }



            if ($old_task_end_date) {

                $end_date_day_diff = get_date_difference_in_days($old_task_end_date, $old_project_start_date);

                $task_data["deadline"] = add_period_to_date($project_start_date, $end_date_day_diff, "days");

            } else {

                $task_data["deadline"] = NULL;

            }

        }



        return $task_data;

    }



    private function _save_custom_fields_on_cloning_project($task, $new_taks_id) {

        $old_custom_fields = $this->Custom_field_values_model->get_all_where(array("related_to_type" => "tasks", "related_to_id" => $task->id, "deleted" => 0))->getResult();



        //prepare new custom fields data

        foreach ($old_custom_fields as $field) {

            $field->related_to_id = $new_taks_id;



            $fields_data = (array) $field;

            unset($fields_data["id"]); //remove id from existing data

            //save custom field

            $this->Custom_field_values_model->ci_save($fields_data);

        }

    }



    /* delete a project */



    function delete() {

        $id = $this->request->getPost('id');



        if (!$this->can_delete_projects($id)) {

            app_redirect("forbidden");

        }



        if ($this->Projects_model->delete_project_and_sub_items($id)) {

            log_notification("project_deleted", array("project_id" => $id));



            try {

                app_hooks()->do_action("app_hook_data_delete", array(

                    "id" => $id,

                    "table" => get_db_prefix() . "projects",

                    "table_without_prefix" => "projects",

                ));

            } catch (\Exception $ex) {

                log_message('error', '[ERROR] {exception}', ['exception' => $ex]);

            }



            echo json_encode(array("success" => true, 'message' => app_lang('record_deleted')));

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('record_cannot_be_deleted')));

        }

    }



    /* list of projcts, prepared for datatable  */



    function list_data() {

        $this->access_only_team_members();



        $custom_fields = $this->Custom_fields_model->get_available_fields_for_table("projects", $this->login_user->is_admin, $this->login_user->user_type);



        $status_ids = $this->request->getPost('status_id') ? implode(",", $this->request->getPost('status_id')) : "";



        $options = array(

            "status_ids" => $status_ids,

            "project_label" => $this->request->getPost("project_label"),

            "custom_fields" => $custom_fields,

            "start_date_from" => $this->request->getPost("start_date_from"),

            "start_date_to" => $this->request->getPost("start_date_to"),

            "deadline" => $this->request->getPost('deadline'),

            "custom_field_filter" => $this->prepare_custom_field_filter_values("projects", $this->login_user->is_admin, $this->login_user->user_type)

        );



        //only admin/ the user has permission to manage all projects, can see all projects, other team mebers can see only their own projects.

        if (!$this->can_manage_all_projects()) {

            $options["user_id"] = $this->login_user->id;

        }



        $list_data = $this->Projects_model->get_details($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_row($data, $custom_fields);

        }

        echo json_encode(array("data" => $result));

    }



    /* list of projcts, prepared for datatable  */



    function projects_list_data_of_team_member($team_member_id = 0) {

        validate_numeric_value($team_member_id);

        $this->access_only_team_members();



        $custom_fields = $this->Custom_fields_model->get_available_fields_for_table("projects", $this->login_user->is_admin, $this->login_user->user_type);



        $status_ids = $this->request->getPost('status_id') ? implode(",", $this->request->getPost('status_id')) : "";



        $options = array(

            "status_ids" => $status_ids,

            "custom_fields" => $custom_fields,

            "custom_field_filter" => $this->prepare_custom_field_filter_values("projects", $this->login_user->is_admin, $this->login_user->user_type)

        );



        //add can see all members projects but team members can see only ther own projects

        if (!$this->can_manage_all_projects() && $team_member_id != $this->login_user->id) {

            app_redirect("forbidden");

        }



        $options["user_id"] = $team_member_id;



        $list_data = $this->Projects_model->get_details($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_row($data, $custom_fields);

        }

        echo json_encode(array("data" => $result));

    }



    function projects_list_data_of_client($client_id = 0) {

        validate_numeric_value($client_id);



        $this->access_only_team_members_or_client_contact($client_id);



        $custom_fields = $this->Custom_fields_model->get_available_fields_for_table("projects", $this->login_user->is_admin, $this->login_user->user_type);



        $status_ids = $this->request->getPost('status_id') ? implode(",", $this->request->getPost('status_id')) : "";



        $options = array(

            "client_id" => $client_id,

            "status_ids" => $status_ids,

            "project_label" => $this->request->getPost("project_label"),

            "custom_fields" => $custom_fields,

            "custom_field_filter" => $this->prepare_custom_field_filter_values("projects", $this->login_user->is_admin, $this->login_user->user_type)

        );



        $list_data = $this->Projects_model->get_details($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_row($data, $custom_fields);

        }

        echo json_encode(array("data" => $result));

    }



    /* return a row of project list  table */



    private function _row_data($id) {

        $custom_fields = $this->Custom_fields_model->get_available_fields_for_table("projects", $this->login_user->is_admin, $this->login_user->user_type);



        $options = array(

            "id" => $id,

            "custom_fields" => $custom_fields

        );



        $data = $this->Projects_model->get_details($options)->getRow();

        return $this->_make_row($data, $custom_fields);

    }



    /* prepare a row of project list table */



    private function _make_row($data, $custom_fields) {



        $progress = $data->total_points ? round(($data->completed_points / $data->total_points) * 100) : 0;



        $class = "bg-primary";

        if ($progress == 100) {

            $class = "progress-bar-success";

        }



        $progress_bar = "<div class='progress' title='$progress%'>

            <div  class='progress-bar $class' role='progressbar' aria-valuenow='$progress' aria-valuemin='0' aria-valuemax='100' style='width: $progress%'>

            </div>

        </div>";

        $start_date = is_date_exists($data->start_date) ? format_to_date($data->start_date, false) : "-";

        $dateline = is_date_exists($data->deadline) ? format_to_date($data->deadline, false) : "-";

        $price = $data->price ? to_currency($data->price, $data->currency_symbol) : "-";



        //has deadline? change the color of date based on status

        if (is_date_exists($data->deadline)) {

            if ($progress !== 100 && $data->status_id == 1 && get_my_local_time("Y-m-d") > $data->deadline) {

                $dateline = "<span class='text-danger mr5'>" . $dateline . "</span> ";

            } else if ($progress !== 100 && $data->status_id == 1 && get_my_local_time("Y-m-d") == $data->deadline) {

                $dateline = "<span class='text-warning mr5'>" . $dateline . "</span> ";

            }

        }



        $title = anchor(get_uri("projects/view/" . $data->id), $data->title);

        if ($data->labels_list) {

            $project_labels = make_labels_view_data($data->labels_list, true);

            $title .= "<br />" . $project_labels;

        }



        $optoins = "";

        if ($this->can_edit_projects($data->id)) {

            $optoins .= modal_anchor(get_uri("projects/modal_form"), "<i data-feather='edit' class='icon-16'></i>", array("class" => "edit", "title" => app_lang('edit_project'), "data-post-id" => $data->id));

        }



        if ($this->can_delete_projects($data->id)) {

            $optoins .= js_anchor("<i data-feather='x' class='icon-16'></i>", array('title' => app_lang('delete_project'), "class" => "delete", "data-id" => $data->id, "data-action-url" => get_uri("projects/delete"), "data-action" => "delete-confirmation"));

        }



        //show the project price to them who has permission to create projects

        if ($this->login_user->user_type == "staff" && !$this->can_create_projects()) {

            $price = "-";

        }



        $client_name = "-";

        if ($data->company_name) {

            $client_name = anchor(get_uri("clients/view/" . $data->client_id), $data->company_name);

        }



        $row_data = array(

            anchor(get_uri("projects/view/" . $data->id), $data->id),

            $title,

            $client_name,

            $price,

            $data->start_date,

            $start_date,

            $data->deadline,

            $dateline,

            $progress_bar,

            $data->title_language_key ? app_lang($data->title_language_key) : $data->status_title

        );



        foreach ($custom_fields as $field) {

            $cf_id = "cfv_" . $field->id;

            $row_data[] = $this->template->view("custom_fields/output_" . $field->field_type, array("value" => $data->$cf_id));

        }



        $row_data[] = $optoins;



        return $row_data;

    }



    /* load project details view */



    function view($project_id = 0, $tab = "") {

        validate_numeric_value($project_id);

        $this->init_project_permission_checker($project_id);



        $view_data = $this->_get_project_info_data($project_id);



        $access_info = $this->get_access_info("invoice");

        $view_data["show_invoice_info"] = (get_setting("module_invoice") && $this->can_view_invoices()) ? true : false;



        $expense_access_info = $this->get_access_info("expense");

        $view_data["show_expense_info"] = (get_setting("module_expense") && $expense_access_info->access_type == "all") ? true : false;



        $access_contract = $this->get_access_info("contract");

        $view_data["show_contract_info"] = (get_setting("module_contract") && $access_contract->access_type == "all") ? true : false;



        $view_data["show_note_info"] = (get_setting("module_note")) ? true : false;



        $view_data["show_timmer"] = get_setting("module_project_timesheet") ? true : false;



        $this->init_project_settings($project_id);

        $view_data["show_timesheet_info"] = $this->can_view_timesheet($project_id);



        $view_data["show_tasks"] = true;



        $view_data["show_gantt_info"] = $this->can_view_gantt();

        $view_data["show_milestone_info"] = $this->can_view_milestones();



        if ($this->login_user->user_type === "client") {

            $view_data["show_timmer"] = false;

            $view_data["show_tasks"] = $this->client_can_view_tasks();



            if (!get_setting("client_can_edit_projects")) {

                $view_data["show_actions_dropdown"] = false;

            }

        }



        $view_data["show_files"] = $this->can_view_files();



        $view_data["tab"] = clean_data($tab);



        $view_data["is_starred"] = strpos($view_data['project_info']->starred_by, ":" . $this->login_user->id . ":") ? true : false;



        $view_data['can_edit_timesheet_settings'] = $this->can_edit_timesheet_settings($project_id);

        $view_data['can_edit_slack_settings'] = $this->can_edit_slack_settings();

        $view_data["can_create_projects"] = $this->can_create_projects();

        $view_data["can_edit_projects"] = $this->can_edit_projects($project_id);



        $view_data["show_actions_dropdown"] = $view_data["can_create_projects"] || $view_data["can_edit_projects"];



        $ticket_access_info = $this->get_access_info("ticket");

        $view_data["show_ticket_info"] = (get_setting("module_ticket") && get_setting("project_reference_in_tickets") && $ticket_access_info->access_type == "all") ? true : false;



        $view_data["project_statuses"] = $this->Project_status_model->get_details()->getResult();

        $view_data["show_customer_feedback"] = $this->has_client_feedback_access_permission();



        return $this->template->rander("projects/details_view", $view_data);

    }



    private function can_edit_timesheet_settings($project_id) {

        $this->init_project_permission_checker($project_id);

        if ($project_id && $this->login_user->user_type === "staff" && $this->can_view_timesheet($project_id)) {

            return true;

        }

    }



    private function can_edit_slack_settings() {

        if ($this->login_user->user_type === "staff" && $this->can_create_projects()) {

            return true;

        }

    }



    /* prepare project info data for reuse */



    private function _get_project_info_data($project_id) {

        $options = array(

            "id" => $project_id,

            "client_id" => $this->login_user->client_id,

        );



        if (!$this->can_manage_all_projects()) {

            $options["user_id"] = $this->login_user->id;

        }



        $project_info = $this->Projects_model->get_details($options)->getRow();

        $view_data['project_info'] = $project_info;



        if ($project_info) {

            $view_data['project_info'] = $project_info;

            $timer = $this->Timesheets_model->get_timer_info($project_id, $this->login_user->id)->getRow();

            $user_has_any_timer_except_this_project = $this->Timesheets_model->user_has_any_timer_except_this_project($project_id, $this->login_user->id);



            //disable the start timer button if the setting is disabled

            $view_data["disable_timer"] = false;

            if ($user_has_any_timer_except_this_project && !get_setting("users_can_start_multiple_timers_at_a_time")) {

                $view_data["disable_timer"] = true;

            }



            if ($timer) {

                $view_data['timer_status'] = "open";

            } else {

                $view_data['timer_status'] = "";

            }



            $view_data['project_progress'] = $project_info->total_points ? round(($project_info->completed_points / $project_info->total_points) * 100) : 0;



            return $view_data;

        } else {

            show_404();

        }

    }



    function show_my_starred_projects() {

        $view_data["projects"] = $this->Projects_model->get_starred_projects($this->login_user->id)->getResult();

        return $this->template->view('projects/star/projects_list', $view_data);

    }



    /* load project overview section */



    function overview($project_id) {

        validate_numeric_value($project_id);

        $this->access_only_team_members();

        $this->init_project_permission_checker($project_id);



        $view_data = $this->_get_project_info_data($project_id);

        $view_data["task_statuses"] = $this->Tasks_model->get_task_statistics(array("project_id" => $project_id))->task_statuses;



        $view_data['project_id'] = $project_id;

        $offset = 0;

        $view_data['offset'] = $offset;

        $view_data['activity_logs_params'] = array("log_for" => "project", "log_for_id" => $project_id, "limit" => 20, "offset" => $offset);



        $view_data["can_add_remove_project_members"] = $this->can_add_remove_project_members();

        $view_data["can_access_clients"] = $this->can_access_clients(true);



        $view_data['custom_fields_list'] = $this->Custom_fields_model->get_combined_details("projects", $project_id, $this->login_user->is_admin, $this->login_user->user_type)->getResult();



        //count total worked hours

        $options = array("project_id" => $project_id);



        //get allowed member ids

        $members = $this->_get_members_to_manage_timesheet();

        if ($members != "all") {

            //if user has permission to access all members, query param is not required

            $options["allowed_members"] = $members;

        }



        $info = $this->Timesheets_model->count_total_time($options);

        $view_data["total_project_hours"] = to_decimal_format($info->timesheet_total / 60 / 60);



        return $this->template->view('projects/overview', $view_data);

    }



    /* add-remove start mark from project */



    function add_remove_star($project_id, $type = "add") {

        if ($project_id) {

            validate_numeric_value($project_id);



            if (get_setting("disable_access_favorite_project_option_for_clients") && $this->login_user->user_type == "client") {

                app_redirect("forbidden");

            }



            $view_data["project_id"] = $project_id;



            if ($type === "add") {

                $this->Projects_model->add_remove_star($project_id, $this->login_user->id, $type = "add");

                return $this->template->view('projects/star/starred', $view_data);

            } else {

                $this->Projects_model->add_remove_star($project_id, $this->login_user->id, $type = "remove");

                return $this->template->view('projects/star/not_starred', $view_data);

            }

        }

    }



    /* load project overview section */



    function overview_for_client($project_id) {

        validate_numeric_value($project_id);

        if ($this->login_user->user_type === "client") {

            $view_data = $this->_get_project_info_data($project_id);



            $view_data['project_id'] = $project_id;



            $offset = 0;

            $view_data['offset'] = $offset;

            $view_data['show_activity'] = false;

            $view_data['show_overview'] = false;

            $view_data['activity_logs_params'] = array();



            $this->init_project_permission_checker($project_id);

            $this->init_project_settings($project_id);

            $view_data["show_timesheet_info"] = $this->can_view_timesheet($project_id);



            $options = array("project_id" => $project_id);

            $timesheet_info = $this->Timesheets_model->count_total_time($options);

            $view_data["total_project_hours"] = to_decimal_format($timesheet_info->timesheet_total / 60 / 60);



            if (get_setting("client_can_view_overview")) {

                $view_data['show_overview'] = true;

                $view_data["task_statuses"] = $this->Tasks_model->get_task_statistics(array("project_id" => $project_id))->task_statuses;



                if (get_setting("client_can_view_activity")) {

                    $view_data['show_activity'] = true;

                    $view_data['activity_logs_params'] = array("log_for" => "project", "log_for_id" => $project_id, "limit" => 20, "offset" => $offset);

                }

            }



            $view_data['custom_fields_list'] = $this->Custom_fields_model->get_combined_details("projects", $project_id, $this->login_user->is_admin, $this->login_user->user_type)->getResult();



            return $this->template->view('projects/overview_for_client', $view_data);

        }

    }



    /* load project members add/edit modal */



    function project_member_modal_form() {

        $view_data['model_info'] = $this->Project_members_model->get_one($this->request->getPost('id'));

        $project_id = $this->request->getPost('project_id') ? $this->request->getPost('project_id') : $view_data['model_info']->project_id;

        $this->init_project_permission_checker($project_id);



        if (!$this->can_add_remove_project_members()) {

            app_redirect("forbidden");

        }



        $view_data['project_id'] = $project_id;



        $view_data["view_type"] = $this->request->getPost("view_type");



        $add_user_type = $this->request->getPost("add_user_type");



        $users_dropdown = array();

        if ($add_user_type == "client_contacts") {

            if (!$this->can_access_clients(true)) {

                app_redirect("forbidden");

            }



            $contacts = $this->Project_members_model->get_client_contacts_of_the_project_client($project_id)->getResult();

            foreach ($contacts as $contact) {

                $users_dropdown[$contact->id] = $contact->contact_name;

            }

        } else {

            $users = $this->Project_members_model->get_rest_team_members_for_a_project($project_id)->getResult();

            foreach ($users as $user) {

                $users_dropdown[$user->id] = $user->member_name;

            }

        }



        $view_data["users_dropdown"] = $users_dropdown;

        $view_data["add_user_type"] = $add_user_type;



        return $this->template->view('projects/project_members/modal_form', $view_data);

    }



    /* add a project members  */



    function save_project_member() {

        $project_id = $this->request->getPost('project_id');



        $this->init_project_permission_checker($project_id);



        if (!$this->can_add_remove_project_members()) {

            app_redirect("forbidden");

        }



        $this->validate_submitted_data(array(

            "user_id.*" => "required"

        ));



        $user_ids = $this->request->getPost('user_id');



        $save_ids = array();

        $already_exists = false;



        if ($user_ids) {

            foreach ($user_ids as $user_id) {

                if ($user_id) {

                    $data = array(

                        "project_id" => $project_id,

                        "user_id" => $user_id

                    );



                    $save_id = $this->Project_members_model->save_member($data);

                    if ($save_id && $save_id != "exists") {

                        $save_ids[] = $save_id;

                        log_notification("project_member_added", array("project_id" => $project_id, "to_user_id" => $user_id));

                    } else if ($save_id === "exists") {

                        $already_exists = true;

                    }

                }

            }

        }





        if (!count($save_ids) && $already_exists) {

            //this member already exists.

            echo json_encode(array("success" => true, 'id' => "exists"));

        } else if (count($save_ids)) {

            $project_member_row = array();

            foreach ($save_ids as $id) {

                $project_member_row[] = $this->_project_member_row_data($id);

            }

            echo json_encode(array("success" => true, "data" => $project_member_row, 'id' => $save_id, 'message' => app_lang('record_saved')));

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('error_occurred')));

        }

    }



    /* delete/undo a project members  */



    function delete_project_member() {

        $id = $this->request->getPost('id');

        $project_member_info = $this->Project_members_model->get_one($id);



        $this->init_project_permission_checker($project_member_info->project_id);

        if (!$this->can_add_remove_project_members()) {

            app_redirect("forbidden");

        }





        if ($this->request->getPost('undo')) {

            if ($this->Project_members_model->delete($id, true)) {

                echo json_encode(array("success" => true, "data" => $this->_project_member_row_data($id), "message" => app_lang('record_undone')));

            } else {

                echo json_encode(array("success" => false, app_lang('error_occurred')));

            }

        } else {

            if ($this->Project_members_model->delete($id)) {



                $project_member_info = $this->Project_members_model->get_one($id);



                log_notification("project_member_deleted", array("project_id" => $project_member_info->project_id, "to_user_id" => $project_member_info->user_id));

                echo json_encode(array("success" => true, 'message' => app_lang('record_deleted')));

            } else {

                echo json_encode(array("success" => false, 'message' => app_lang('record_cannot_be_deleted')));

            }

        }

    }



    /* list of project members, prepared for datatable  */



    function project_member_list_data($project_id = 0, $user_type = "") {

        validate_numeric_value($project_id);

        $this->access_only_team_members();

        $this->init_project_permission_checker($project_id);



        //show the message icon to client contacts list only if the user can send message to client. 

        $can_send_message_to_client = false;

        $client_message_users = get_setting("client_message_users");

        $client_message_users_array = explode(",", $client_message_users);

        if (in_array($this->login_user->id, $client_message_users_array)) {



            $can_send_message_to_client = true;

        }



        $options = array("project_id" => $project_id, "user_type" => $user_type, "show_user_wise" => true);

        $list_data = $this->Project_members_model->get_details($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_project_member_row($data, $can_send_message_to_client);

        }

        echo json_encode(array("data" => $result));

    }



    /* return a row of project member list */



    private function _project_member_row_data($id) {

        $options = array("id" => $id);

        $data = $this->Project_members_model->get_details($options)->getRow();

        return $this->_make_project_member_row($data);

    }



    /* prepare a row of project member list */



    private function _make_project_member_row($data, $can_send_message_to_client = false) {

        $member_image = "<span class='avatar avatar-sm'><img src='" . get_avatar($data->member_image) . "' alt='...'></span> ";



        if ($data->user_type == "staff") {

            $member = get_team_member_profile_link($data->user_id, $member_image);

            $member_name = get_team_member_profile_link($data->user_id, $data->member_name, array("class" => "dark strong"));

        } else {

            $member = get_client_contact_profile_link($data->user_id, $member_image);

            $member_name = get_client_contact_profile_link($data->user_id, $data->member_name, array("class" => "dark strong"));

        }



        $link = "";



        //check message module availability and show message button

        if (get_setting("module_message") && ($this->login_user->id != $data->user_id)) {

            $link = modal_anchor(get_uri("messages/modal_form/" . $data->user_id), "<i data-feather='mail' class='icon-16'></i>", array("class" => "edit", "title" => app_lang('send_message')));

        }



        //check message icon permission for client contacts

        if (!$can_send_message_to_client && $data->user_type === "client") {

            $link = "";

        }





        if ($this->can_add_remove_project_members()) {

            $delete_link = js_anchor("<i data-feather='x' class='icon-16'></i>", array('title' => app_lang('delete_member'), "class" => "delete", "data-id" => $data->id, "data-action-url" => get_uri("projects/delete_project_member"), "data-action" => "delete"));



            if (!$this->can_manage_all_projects() && ($this->login_user->id === $data->user_id)) {

                $delete_link = "";

            }

            $link .= $delete_link;

        }



        $member = '<div class="d-flex"><div class="p-2 flex-shrink-1">' . $member . '</div><div class="p-2 w-100"><div>' . $member_name . '</div><label class="text-off">' . $data->job_title . '</label></div></div>';



        return array($member, $link);

    }



    //stop timer note modal

    function stop_timer_modal_form($project_id) {

        validate_numeric_value($project_id);

        $this->access_only_team_members();



        if ($project_id) {

            $view_data["project_id"] = $project_id;

            $view_data["tasks_dropdown"] = $this->_get_timesheet_tasks_dropdown($project_id);



            $options = array(

                "project_id" => $project_id,

                "task_status_id" => 2,

                "assigned_to" => $this->login_user->id

            );



            $task_info = $this->Tasks_model->get_details($options)->getRow();



            $open_task_id = $this->request->getPost("task_id");



            $task_id = "";

            if ($open_task_id) {

                $task_id = $open_task_id;

            } else if ($task_info) {

                $task_id = $task_info->id;

            }



            $view_data["open_task_id"] = $open_task_id;

            $view_data["task_id"] = $task_id;



            return $this->template->view('projects/timesheets/stop_timer_modal_form', $view_data);

        }

    }



    private function _get_timesheet_tasks_dropdown($project_id, $return_json = false) {

        $tasks_dropdown = array("" => "-");

        $tasks_dropdown_json = array(array("id" => "", "text" => "- " . app_lang("task") . " -"));



        $show_assigned_tasks_only_user_id = $this->show_assigned_tasks_only_user_id();

        if (!$show_assigned_tasks_only_user_id) {

            $timesheet_manage_permission = get_array_value($this->login_user->permissions, "timesheet_manage_permission");

            if (!$timesheet_manage_permission || $timesheet_manage_permission === "own") {

                //show only own tasks when the permission is no/own

                $show_assigned_tasks_only_user_id = $this->login_user->id;

            }

        }



        $options = array(

            "project_id" => $project_id,

            "show_assigned_tasks_only_user_id" => $show_assigned_tasks_only_user_id

        );



        $tasks = $this->Tasks_model->get_details($options)->getResult();



        foreach ($tasks as $task) {

            $tasks_dropdown_json[] = array("id" => $task->id, "text" => $task->id . " - " . $task->title);

            $tasks_dropdown[$task->id] = $task->id . " - " . $task->title;

        }



        if ($return_json) {

            return json_encode($tasks_dropdown_json);

        } else {

            return $tasks_dropdown;

        }

    }



    /* start/stop project timer */



    function timer($project_id, $timer_status = "start") {

        validate_numeric_value($project_id);

        $this->access_only_team_members();

        $note = $this->request->getPost("note");

        $task_id = $this->request->getPost("task_id");



        $data = array(

            "project_id" => $project_id,

            "user_id" => $this->login_user->id,

            "status" => $timer_status,

            "note" => $note ? $note : "",

            "task_id" => $task_id ? $task_id : 0,

        );



        $user_has_any_timer_except_this_project = $this->Timesheets_model->user_has_any_timer_except_this_project($project_id, $this->login_user->id);



        $user_has_any_open_timer_on_this_task = false;



        if ($task_id) {

            $user_has_any_open_timer_on_this_task = $this->Timesheets_model->user_has_any_open_timer_on_this_task($task_id, $this->login_user->id);

        }



        if ($timer_status == "start" && $user_has_any_timer_except_this_project && !get_setting("users_can_start_multiple_timers_at_a_time")) {

            app_redirect("forbidden");

        } else if ($timer_status == "start" && $user_has_any_open_timer_on_this_task) {

            app_redirect("forbidden");

        }



        $this->Timesheets_model->process_timer($data);

        if ($timer_status === "start") {

            if ($this->request->getPost("task_timer")) {

                echo modal_anchor(get_uri("projects/stop_timer_modal_form/" . $project_id), "<i data-feather='clock' class='icon-16'></i> " . app_lang('stop_timer'), array("class" => "btn btn-danger", "title" => app_lang('stop_timer'), "data-post-task_id" => $task_id));

            } else {

                $view_data = $this->_get_project_info_data($project_id);

                return $this->template->view('projects/project_timer', $view_data);

            }

        } else {

            echo json_encode(array("success" => true));

        }

    }



    /* load timesheets view for a project */



    function timesheets($project_id) {

        validate_numeric_value($project_id);



        $this->init_project_permission_checker($project_id);

        $this->init_project_settings($project_id); //since we'll check this permission project wise





        if (!$this->can_view_timesheet($project_id)) {

            app_redirect("forbidden");

        }



        $view_data['project_id'] = $project_id;



        //client can't add log or update settings

        $view_data['can_add_log'] = false;



        if ($this->login_user->user_type === "staff") {

            $view_data['can_add_log'] = true;

        }



        $view_data['project_members_dropdown'] = json_encode($this->_get_project_members_dropdown_list_for_filter($project_id));

        $view_data['tasks_dropdown'] = $this->_get_timesheet_tasks_dropdown($project_id, true);



        $view_data["custom_field_headers"] = $this->Custom_fields_model->get_custom_field_headers_for_table("timesheets", $this->login_user->is_admin, $this->login_user->user_type);

        $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("timesheets", $this->login_user->is_admin, $this->login_user->user_type);



        $view_data["show_members_dropdown"] = true;

        $timesheet_access_info = $this->get_access_info("timesheet_manage_permission");

        $timesheet_access_type = $timesheet_access_info->access_type;



        if (!$timesheet_access_type || $timesheet_access_type === "own") {

            $view_data["show_members_dropdown"] = false;

        }



        return $this->template->view("projects/timesheets/index", $view_data);

    }



    /* prepare project members dropdown */



    private function _get_project_members_dropdown_list_for_filter($project_id) {



        $project_members = $this->Project_members_model->get_project_members_dropdown_list($project_id)->getResult();

        $project_members_dropdown = array(array("id" => "", "text" => "- " . app_lang("member") . " -"));

        foreach ($project_members as $member) {

            $project_members_dropdown[] = array("id" => $member->user_id, "text" => $member->member_name);

        }

        return $project_members_dropdown;

    }



    /* load timelog add/edit modal */



    function timelog_modal_form() {

        $this->access_only_team_members();

        $view_data['time_format_24_hours'] = get_setting("time_format") == "24_hours" ? true : false;

        $model_info = $this->Timesheets_model->get_one($this->request->getPost('id'));

        $project_id = $this->request->getPost('project_id') ? $this->request->getPost('project_id') : $model_info->project_id;



        //set the login user as a default selected member

        if (!$model_info->user_id) {

            $model_info->user_id = $this->login_user->id;

        }



        //get related data

        $related_data = $this->_prepare_all_related_data_for_timelog($project_id);

        $show_porject_members_dropdown = get_array_value($related_data, "show_porject_members_dropdown");

        $view_data["tasks_dropdown"] = get_array_value($related_data, "tasks_dropdown");

        $view_data["project_members_dropdown"] = get_array_value($related_data, "project_members_dropdown");



        $view_data["model_info"] = $model_info;



        if ($model_info->id) {

            $show_porject_members_dropdown = false; //don't allow to edit the user on update.

        }



        $view_data["project_id"] = $project_id;

        $view_data['show_porject_members_dropdown'] = $show_porject_members_dropdown;

        $view_data["projects_dropdown"] = $this->_get_projects_dropdown();



        $view_data["custom_fields"] = $this->Custom_fields_model->get_combined_details("timesheets", $view_data['model_info']->id, $this->login_user->is_admin, $this->login_user->user_type)->getResult();



        return $this->template->view('projects/timesheets/modal_form', $view_data);

    }



    private function _prepare_all_related_data_for_timelog($project_id = 0) {

        //we have to check if any defined project exists, then go through with the project id

        $show_porject_members_dropdown = false;

        if ($project_id) {

            $tasks_dropdown = $this->_get_timesheet_tasks_dropdown($project_id, true);



            //prepare members dropdown list

            $allowed_members = $this->_get_members_to_manage_timesheet();

            $project_members = "";



            if ($allowed_members === "all") {

                $project_members = $this->Project_members_model->get_project_members_dropdown_list($project_id)->getResult(); //get all members of this project

            } else {

                $project_members = $this->Project_members_model->get_project_members_dropdown_list($project_id, $allowed_members)->getResult();

            }



            $project_members_dropdown = array();

            if ($project_members) {

                foreach ($project_members as $member) {



                    if ($member->user_id !== $this->login_user->id) {

                        $show_porject_members_dropdown = true; //user can manage other users time.

                    }



                    $project_members_dropdown[] = array("id" => $member->user_id, "text" => $member->member_name);

                }

            }

        } else {

            //we have show an empty dropdown when there is no project_id defined

            $tasks_dropdown = json_encode(array(array("id" => "", "text" => "-")));

            $project_members_dropdown = array(array("id" => "", "text" => "-"));

            $show_porject_members_dropdown = true;

        }



        return array(

            "project_members_dropdown" => $project_members_dropdown,

            "tasks_dropdown" => $tasks_dropdown,

            "show_porject_members_dropdown" => $show_porject_members_dropdown

        );

    }



    function get_all_related_data_of_selected_project_for_timelog($project_id = "") {

        validate_numeric_value($project_id);

        if ($project_id) {

            $related_data = $this->_prepare_all_related_data_for_timelog($project_id);



            echo json_encode(array(

                "project_members_dropdown" => get_array_value($related_data, "project_members_dropdown"),

                "tasks_dropdown" => json_decode(get_array_value($related_data, "tasks_dropdown"))

            ));

        }

    }



    /* insert/update a timelog */



    function save_timelog() {

        $this->access_only_team_members();

        $id = $this->request->getPost('id');



        $start_date_time = "";

        $end_date_time = "";

        $hours = "";



        $start_time = $this->request->getPost('start_time');

        $end_time = $this->request->getPost('end_time');

        $note = $this->request->getPost("note");

        $task_id = $this->request->getPost("task_id");



        if ($start_time) {

            //start time and end time mode

            //convert to 24hrs time format

            if (get_setting("time_format") != "24_hours") {

                $start_time = convert_time_to_24hours_format($start_time);

                $end_time = convert_time_to_24hours_format($end_time);

            }



            //join date with time

            $start_date_time = $this->request->getPost('start_date') . " " . $start_time;

            $end_date_time = $this->request->getPost('end_date') . " " . $end_time;



            //add time offset

            $start_date_time = convert_date_local_to_utc($start_date_time);

            $end_date_time = convert_date_local_to_utc($end_date_time);

        } else {

            //date and hour mode

            $date = $this->request->getPost("date");

            $start_date_time = $date . " 00:00:00";

            $end_date_time = $date . " 00:00:00";



            //prepare hours

            $hours = convert_humanize_data_to_hours($this->request->getPost("hours"));

            if (!$hours) {

                echo json_encode(array("success" => false, 'message' => app_lang("hour_log_time_error_message")));

                return false;

            }

        }



        $project_id = $this->request->getPost('project_id');

        $data = array(

            "project_id" => $project_id,

            "start_time" => $start_date_time,

            "end_time" => $end_date_time,

            "note" => $note ? $note : "",

            "task_id" => $task_id ? $task_id : 0,

            "hours" => $hours

        );



        //save user_id only on insert and it will not be editable

        if (!$id) {

            //insert mode

            $data["user_id"] = $this->request->getPost('user_id') ? $this->request->getPost('user_id') : $this->login_user->id;

        }



        $this->check_timelog_update_permission($id, $project_id, get_array_value($data, "user_id"));



        $save_id = $this->Timesheets_model->ci_save($data, $id);

        if ($save_id) {



            save_custom_fields("timesheets", $save_id, $this->login_user->is_admin, $this->login_user->user_type);



            echo json_encode(array("success" => true, "data" => $this->_timesheet_row_data($save_id), 'id' => $save_id, 'message' => app_lang('record_saved')));

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('error_occurred')));

        }

    }



    /* delete/undo a timelog */



    function delete_timelog() {

        $this->access_only_team_members();



        $id = $this->request->getPost('id');



        $this->check_timelog_update_permission($id);



        if ($this->request->getPost('undo')) {

            if ($this->Timesheets_model->delete($id, true)) {

                echo json_encode(array("success" => true, "data" => $this->_timesheet_row_data($id), "message" => app_lang('record_undone')));

            } else {

                echo json_encode(array("success" => false, app_lang('error_occurred')));

            }

        } else {

            if ($this->Timesheets_model->delete($id)) {

                echo json_encode(array("success" => true, 'message' => app_lang('record_deleted')));

            } else {

                echo json_encode(array("success" => false, 'message' => app_lang('record_cannot_be_deleted')));

            }

        }

    }



    private function check_timelog_update_permission($log_id = null, $project_id = null, $user_id = null) {

        if ($log_id) {

            $info = $this->Timesheets_model->get_one($log_id);

            $user_id = $info->user_id;

        }



        if (!$log_id && $user_id === $this->login_user->id) { //adding own timelogs

            return true;

        }



        $members = $this->_get_members_to_manage_timesheet();



        if ($members === "all") {

            return true;

        } else if (is_array($members) && count($members) && in_array($user_id, $members)) {

            //permission: no / own / specific / specific_excluding_own

            $timesheet_manage_permission = get_array_value($this->login_user->permissions, "timesheet_manage_permission");



            if (!$timesheet_manage_permission && $log_id) { //permission: no

                app_redirect("forbidden");

            }



            if ($timesheet_manage_permission === "specific_excluding_own" && $log_id && $user_id === $this->login_user->id) { //permission: specific_excluding_own

                app_redirect("forbidden");

            }



            //permission: own / specific

            return true;

        } else if ($members === "own_project_members" || $members === "own_project_members_excluding_own") {

            if (!$project_id) { //there has $log_id or $project_id

                $project_id = $info->project_id;

            }



            if ($this->Project_members_model->is_user_a_project_member($project_id, $user_id) || $this->Project_members_model->is_user_a_project_member($project_id, $this->login_user->id)) { //check if the login user and timelog user is both on same project

                if ($members === "own_project_members") {

                    return true;

                } else if ($this->login_user->id !== $user_id) {

                    //can't edit own but can edit other user's of project

                    //no need to check own condition here for new timelogs since it's already checked before

                    return true;

                }

            }

        }



        app_redirect("forbidden");

    }



    /* list of timesheets, prepared for datatable  */



    function timesheet_list_data() {



        $project_id = $this->request->getPost("project_id");



        $this->init_project_permission_checker($project_id);

        $this->init_project_settings($project_id); //since we'll check this permission project wise





        if (!$this->can_view_timesheet($project_id, true)) {

            app_redirect("forbidden");

        }



        $custom_fields = $this->Custom_fields_model->get_available_fields_for_table("timesheets", $this->login_user->is_admin, $this->login_user->user_type);



        $options = array(

            "project_id" => $project_id,

            "status" => "none_open",

            "user_id" => $this->request->getPost("user_id"),

            "start_date" => $this->request->getPost("start_date"),

            "end_date" => $this->request->getPost("end_date"),

            "task_id" => $this->request->getPost("task_id"),

            "client_id" => $this->request->getPost("client_id"),

            "custom_fields" => $custom_fields,

            "custom_field_filter" => $this->prepare_custom_field_filter_values("timesheets", $this->login_user->is_admin, $this->login_user->user_type)

        );



        //get allowed member ids

        $members = $this->_get_members_to_manage_timesheet();

        if ($members != "all" && $this->login_user->user_type == "staff") {

            //if user has permission to access all members, query param is not required

            //client can view all timesheet

            $options["allowed_members"] = $members;

        }



        $all_options = append_server_side_filtering_commmon_params($options);



        $result = $this->Timesheets_model->get_details($all_options);



        //by this, we can handel the server side or client side from the app table prams.

        if (get_array_value($all_options, "server_side")) {

            $list_data = get_array_value($result, "data");

        } else {

            $list_data = $result->getResult();

            $result = array();

        }



        $result_data = array();

        foreach ($list_data as $data) {

            $result_data[] = $this->_make_timesheet_row($data, $custom_fields);

        }



        $result["data"] = $result_data;



        echo json_encode($result);

    }



    /* return a row of timesheet list  table */



    private function _timesheet_row_data($id) {

        $custom_fields = $this->Custom_fields_model->get_available_fields_for_table("timesheets", $this->login_user->is_admin, $this->login_user->user_type);



        $options = array("id" => $id, "custom_fields" => $custom_fields);

        $data = $this->Timesheets_model->get_details($options)->getRow();

        return $this->_make_timesheet_row($data, $custom_fields);

    }



    /* prepare a row of timesheet list table */



    private function _make_timesheet_row($data, $custom_fields) {

        $image_url = get_avatar($data->logged_by_avatar);

        $user = "<span class='avatar avatar-xs mr10'><img src='$image_url' alt=''></span> $data->logged_by_user";



        $start_time = $data->start_time;

        $end_time = $data->end_time;

        $project_title = anchor(get_uri("projects/view/" . $data->project_id), $data->project_title);

        $task_title = modal_anchor(get_uri("tasks/view"), $data->task_title, array("title" => app_lang('task_info') . " #$data->task_id", "data-post-id" => $data->task_id, "data-modal-lg" => "1"));



        $client_name = "-";

        if ($data->timesheet_client_company_name) {

            $client_name = anchor(get_uri("clients/view/" . $data->timesheet_client_id), $data->timesheet_client_company_name);

        }



        $duration = convert_seconds_to_time_format($data->hours ? (round(($data->hours * 60), 0) * 60) : (abs(strtotime($end_time) - strtotime($start_time))));



        $row_data = array(

            get_team_member_profile_link($data->user_id, $user),

            $project_title,

            $client_name,

            $task_title,

            $data->start_time,

            ($data->hours || get_setting("users_can_input_only_total_hours_instead_of_period")) ? format_to_date($data->start_time) : format_to_datetime($data->start_time),

            $data->end_time,

            $data->hours ? format_to_date($data->end_time) : format_to_datetime($data->end_time),

            $duration,

            to_decimal_format(convert_time_string_to_decimal($duration)),

            $data->note

        );



        foreach ($custom_fields as $field) {

            $cf_id = "cfv_" . $field->id;

            $row_data[] = $this->template->view("custom_fields/output_" . $field->field_type, array("value" => $data->$cf_id));

        }



        $options = modal_anchor(get_uri("projects/timelog_modal_form"), "<i data-feather='edit' class='icon-16'></i>", array("class" => "edit", "title" => app_lang('edit_timelog'), "data-post-id" => $data->id))

                . js_anchor("<i data-feather='x' class='icon-16'></i>", array('title' => app_lang('delete_timelog'), "class" => "delete", "data-id" => $data->id, "data-action-url" => get_uri("projects/delete_timelog"), "data-action" => "delete"));



        $timesheet_manage_permission = get_array_value($this->login_user->permissions, "timesheet_manage_permission");

        if ($data->user_id === $this->login_user->id && ($timesheet_manage_permission === "own_project_members_excluding_own" || $timesheet_manage_permission === "specific_excluding_own")) {

            $options = "";

        }



        $row_data[] = $options;



        return $row_data;

    }



    /* load timesheets summary view for a project */



    function timesheet_summary($project_id) {

        validate_numeric_value($project_id);



        $this->init_project_permission_checker($project_id);

        $this->init_project_settings($project_id); //since we'll check this permission project wise



        if (!$this->can_view_timesheet($project_id)) {

            app_redirect("forbidden");

        }







        $view_data['project_id'] = $project_id;



        $view_data['group_by_dropdown'] = json_encode(

                array(

                    array("id" => "", "text" => "- " . app_lang("group_by") . " -"),

                    array("id" => "member", "text" => app_lang("member")),

                    array("id" => "task", "text" => app_lang("task"))

        ));



        $view_data['project_members_dropdown'] = json_encode($this->_get_project_members_dropdown_list_for_filter($project_id));

        $view_data['tasks_dropdown'] = $this->_get_timesheet_tasks_dropdown($project_id, true);

        $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("timesheets", $this->login_user->is_admin, $this->login_user->user_type);



        $view_data["show_members_dropdown"] = true;

        $timesheet_access_info = $this->get_access_info("timesheet_manage_permission");

        $timesheet_access_type = $timesheet_access_info->access_type;



        if (!$timesheet_access_type || $timesheet_access_type === "own") {

            $view_data["show_members_dropdown"] = false;

        }



        return $this->template->view("projects/timesheets/summary_list", $view_data);

    }



    /* list of timesheets summary, prepared for datatable  */



    function timesheet_summary_list_data() {



        $project_id = $this->request->getPost("project_id");



        //client can't view all projects timesheet. project id is required.

        if (!$project_id) {

            $this->access_only_team_members();

        }



        if ($project_id) {

            $this->init_project_permission_checker($project_id);

            $this->init_project_settings($project_id); //since we'll check this permission project wise



            if (!$this->can_view_timesheet($project_id, true)) {

                app_redirect("forbidden");

            }

        }





        $group_by = $this->request->getPost("group_by");



        $options = array(

            "project_id" => $project_id,

            "status" => "none_open",

            "user_id" => $this->request->getPost("user_id"),

            "start_date" => $this->request->getPost("start_date"),

            "end_date" => $this->request->getPost("end_date"),

            "task_id" => $this->request->getPost("task_id"),

            "group_by" => $group_by,

            "client_id" => $this->request->getPost("client_id"),

            "custom_field_filter" => $this->prepare_custom_field_filter_values("timesheets", $this->login_user->is_admin, $this->login_user->user_type)

        );



        //get allowed member ids

        $members = $this->_get_members_to_manage_timesheet();

        if ($members != "all" && $this->login_user->user_type == "staff") {

            //if user has permission to access all members, query param is not required

            //client can view all timesheet

            $options["allowed_members"] = $members;

        }



        $list_data = $this->Timesheets_model->get_summary_details($options)->getResult();



        $result = array();

        foreach ($list_data as $data) {





            $member = "-";

            $task_title = "-";



            if ($group_by != "task") {

                $image_url = get_avatar($data->logged_by_avatar);

                $user = "<span class='avatar avatar-xs mr10'><img src='$image_url' alt=''></span> $data->logged_by_user";



                $member = get_team_member_profile_link($data->user_id, $user);

            }



            $project_title = anchor(get_uri("projects/view/" . $data->project_id), $data->project_title);



            if ($group_by != "member") {

                $task_title = modal_anchor(get_uri("tasks/view"), $data->task_title, array("title" => app_lang('task_info') . " #$data->task_id", "data-post-id" => $data->task_id, "data-modal-lg" => "1"));

                if (!$data->task_title) {

                    $task_title = app_lang("not_specified");

                }

            }





            $duration = convert_seconds_to_time_format(abs($data->total_duration));



            $client_name = "-";

            if ($data->timesheet_client_company_name) {

                $client_name = anchor(get_uri("clients/view/" . $data->timesheet_client_id), $data->timesheet_client_company_name);

            }



            $result[] = array(

                $project_title,

                $client_name,

                $member,

                $task_title,

                $duration,

                to_decimal_format(convert_time_string_to_decimal($duration))

            );

        }

        echo json_encode(array("data" => $result));

    }



    /* get all projects list */



    private function _get_all_projects_dropdown_list() {

        $projects = $this->Projects_model->get_dropdown_list(array("title"));



        $projects_dropdown = array(array("id" => "", "text" => "- " . app_lang("project") . " -"));

        foreach ($projects as $id => $title) {

            $projects_dropdown[] = array("id" => $id, "text" => $title);

        }

        return $projects_dropdown;

    }



    /* get all projects list according to the login user */



    private function _get_all_projects_dropdown_list_for_timesheets_filter() {

        $options = array();



        if (!$this->can_manage_all_projects()) {

            $options["user_id"] = $this->login_user->id;

        }



        $projects = $this->Projects_model->get_details($options)->getResult();



        $projects_dropdown = array(array("id" => "", "text" => "- " . app_lang("project") . " -"));

        foreach ($projects as $project) {

            $projects_dropdown[] = array("id" => $project->id, "text" => $project->title);

        }



        return $projects_dropdown;

    }



    /* prepare dropdown list */



    private function _prepare_members_dropdown_for_timesheet_filter($members) {

        $where = array("user_type" => "staff");



        if ($members != "all" && is_array($members) && count($members)) {

            $where["where_in"] = array("id" => $members);

        }



        $users = $this->Users_model->get_dropdown_list(array("first_name", "last_name"), "id", $where);



        $members_dropdown = array(array("id" => "", "text" => "- " . app_lang("member") . " -"));

        foreach ($users as $id => $name) {

            $members_dropdown[] = array("id" => $id, "text" => $name);

        }

        return $members_dropdown;

    }



    /* load all time sheets view  */



    function all_timesheets() {

        $this->access_only_team_members();

        $members = $this->_get_members_to_manage_timesheet();



        $view_data['members_dropdown'] = json_encode($this->_prepare_members_dropdown_for_timesheet_filter($members));

        $view_data['projects_dropdown'] = json_encode($this->_get_all_projects_dropdown_list_for_timesheets_filter());

        $view_data['clients_dropdown'] = json_encode($this->_get_clients_dropdown());



        $view_data["custom_field_headers"] = $this->Custom_fields_model->get_custom_field_headers_for_table("timesheets", $this->login_user->is_admin, $this->login_user->user_type);

        $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("timesheets", $this->login_user->is_admin, $this->login_user->user_type);



        return $this->template->rander("projects/timesheets/all_timesheets", $view_data);

    }



    /* load all timesheets summary view */



    function all_timesheet_summary() {

        $this->access_only_team_members();



        $members = $this->_get_members_to_manage_timesheet();



        $view_data['group_by_dropdown'] = json_encode(

                array(

                    array("id" => "", "text" => "- " . app_lang("group_by") . " -"),

                    array("id" => "member", "text" => app_lang("member")),

                    array("id" => "project", "text" => app_lang("project")),

                    array("id" => "task", "text" => app_lang("task"))

        ));



        $view_data['members_dropdown'] = json_encode($this->_prepare_members_dropdown_for_timesheet_filter($members));

        $view_data['projects_dropdown'] = json_encode($this->_get_all_projects_dropdown_list_for_timesheets_filter());

        $view_data['clients_dropdown'] = json_encode($this->_get_clients_dropdown());

        $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("timesheets", $this->login_user->is_admin, $this->login_user->user_type);



        return $this->template->view("projects/timesheets/all_summary_list", $view_data);

    }



    /* load milestones view */



    function milestones($project_id) {

        validate_numeric_value($project_id);

        $this->init_project_permission_checker($project_id);



        if (!$this->can_view_milestones()) {

            app_redirect("forbidden");

        }



        $view_data['project_id'] = $project_id;



        $view_data["can_create_milestones"] = $this->can_create_milestones();

        $view_data["can_edit_milestones"] = $this->can_edit_milestones();

        $view_data["can_delete_milestones"] = $this->can_delete_milestones();



        return $this->template->view("projects/milestones/index", $view_data);

    }



    /* load milestone add/edit modal */



    function milestone_modal_form() {

        $id = $this->request->getPost('id');

        $view_data['model_info'] = $this->Milestones_model->get_one($this->request->getPost('id'));

        $project_id = $this->request->getPost('project_id') ? $this->request->getPost('project_id') : $view_data['model_info']->project_id;



        $this->init_project_permission_checker($project_id);



        if ($id) {

            if (!$this->can_edit_milestones()) {

                app_redirect("forbidden");

            }

        } else {

            if (!$this->can_create_milestones()) {

                app_redirect("forbidden");

            }

        }



        $view_data['project_id'] = $project_id;



        return $this->template->view('projects/milestones/modal_form', $view_data);

    }



    /* insert/update a milestone */



    function save_milestone() {



        $id = $this->request->getPost('id');

        $project_id = $this->request->getPost('project_id');



        $this->init_project_permission_checker($project_id);



        if ($id) {

            if (!$this->can_edit_milestones()) {

                app_redirect("forbidden");

            }

        } else {

            if (!$this->can_create_milestones()) {

                app_redirect("forbidden");

            }

        }



        $data = array(

            "title" => $this->request->getPost('title'),

            "description" => $this->request->getPost('description'),

            "project_id" => $this->request->getPost('project_id'),

            "due_date" => $this->request->getPost('due_date')

        );

        $save_id = $this->Milestones_model->ci_save($data, $id);

        if ($save_id) {

            echo json_encode(array("success" => true, "data" => $this->_milestone_row_data($save_id), 'id' => $save_id, 'message' => app_lang('record_saved')));

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('error_occurred')));

        }

    }



    /* delete/undo a milestone */



    function delete_milestone() {



        $id = $this->request->getPost('id');

        $info = $this->Milestones_model->get_one($id);

        $this->init_project_permission_checker($info->project_id);



        if (!$this->can_delete_milestones()) {

            app_redirect("forbidden");

        }



        if ($this->request->getPost('undo')) {

            if ($this->Milestones_model->delete($id, true)) {

                echo json_encode(array("success" => true, "data" => $this->_milestone_row_data($id), "message" => app_lang('record_undone')));

            } else {

                echo json_encode(array("success" => false, app_lang('error_occurred')));

            }

        } else {

            if ($this->Milestones_model->delete($id)) {

                echo json_encode(array("success" => true, 'message' => app_lang('record_deleted')));

            } else {

                echo json_encode(array("success" => false, 'message' => app_lang('record_cannot_be_deleted')));

            }

        }

    }



    /* list of milestones, prepared for datatable  */



    function milestones_list_data($project_id = 0) {

        validate_numeric_value($project_id);

        $this->init_project_permission_checker($project_id);



        $options = array("project_id" => $project_id);

        $list_data = $this->Milestones_model->get_details($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_milestone_row($data);

        }

        echo json_encode(array("data" => $result));

    }



    /* return a row of milestone list  table */



    private function _milestone_row_data($id) {

        $options = array("id" => $id);

        $data = $this->Milestones_model->get_details($options)->getRow();

        $this->init_project_permission_checker($data->project_id);



        return $this->_make_milestone_row($data);

    }



    /* prepare a row of milestone list table */



    private function _make_milestone_row($data) {



        //calculate milestone progress

        $progress = $data->total_points ? round(($data->completed_points / $data->total_points) * 100) : 0;

        $class = "bg-primary";

        if ($progress == 100) {

            $class = "progress-bar-success";

        }



        $total_tasks = $data->total_tasks ? $data->total_tasks : 0;

        $completed_tasks = $data->completed_tasks ? $data->completed_tasks : 0;



        $progress_bar = "<div class='ml10 mr10 clearfix'><span class='float-start'>$completed_tasks/$total_tasks</span><span class='float-end'>$progress%</span></div><div class='progress mt0' title='$progress%'>

            <div  class='progress-bar $class' role='progressbar' aria-valuenow='$progress' aria-valuemin='0' aria-valuemax='100' style='width: $progress%'>

            </div>

        </div>";



        //define milesone color based on due date

        $due_date = date("L", strtotime($data->due_date));

        $label_class = "";

        if ($progress == 100) {

            $label_class = "bg-success";

        } else if ($progress !== 100 && get_my_local_time("Y-m-d") > $data->due_date) {

            $label_class = "bg-danger";

        } else if ($progress !== 100 && get_my_local_time("Y-m-d") == $data->due_date) {

            $label_class = "bg-warning";

        } else {

            $label_class = "bg-primary";

        }



        $day_or_year_name = "";

        if (date("Y", strtotime(get_current_utc_time())) === date("Y", strtotime($data->due_date))) {

            $day_or_year_name = app_lang(strtolower(date("l", strtotime($data->due_date)))); //get day name from language

        } else {

            $day_or_year_name = date("Y", strtotime($data->due_date)); //get current year

        }



        $month_name = app_lang(strtolower(date("F", strtotime($data->due_date)))); //get month name from language



        $due_date = "<div class='milestone float-start' title='" . format_to_date($data->due_date) . "'>

            <span class='badge $label_class'>" . $month_name . "</span>

            <h1>" . date("d", strtotime($data->due_date)) . "</h1>

            <span>" . $day_or_year_name . "</span>

            </div>

            "

        ;



        $optoins = "";

        if ($this->can_edit_milestones()) {

            $optoins .= modal_anchor(get_uri("projects/milestone_modal_form"), "<i data-feather='edit' class='icon-16'></i>", array("class" => "edit", "title" => app_lang('edit_milestone'), "data-post-id" => $data->id));

        }



        if ($this->can_delete_milestones()) {

            $optoins .= js_anchor("<i data-feather='x' class='icon-16'></i>", array('title' => app_lang('delete_milestone'), "class" => "delete", "data-id" => $data->id, "data-action-url" => get_uri("projects/delete_milestone"), "data-action" => "delete"));

        }





        $title = "<div><b>" . $data->title . "</b></div>";

        if ($data->description) {

            $title .= "<div>" . nl2br($data->description) . "<div>";

        }



        return array(

            $data->due_date,

            $due_date,

            $title,

            $progress_bar,

            $optoins

        );

    }



    /* load comments view */



    function comments($project_id) {

        validate_numeric_value($project_id);

        $this->access_only_team_members();



        $options = array("project_id" => $project_id, "login_user_id" => $this->login_user->id);

        $view_data['comments'] = $this->Project_comments_model->get_details($options)->getResult();

        $view_data['project_id'] = $project_id;

        return $this->template->view("projects/comments/index", $view_data);

    }



    /* load comments view */



    function customer_feedback($project_id) {

        if ($this->login_user->user_type == "staff") {

            if (!$this->has_client_feedback_access_permission()) {

                app_redirect("forbidden");

            }

        }



        validate_numeric_value($project_id);

        $options = array("customer_feedback_id" => $project_id, "login_user_id" => $this->login_user->id); //customer feedback id and project id is same

        $view_data['comments'] = $this->Project_comments_model->get_details($options)->getResult();

        $view_data['customer_feedback_id'] = $project_id;

        $view_data['project_id'] = $project_id;

        return $this->template->view("projects/comments/index", $view_data);

    }



    /* save project comments */



    function save_comment() {

        $id = $this->request->getPost('id');



        $target_path = get_setting("timeline_file_path");

        $files_data = move_files_from_temp_dir_to_permanent_dir($target_path, "project_comment");



        $project_id = $this->request->getPost('project_id');

        $file_id = $this->request->getPost('file_id');

        $customer_feedback_id = $this->request->getPost('customer_feedback_id');

        $comment_id = $this->request->getPost('comment_id');

        $description = $this->request->getPost('description');



        if ($customer_feedback_id && $this->login_user->user_type == "staff") {

            if (!$this->has_client_feedback_access_permission()) {

                app_redirect("forbidden");

            }

        }



        $data = array(

            "created_by" => $this->login_user->id,

            "created_at" => get_current_utc_time(),

            "project_id" => $project_id,

            "file_id" => $file_id ? $file_id : 0,

            "task_id" => 0,

            "customer_feedback_id" => $customer_feedback_id ? $customer_feedback_id : 0,

            "comment_id" => $comment_id ? $comment_id : 0,

            "description" => $description

        );



        $data = clean_data($data);



        $data["files"] = $files_data; //don't clean serilized data



        $save_id = $this->Project_comments_model->save_comment($data, $id);

        if ($save_id) {

            $response_data = "";

            $options = array("id" => $save_id, "login_user_id" => $this->login_user->id);



            if ($this->request->getPost("reload_list")) {

                $view_data['comments'] = $this->Project_comments_model->get_details($options)->getResult();

                $response_data = $this->template->view("projects/comments/comment_list", $view_data);

            }

            echo json_encode(array("success" => true, "data" => $response_data, 'message' => app_lang('comment_submited')));



            $comment_info = $this->Project_comments_model->get_one($save_id);



            $notification_options = array("project_id" => $comment_info->project_id, "project_comment_id" => $save_id);



            if ($comment_info->file_id) { //file comment

                $notification_options["project_file_id"] = $comment_info->file_id;

                log_notification("project_file_commented", $notification_options);

            } else if ($comment_info->customer_feedback_id) {  //customer feedback comment

                if ($comment_id) {

                    log_notification("project_customer_feedback_replied", $notification_options);

                } else {

                    log_notification("project_customer_feedback_added", $notification_options);

                }

            } else {  //project comment

                if ($comment_id) {

                    log_notification("project_comment_replied", $notification_options);

                } else {

                    log_notification("project_comment_added", $notification_options);

                }

            }

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('error_occurred')));

        }

    }



    function delete_comment($id = 0) {



        if (!$id) {

            exit();

        }



        $comment_info = $this->Project_comments_model->get_one($id);



        //only admin and creator can delete the comment

        if (!($this->login_user->is_admin || $comment_info->created_by == $this->login_user->id)) {

            app_redirect("forbidden");

        }





        //delete the comment and files

        if ($this->Project_comments_model->delete($id) && $comment_info->files) {



            //delete the files

            $file_path = get_setting("timeline_file_path");

            $files = unserialize($comment_info->files);



            foreach ($files as $file) {

                delete_app_files($file_path, array($file));

            }

        }

    }



    /* load all replies of a comment */



    function view_comment_replies($comment_id) {

        validate_numeric_value($comment_id);

        $view_data['reply_list'] = $this->Project_comments_model->get_details(array("comment_id" => $comment_id))->getResult();

        return $this->template->view("projects/comments/reply_list", $view_data);

    }



    /* show comment reply form */



    function comment_reply_form($comment_id, $type = "project", $type_id = 0) {

        validate_numeric_value($comment_id);

        validate_numeric_value($type_id);



        $view_data['comment_id'] = $comment_id;



        if ($type === "project") {

            $view_data['project_id'] = $type_id;

        } else if ($type === "task") {

            $view_data['task_id'] = $type_id;

        } else if ($type === "file") {

            $view_data['file_id'] = $type_id;

        } else if ($type == "customer_feedback") {

            $view_data['project_id'] = $type_id;

        }

        return $this->template->view("projects/comments/reply_form", $view_data);

    }



    /* load files view */



    function files($project_id) {

        validate_numeric_value($project_id);



        $this->init_project_permission_checker($project_id);



        if (!$this->can_view_files()) {

            app_redirect("forbidden");

        }



        $view_data['can_add_files'] = $this->can_add_files();

        $options = array("project_id" => $project_id);

        $view_data['files'] = $this->Project_files_model->get_details($options)->getResult();

        $view_data['project_id'] = $project_id;



        $file_categories = $this->File_category_model->get_details()->getResult();

        $file_categories_dropdown = array(array("id" => "", "text" => "- " . app_lang("category") . " -"));



        if ($file_categories) {

            foreach ($file_categories as $file_category) {

                $file_categories_dropdown[] = array("id" => $file_category->id, "text" => $file_category->name);

            }

        }



        $view_data["file_categories_dropdown"] = json_encode($file_categories_dropdown);



        $view_data["custom_field_headers"] = $this->Custom_fields_model->get_custom_field_headers_for_table("project_files", $this->login_user->is_admin, $this->login_user->user_type);

        $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("project_files", $this->login_user->is_admin, $this->login_user->user_type);



        return $this->template->view("projects/files/index", $view_data);

    }



    function view_file($file_id = 0) {

        validate_numeric_value($file_id);

        $file_info = $this->Project_files_model->get_details(array("id" => $file_id))->getRow();



        if ($file_info) {



            $this->init_project_permission_checker($file_info->project_id);



            if (!$this->can_view_files()) {

                app_redirect("forbidden");

            }



            $view_data['can_comment_on_files'] = $this->can_comment_on_files();



            $file_url = get_source_url_of_file(make_array_of_file($file_info), get_setting("project_file_path") . $file_info->project_id . "/");



            $view_data["file_url"] = $file_url;

            $view_data["is_image_file"] = is_image_file($file_info->file_name);

            $view_data["is_iframe_preview_available"] = is_iframe_preview_available($file_info->file_name);

            $view_data["is_google_preview_available"] = is_google_preview_available($file_info->file_name);

            $view_data["is_viewable_video_file"] = is_viewable_video_file($file_info->file_name);

            $view_data["is_google_drive_file"] = ($file_info->file_id && $file_info->service_type == "google") ? true : false;



            $view_data["file_info"] = $file_info;

            $options = array("file_id" => $file_id, "login_user_id" => $this->login_user->id);

            $view_data['comments'] = $this->Project_comments_model->get_details($options)->getResult();

            $view_data['file_id'] = $file_id;

            $view_data['project_id'] = $file_info->project_id;

            $view_data['current_url'] = get_uri("projects/view_file/" . $file_id);

            return $this->template->view("projects/files/view", $view_data);

        } else {

            show_404();

        }

    }



    /* file upload modal */



    function file_modal_form() {

        $view_data['model_info'] = $this->Project_files_model->get_one($this->request->getPost('id'));

        $project_id = $this->request->getPost('project_id') ? $this->request->getPost('project_id') : $view_data['model_info']->project_id;



        $view_data["custom_fields"] = $this->Custom_fields_model->get_combined_details("project_files", $view_data['model_info']->id, $this->login_user->is_admin, $this->login_user->user_type)->getResult();



        $this->init_project_permission_checker($project_id);



        if (!$this->can_add_files()) {

            app_redirect("forbidden");

        }



        $view_data['project_id'] = $project_id;



        $file_categories = $this->File_category_model->get_details()->getResult();

        $file_categories_dropdown = array("" => "-");



        if ($file_categories) {

            foreach ($file_categories as $file_category) {

                $file_categories_dropdown[$file_category->id] = $file_category->name;

            }

        }



        $view_data["file_categories_dropdown"] = $file_categories_dropdown;



        return $this->template->view('projects/files/modal_form', $view_data);

    }



    /* save project file data and move temp file to parmanent file directory */



    function save_file() {



        $project_id = $this->request->getPost('project_id');

        $category_id = $this->request->getPost('category_id');



        $this->init_project_permission_checker($project_id);



        if (!$this->can_add_files()) {

            app_redirect("forbidden");

        }



        $id = $this->request->getPost('id');



        $files = $this->request->getPost("files");

        $success = false;

        $now = get_current_utc_time();



        $target_path = getcwd() . "/" . get_setting("project_file_path") . $project_id . "/";



        if ($id) {

            $data = array(

                "description" => $this->request->getPost('description'),

                "category_id" => $category_id ? $category_id : 0

            );



            $success = $this->Project_files_model->ci_save($data, $id);

            save_custom_fields("project_files", $success, $this->login_user->is_admin, $this->login_user->user_type);

        } else {

            //process the fiiles which has been uploaded by dropzone

            if ($files && get_array_value($files, 0)) {

                foreach ($files as $file) {

                    $file_name = $this->request->getPost('file_name_' . $file);

                    $file_info = move_temp_file($file_name, $target_path, "");

                    if ($file_info) {

                        $data = array(

                            "project_id" => $project_id,

                            "file_name" => get_array_value($file_info, 'file_name'),

                            "file_id" => get_array_value($file_info, 'file_id'),

                            "service_type" => get_array_value($file_info, 'service_type'),

                            "description" => $this->request->getPost('description_' . $file),

                            "file_size" => $this->request->getPost('file_size_' . $file),

                            "created_at" => $now,

                            "uploaded_by" => $this->login_user->id,

                            "category_id" => $category_id ? $category_id : 0

                        );



                        $data = clean_data($data);



                        $success = $this->Project_files_model->ci_save($data);

                        save_custom_fields("project_files", $success, $this->login_user->is_admin, $this->login_user->user_type);

                        log_notification("project_file_added", array("project_id" => $project_id, "project_file_id" => $success));

                    } else {

                        $success = false;

                    }

                }

            }

            //process the files which has been submitted manually

            if ($_FILES) {

                $files = $_FILES['manualFiles'];

                if ($files && count($files) > 0) {

                    $description = $this->request->getPost('description');

                    foreach ($files["tmp_name"] as $key => $file) {

                        $temp_file = $file;

                        $file_name = $files["name"][$key];

                        $file_size = $files["size"][$key];



                        $file_info = move_temp_file($file_name, $target_path, "", $temp_file);

                        if ($file_info) {

                            $data = array(

                                "project_id" => $project_id,

                                "file_name" => get_array_value($file_info, 'file_name'),

                                "file_id" => get_array_value($file_info, 'file_id'),

                                "service_type" => get_array_value($file_info, 'service_type'),

                                "description" => get_array_value($description, $key),

                                "file_size" => $file_size,

                                "created_at" => $now,

                                "uploaded_by" => $this->login_user->id

                            );

                            $success = $this->Project_files_model->ci_save($data);

                            save_custom_fields("project_files", $success, $this->login_user->is_admin, $this->login_user->user_type);

                            log_notification("project_file_added", array("project_id" => $project_id, "project_file_id" => $success));

                        }

                    }

                }

            }

        }



        if ($success) {

            echo json_encode(array("success" => true, 'message' => app_lang('record_saved')));

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('error_occurred')));

        }

    }



    /* upload a post file */



    function upload_file() {

        upload_file_to_temp();

    }



    /* check valid file for project */



    function validate_project_file() {

        return validate_post_file($this->request->getPost("file_name"));

    }



    /* delete a file */



    function delete_file() {



        $id = $this->request->getPost('id');

        $info = $this->Project_files_model->get_one($id);



        $this->init_project_permission_checker($info->project_id);



        if (!$this->can_delete_files($info->uploaded_by)) {

            app_redirect("forbidden");

        }



        if ($this->Project_files_model->delete($id)) {



            //delete the files

            $file_path = get_setting("project_file_path");

            delete_app_files($file_path . $info->project_id . "/", array(make_array_of_file($info)));



            log_notification("project_file_deleted", array("project_id" => $info->project_id, "project_file_id" => $id));

            echo json_encode(array("success" => true, 'message' => app_lang('record_deleted')));

        } else {

            echo json_encode(array("success" => false, 'message' => app_lang('record_cannot_be_deleted')));

        }

    }



    /* download a file */



    function download_file($id) {



        $file_info = $this->Project_files_model->get_one($id);



        $this->init_project_permission_checker($file_info->project_id);

        if (!$this->can_view_files()) {

            app_redirect("forbidden");

        }



        //serilize the path

        $file_data = serialize(array(array("file_name" => $file_info->project_id . "/" . $file_info->file_name, "file_id" => $file_info->file_id, "service_type" => $file_info->service_type)));



        //delete the file

        return $this->download_app_files(get_setting("project_file_path"), $file_data);

    }



    /* download multiple files as zip */



    function download_multiple_files($files_ids = "") {



        if ($files_ids) {





            $files_ids_array = explode('-', $files_ids);



            $files = $this->Project_files_model->get_files($files_ids_array);



            if ($files) {

                $file_path_array = array();

                $project_id = 0;



                foreach ($files->getResult() as $file_info) {



                    //we have to check the permission for each file

                    //initialize the permission check only if the project id is different



                    if ($project_id != $file_info->project_id) {

                        $this->init_project_permission_checker($file_info->project_id);

                        $project_id = $file_info->project_id;

                    }



                    if (!$this->can_view_files()) {

                        app_redirect("forbidden");

                    }



                    $file_path_array[] = array("file_name" => $file_info->project_id . "/" . $file_info->file_name, "file_id" => $file_info->file_id, "service_type" => $file_info->service_type);

                }



                $serialized_file_data = serialize($file_path_array);



                return $this->download_app_files(get_setting("project_file_path"), $serialized_file_data);

            }

        }

    }



    /* download files by zip */



    function download_comment_files($id) {



        $info = $this->Project_comments_model->get_one($id);



        $this->init_project_permission_checker($info->project_id);

        if ($this->login_user->user_type == "client" && !$this->is_clients_project) {

            app_redirect("forbidden");

        } else if ($this->login_user->user_type == "user" && !$this->is_user_a_project_member) {

            app_redirect("forbidden");

        }



        return $this->download_app_files(get_setting("timeline_file_path"), $info->files);

    }



    /* list of files, prepared for datatable  */



    function files_list_data($project_id = 0) {

        validate_numeric_value($project_id);

        $this->init_project_permission_checker($project_id);



        if (!$this->can_view_files()) {

            app_redirect("forbidden");

        }



        $custom_fields = $this->Custom_fields_model->get_available_fields_for_table("project_files", $this->login_user->is_admin, $this->login_user->user_type);



        $options = array(

            "project_id" => $project_id,

            "category_id" => $this->request->getPost("category_id"),

            "custom_fields" => $custom_fields,

            "custom_field_filter" => $this->prepare_custom_field_filter_values("project_files", $this->login_user->is_admin, $this->login_user->user_type)

        );



        $list_data = $this->Project_files_model->get_details($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_file_row($data, $custom_fields);

        }

        echo json_encode(array("data" => $result));

    }



    /* prepare a row of file list table */



    private function _make_file_row($data, $custom_fields) {

        $file_icon = get_file_icon(strtolower(pathinfo($data->file_name, PATHINFO_EXTENSION)));



        $image_url = get_avatar($data->uploaded_by_user_image);

        $uploaded_by = "<span class='avatar avatar-xs mr10'><img src='$image_url' alt='...'></span> $data->uploaded_by_user_name";



        if ($data->uploaded_by_user_type == "staff") {

            $uploaded_by = get_team_member_profile_link($data->uploaded_by, $uploaded_by);

        } else {

            $uploaded_by = get_client_contact_profile_link($data->uploaded_by, $uploaded_by);

        }



        $description = "<div class='float-start text-wrap'>" .

                js_anchor(remove_file_prefix($data->file_name), array('title' => "", "data-toggle" => "app-modal", "data-sidebar" => "1", "data-url" => get_uri("projects/view_file/" . $data->id)));



        if ($data->description) {

            $description .= "<br /><span class='text-wrap'>" . $data->description . "</span></div>";

        } else {

            $description .= "</div>";

        }



        //show checkmark to download multiple files

        $checkmark = js_anchor("<span class='checkbox-blank mr15 float-start'></span>", array('title' => "", "class" => "", "data-id" => $data->id, "data-act" => "download-multiple-file-checkbox")) . $data->id;



        $row_data = array(

            $checkmark,

            "<div data-feather='$file_icon' class='mr10 float-start'></div>" . $description,

            $data->category_name ? $data->category_name : "-",

            convert_file_size($data->file_size),

            $uploaded_by,

            format_to_datetime($data->created_at)

        );



        foreach ($custom_fields as $field) {

            $cf_id = "cfv_" . $field->id;

            $row_data[] = $this->template->view("custom_fields/output_" . $field->field_type, array("value" => $data->$cf_id));

        }



        $options = anchor(get_uri("projects/download_file/" . $data->id), "<i data-feather='download-cloud' class='icon-16'></i>", array("title" => app_lang("download")));

        if ($this->can_add_files()) {

            $options .= modal_anchor(get_uri("projects/file_modal_form"), "<i data-feather='edit' class='icon-16'></i>", array("class" => "edit", "title" => app_lang('edit_files'), "data-post-id" => $data->id));

        }

        if ($this->can_delete_files($data->uploaded_by)) {

            $options .= js_anchor("<i data-feather='x' class='icon-16'></i>", array('title' => app_lang('delete_file'), "class" => "delete", "data-id" => $data->id, "data-action-url" => get_uri("projects/delete_file"), "data-action" => "delete-confirmation"));

        }



        $row_data[] = $options;



        return $row_data;

    }



    /* load notes view */



    function notes($project_id) {

        validate_numeric_value($project_id);

        $this->access_only_team_members();

        $view_data['project_id'] = $project_id;

        return $this->template->view("projects/notes/index", $view_data);

    }



    /* load history view */



    function history($offset = 0, $log_for = "", $log_for_id = "", $log_type = "", $log_type_id = "") {

        if ($this->login_user->user_type !== "staff" && ($this->login_user->user_type == "client" && get_setting("client_can_view_activity") !== "1")) {

            app_redirect("forbidden");

        }



        $view_data['offset'] = $offset;

        $view_data['activity_logs_params'] = array("log_for" => $log_for, "log_for_id" => $log_for_id, "log_type" => $log_type, "log_type_id" => $log_type_id, "limit" => 20, "offset" => $offset);

        return $this->template->view("projects/history/index", $view_data);

    }



    /* load project members view */



    function members($project_id = 0) {

        validate_numeric_value($project_id);

        $this->access_only_team_members();

        $view_data['project_id'] = $project_id;

        return $this->template->view("projects/project_members/index", $view_data);

    }



    /* load payments tab  */



    function payments($project_id) {

        validate_numeric_value($project_id);

        $this->access_only_team_members();

        if ($project_id) {

            $view_data['project_info'] = $this->Projects_model->get_details(array("id" => $project_id))->getRow();

            $view_data['project_id'] = $project_id;

            return $this->template->view("projects/payments/index", $view_data);

        }

    }



    /* load invoices tab  */



    function invoices($project_id, $client_id = 0) {

        $this->access_only_team_members_or_client_contact($client_id);

        validate_numeric_value($project_id);

        if ($project_id) {

            $view_data['project_id'] = $project_id;

            $view_data['project_info'] = $this->Projects_model->get_details(array("id" => $project_id))->getRow();



            $view_data["custom_field_headers"] = $this->Custom_fields_model->get_custom_field_headers_for_table("invoices", $this->login_user->is_admin, $this->login_user->user_type);

            $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("invoices", $this->login_user->is_admin, $this->login_user->user_type);



            $view_data["can_edit_invoices"] = $this->can_edit_invoices();



            return $this->template->view("projects/invoices/index", $view_data);

        }

    }



    /* load expenses tab  */



    function expenses($project_id) {

        validate_numeric_value($project_id);

        $this->access_only_team_members();

        if ($project_id) {

            $view_data['project_id'] = $project_id;



            $view_data["custom_field_headers"] = $this->Custom_fields_model->get_custom_field_headers_for_table("expenses", $this->login_user->is_admin, $this->login_user->user_type);

            $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("expenses", $this->login_user->is_admin, $this->login_user->user_type);



            return $this->template->view("projects/expenses/index", $view_data);

        }

    }



    //save project status

    function change_status($project_id, $status_id) {

        if ($project_id && $this->can_edit_projects() && $status_id) {

            validate_numeric_value($project_id);

            validate_numeric_value($status_id);

            $status_data = array("status_id" => $status_id);

            $save_id = $this->Projects_model->ci_save($status_data, $project_id);



            //send notification

            if ($status_id == 2) {

                log_notification("project_completed", array("project_id" => $save_id));

            }

        }

    }



    /* load project settings modal */



    function settings_modal_form() {

        $project_id = $this->request->getPost('project_id');



        $can_edit_timesheet_settings = $this->can_edit_timesheet_settings($project_id);

        $can_edit_slack_settings = $this->can_edit_slack_settings();

        $can_create_projects = $this->can_create_projects();



        if (!$project_id || !($can_edit_timesheet_settings || $can_edit_slack_settings || $can_create_projects)) {

            app_redirect("forbidden");

        }





        $this->init_project_settings($project_id);



        $view_data['project_id'] = $project_id;

        $view_data['can_edit_timesheet_settings'] = $can_edit_timesheet_settings;

        $view_data['can_edit_slack_settings'] = $can_edit_slack_settings;

        $view_data["can_create_projects"] = $this->can_create_projects();



        $task_statuses_dropdown = array();

        $task_statuses = $this->Task_status_model->get_details()->getResult();

        foreach ($task_statuses as $task_status) {

            $task_statuses_dropdown[] = array("id" => $task_status->id, "text" => $task_status->key_name ? app_lang($task_status->key_name) : $task_status->title);

        }



        $view_data["task_statuses_dropdown"] = json_encode($task_statuses_dropdown);

        $view_data["project_info"] = $this->Projects_model->get_one($project_id);



        return $this->template->view('projects/settings/modal_form', $view_data);

    }



    /* save project settings */



    function save_settings() {

        $project_id = $this->request->getPost('project_id');



        $can_edit_timesheet_settings = $this->can_edit_timesheet_settings($project_id);

        $can_edit_slack_settings = $this->can_edit_slack_settings();

        $can_create_projects = $this->can_create_projects();



        if (!$project_id || !($can_edit_timesheet_settings || $can_edit_slack_settings || $can_create_projects)) {

            app_redirect("forbidden");

        }



        $this->validate_submitted_data(array(

            "project_id" => "required|numeric"

        ));



        $settings = array();

        if ($can_edit_timesheet_settings) {

            $settings[] = "client_can_view_timesheet";

        }



        if ($can_edit_slack_settings) {

            $settings[] = "project_enable_slack";

            $settings[] = "project_slack_webhook_url";

        }



        if ($can_create_projects) {

            $settings[] = "remove_task_statuses";

        }



        foreach ($settings as $setting) {

            $value = $this->request->getPost($setting);

            if (!$value) {

                $value = "";

            }



            $this->Project_settings_model->save_setting($project_id, $setting, $value);

        }



        //send test message

        if ($can_edit_slack_settings && $this->request->getPost("send_a_test_message")) {

            helper('notifications');

            if (send_slack_notification("test_slack_notification", $this->login_user->id, 0, $this->request->getPost("project_slack_webhook_url"))) {

                echo json_encode(array("success" => true, 'message' => app_lang('settings_updated')));

            } else {

                echo json_encode(array("success" => false, 'message' => app_lang('slack_notification_error_message')));

            }

        } else {

            echo json_encode(array("success" => true, 'message' => app_lang('settings_updated')));

        }

    }



    /* get member suggestion with start typing '@' */



    function get_member_suggestion_to_mention() {



        $this->validate_submitted_data(array(

            "project_id" => "required|numeric"

        ));



        $project_id = $this->request->getPost("project_id");



        $project_members = $this->Project_members_model->get_project_members_dropdown_list($project_id, "", $this->can_access_clients(true))->getResult();

        $project_members_dropdown = array();

        foreach ($project_members as $member) {

            $project_members_dropdown[] = array("name" => $member->member_name, "content" => "@[" . $member->member_name . " :" . $member->user_id . "]");

        }



        if ($project_members_dropdown) {

            echo json_encode(array("success" => TRUE, "data" => $project_members_dropdown));

        } else {

            echo json_encode(array("success" => FALSE));

        }

    }



    //reset projects dropdown on changing of client 

    function get_projects_of_selected_client_for_filter() {

        $this->access_only_team_members();

        $client_id = $this->request->getPost("client_id");

        if ($client_id) {

            $projects = $this->Projects_model->get_all_where(array("client_id" => $client_id, "deleted" => 0), 0, 0, "title")->getResult();

            $projects_dropdown = array(array("id" => "", "text" => "- " . app_lang("project") . " -"));

            foreach ($projects as $project) {

                $projects_dropdown[] = array("id" => $project->id, "text" => $project->title);

            }

            echo json_encode($projects_dropdown);

        } else {

            //we have show all projects by de-selecting client

            echo json_encode($this->_get_all_projects_dropdown_list());

        }

    }



    //get clients dropdown

    private function _get_clients_dropdown() {

        $clients_dropdown = array(array("id" => "", "text" => "- " . app_lang("client") . " -"));

        $clients = $this->Clients_model->get_dropdown_list(array("company_name"), "id", array("is_lead" => 0));

        foreach ($clients as $key => $value) {

            $clients_dropdown[] = array("id" => $key, "text" => $value);

        }

        return $clients_dropdown;

    }



    //show timesheets chart

    function timesheet_chart($project_id = 0) {

        validate_numeric_value($project_id);

        $members = $this->_get_members_to_manage_timesheet();



        $view_data['members_dropdown'] = json_encode($this->_prepare_members_dropdown_for_timesheet_filter($members));

        $view_data['projects_dropdown'] = json_encode($this->_get_all_projects_dropdown_list_for_timesheets_filter());

        $view_data["project_id"] = $project_id;



        return $this->template->view("projects/timesheets/timesheet_chart", $view_data);

    }



    //timesheets chart data

    function timesheet_chart_data($project_id = 0) {

        if (!$project_id) {

            $project_id = $this->request->getPost("project_id");

        }



        validate_numeric_value($project_id);



        $this->init_project_permission_checker($project_id);

        $this->init_project_settings($project_id); //since we'll check this permission project wise



        if (!$this->can_view_timesheet($project_id, true)) {

            app_redirect("forbidden");

        }



        $timesheets = array();

        $timesheets_array = array();

        $ticks = array();



        $start_date = $this->request->getPost("start_date");

        $end_date = $this->request->getPost("end_date");

        $user_id = $this->request->getPost("user_id");



        $options = array(

            "start_date" => $start_date,

            "end_date" => $end_date,

            "user_id" => $user_id,

            "project_id" => $project_id

        );



        //get allowed member ids

        $members = $this->_get_members_to_manage_timesheet();

        if ($members != "all" && $this->login_user->user_type == "staff") {

            //if user has permission to access all members, query param is not required

            //client can view all timesheet

            $options["allowed_members"] = $members;

        }



        $timesheets_result = $this->Timesheets_model->get_timesheet_statistics($options)->timesheets_data;

        $timesheet_users_result = $this->Timesheets_model->get_timesheet_statistics($options)->timesheet_users_data;



        $user_result = array();

        foreach ($timesheet_users_result AS $user) {

            $time = convert_seconds_to_time_format($user->total_sec);

            $user_result[] = "<div class='user-avatar avatar-30 avatar-circle' data-bs-toggle='tooltip' title='" . $user->user_name . " - " . $time . "'><img alt='' src='" . get_avatar($user->user_avatar) . "'></div>";

        }



        $days_of_month = date("t", strtotime($start_date));



        for ($i = 1; $i <= $days_of_month; $i++) {

            $timesheets[$i] = 0;

        }



        foreach ($timesheets_result as $value) {

            $timesheets[$value->day * 1] = $value->total_sec / 60 / 60;

        }



        foreach ($timesheets as $value) {

            $timesheets_array[] = $value;

        }



        for ($i = 1; $i <= $days_of_month; $i++) {

            $ticks[] = $i;

        }



        echo json_encode(array("timesheets" => $timesheets_array, "ticks" => $ticks, "timesheet_users_result" => $user_result));

    }



    function like_comment($comment_id = 0) {

        if ($comment_id) {

            validate_numeric_value($comment_id);

            $data = array(

                "project_comment_id" => $comment_id,

                "created_by" => $this->login_user->id

            );



            $existing = $this->Likes_model->get_one_where(array_merge($data, array("deleted" => 0)));

            if ($existing->id) {

                //liked already, unlike now

                $this->Likes_model->delete($existing->id);

            } else {

                //not liked, like now

                $data["created_at"] = get_current_utc_time();

                $this->Likes_model->ci_save($data);

            }



            $options = array("id" => $comment_id, "login_user_id" => $this->login_user->id);

            $comment = $this->Project_comments_model->get_details($options)->getRow();



            return $this->template->view("projects/comments/like_comment", array("comment" => $comment));

        }

    }



    /* load contracts tab  */



    function contracts($project_id) {

        $this->access_only_team_members();

        if ($project_id) {

            $view_data['project_id'] = $project_id;

            $view_data['project_info'] = $this->Projects_model->get_details(array("id" => $project_id))->getRow();



            $view_data["custom_field_headers"] = $this->Custom_fields_model->get_custom_field_headers_for_table("contracts", $this->login_user->is_admin, $this->login_user->user_type);

            $view_data["custom_field_filters"] = $this->Custom_fields_model->get_custom_field_filters("contracts", $this->login_user->is_admin, $this->login_user->user_type);



            return $this->template->view("projects/contracts/index", $view_data);

        }

    }



    // pin/unpin comments

    function pin_comment($comment_id = 0) {

        if ($comment_id) {

            $data = array(

                "project_comment_id" => $comment_id,

                "pinned_by" => $this->login_user->id

            );



            $existing = $this->Pin_comments_model->get_one_where(array_merge($data, array("deleted" => 0)));



            $save_id = "";

            if ($existing->id) {

                //pinned already, unpin now

                $save_id = $this->Pin_comments_model->delete($existing->id);

            } else {

                //not pinned, pin now

                $data["created_at"] = get_current_utc_time();

                $save_id = $this->Pin_comments_model->ci_save($data);

            }



            if ($save_id) {

                $options = array("id" => $save_id);

                $pinned_comments = $this->Pin_comments_model->get_details($options)->getResult();



                $status = "pinned";



                $save_data = $this->template->view("projects/comments/pinned_comments", array("pinned_comments" => $pinned_comments));

                echo json_encode(array("success" => true, "data" => $save_data, "status" => $status));

            } else {

                echo json_encode(array("success" => false));

            }

        }

    }



    /* load tickets tab  */



    function tickets($project_id) {

        $this->access_only_team_members();

        if ($project_id) {

            validate_numeric_value($project_id);

            $view_data['project_id'] = $project_id;



            $view_data["custom_field_headers"] = $this->Custom_fields_model->get_custom_field_headers_for_table("tickets", $this->login_user->is_admin, $this->login_user->user_type);



            return $this->template->view("projects/tickets/index", $view_data);

        }

    }



    function file_category($project_id = 0) {

        $this->access_only_team_members();

        validate_numeric_value($project_id);

        $this->init_project_permission_checker($project_id);

        if (!$this->can_view_files()) {

            app_redirect("forbidden");

        }



        $view_data["project_id"] = $project_id;

        $view_data['can_add_files'] = $this->can_add_files();

        return $this->template->view("projects/files/category/index", $view_data);

    }



    function file_category_list_data($project_id = 0) {

        $this->access_only_team_members();

        validate_numeric_value($project_id);

        $this->init_project_permission_checker($project_id);

        if (!$this->can_view_files()) {

            app_redirect("forbidden");

        }



        $options = array("type" => "project");

        $list_data = $this->File_category_model->get_details($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_file_category_row($data, $project_id);

        }



        echo json_encode(array("data" => $result));

    }



    private function _file_category_row_data($id, $project_id = 0) {

        $options = array("id" => $id);

        $data = $this->File_category_model->get_details($options)->getRow();



        return $this->_make_file_category_row($data, $project_id);

    }



    private function _make_file_category_row($data, $project_id = 0) {

        $options = "";

        if ($this->can_add_files()) {

            $options .= modal_anchor(get_uri("projects/file_category_modal_form"), "<i data-feather='edit' class='icon-16'></i>", array("class" => "edit", "title" => app_lang('edit_category'), "data-post-id" => $data->id, "data-post-project_id" => $project_id));

        }



        if ($this->can_delete_files()) {

            $options .= js_anchor("<i data-feather='x' class='icon-16'></i>", array('title' => app_lang('delete_category'), "class" => "delete", "data-id" => $data->id, "data-action-url" => get_uri("projects/delete_file_category"), "data-action" => "delete", "data-post-project_id" => $project_id));

        }



        return array(

            $data->name,

            $options

        );

    }



    function file_category_modal_form() {

        $this->access_only_team_members();

        $project_id = $this->request->getPost('project_id');

        $this->init_project_permission_checker($project_id);

        if (!$this->can_add_files()) {

            app_redirect("forbidden");

        }



        $view_data['model_info'] = $this->File_category_model->get_one($this->request->getPost('id'));

        $view_data['project_id'] = $project_id;

        return $this->template->view('projects/files/category/modal_form', $view_data);

    }



    function save_file_category() {

        $this->access_only_team_members();

        $project_id = $this->request->getPost('project_id');

        $this->init_project_permission_checker($project_id);

        if (!$this->can_add_files()) {

            app_redirect("forbidden");

        }



        $id = $this->request->getPost("id");



        $data = array(

            "name" => $this->request->getPost('name'),

            "type" => "project"

        );



        $save_id = $this->File_category_model->ci_save($data, $id);



        if ($save_id) {

            echo json_encode(array("success" => true, "data" => $this->_file_category_row_data($save_id, $project_id), 'id' => $save_id, 'message' => app_lang('record_saved')));

        } else {



            echo json_encode(array("success" => false, 'message' => app_lang('error_occurred')));

        }

    }



    function delete_file_category() {

        $this->access_only_team_members();

        $project_id = $this->request->getPost('project_id');

        $this->init_project_permission_checker($project_id);

        if (!$this->can_delete_files()) {

            app_redirect("forbidden");

        }



        $id = $this->request->getPost('id');



        if ($this->request->getPost('undo')) {

            if ($this->File_category_model->delete($id, true)) {

                echo json_encode(array("success" => true, "data" => $this->_file_category_row_data($id, $project_id), "message" => app_lang('record_undone')));

            } else {

                echo json_encode(array("success" => false, app_lang('error_occurred')));

            }

        } else {

            if ($this->File_category_model->delete($id)) {

                echo json_encode(array("success" => true, 'message' => app_lang('record_deleted')));

            } else {

                echo json_encode(array("success" => false, 'message' => app_lang('record_cannot_be_deleted')));

            }

        }

    }



    /* delete multiple files */



    function delete_multiple_files($files_ids = "") {



        if ($files_ids) {



            $files_ids_array = explode('-', $files_ids);

            $files = $this->Project_files_model->get_files($files_ids_array)->getResult();

            $is_success = true;

            $is_permission_success = true;

            $project_id = get_array_value($files, 0)->project_id;

            $this->init_project_permission_checker($project_id);



            foreach ($files as $file) {



                if (!$this->can_delete_files($file->uploaded_by)) {

                    $is_permission_success = false;

                    continue; //continue to the next file

                }



                if ($this->Project_files_model->delete($file->id)) {



                    //delete the files

                    $file_path = get_setting("project_file_path");

                    delete_app_files($file_path . $file->project_id . "/", array(make_array_of_file($file)));



                    log_notification("project_file_deleted", array("project_id" => $file->project_id, "project_file_id" => $file->id));

                } else {

                    $is_success = false;

                }

            }



            if ($is_success && $is_permission_success) {

                echo json_encode(array("success" => true, 'message' => app_lang('record_deleted')));

            } else {

                if (!$is_permission_success) {

                    echo json_encode(array("success" => false, 'message' => app_lang('file_delete_permission_error_message')));

                } else {

                    echo json_encode(array("success" => false, 'message' => app_lang('record_cannot_be_deleted')));

                }

            }

        }

    }



    private function has_client_feedback_access_permission() {

        if ($this->login_user->user_type != "client") {

            if ($this->login_user->is_admin || get_array_value($this->login_user->permissions, "client_feedback_access_permission") || $this->can_manage_all_projects()) {

                return true;

            }

        }

    }



    function show_my_open_timers() {

        $timers = $this->Timesheets_model->get_open_timers($this->login_user->id);

        $view_data["timers"] = $timers->getResult();

        return $this->template->view("projects/open_timers", $view_data);

    }



    function task_timesheet($task_id, $project_id) {

        validate_numeric_value($task_id);

        validate_numeric_value($project_id);



        $this->init_project_permission_checker($project_id);

        $this->init_project_settings($project_id);



        if (!$this->can_view_timesheet($project_id, true)) {

            app_redirect("forbidden");

        }

        $options = array(

            "project_id" => $project_id,

            "status" => "none_open",

            "task_id" => $task_id,

        );



        //get allowed member ids

        $members = $this->_get_members_to_manage_timesheet();

        if ($members != "all" && $this->login_user->user_type == "staff") {

            //if user has permission to access all members, query param is not required

            //client can view all timesheet

            $options["allowed_members"] = $members;

        }



        $view_data['task_timesheet'] = $this->Timesheets_model->get_details($options)->getResult();

        return $this->template->view("tasks/task_timesheet", $view_data);

    }



    //for old notifications, redirect to tasks/view



    function task_view($task_id = 0) {

        if ($task_id) {

            app_redirect("tasks/view/" . $task_id);

        }

    }



    function team_members_summary() {

        if (!$this->can_manage_all_projects()) {

            app_redirect("forbidden");

        }



        $view_data["project_status_text_info"] = get_project_status_text_info();

        $view_data["show_time_logged_data"] = get_setting("module_project_timesheet") ? 1 : 0;



        return $this->template->rander("projects/reports/team_members_summary", $view_data);

    }



    function team_members_summary_data() {

        if (!$this->can_manage_all_projects()) {

            app_redirect("forbidden");

        }

        $options = array(

            "start_date_from" => $this->request->getPost("start_date_from"),

            "start_date_to" => $this->request->getPost("start_date_to")

        );



        $list_data = $this->Projects_model->get_team_members_summary($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_team_members_summary_row($data);

        }

        echo json_encode(array("data" => $result));

    }



    private function _make_team_members_summary_row($data) {

        $image_url = get_avatar($data->image);

        $member = "<span class='avatar avatar-xs mr10'><img src='$image_url' alt=''></span> $data->team_member_name";



        $duration = convert_seconds_to_time_format($data->total_secconds_worked);



        $row_data = array(

            get_team_member_profile_link($data->team_member_id, $member),

            $data->open_projects,

            $data->completed_projects,

            $data->hold_projects,

            $data->open_tasks,

            $data->completed_tasks,

            $duration,

            to_decimal_format(convert_time_string_to_decimal($duration)),

        );



        return $row_data;

    }



    function clients_summary() {

        if (!$this->can_manage_all_projects()) {

            app_redirect("forbidden");

        }

        $view_data["project_status_text_info"] = get_project_status_text_info();

        $view_data["show_time_logged_data"] = get_setting("module_project_timesheet") ? 1 : 0;



        return $this->template->view("projects/reports/clints_summary", $view_data);

    }



    function clients_summary_data() {

        if (!$this->can_manage_all_projects()) {

            app_redirect("forbidden");

        }

        $options = array(

            "start_date_from" => $this->request->getPost("start_date_from"),

            "start_date_to" => $this->request->getPost("start_date_to")

        );



        $list_data = $this->Projects_model->get_clients_summary($options)->getResult();

        $result = array();

        foreach ($list_data as $data) {

            $result[] = $this->_make_clients_summary_row($data);

        }

        echo json_encode(array("data" => $result));

    }



    private function _make_clients_summary_row($data) {





        $client_name = anchor(get_uri("clients/view/" . $data->client_id), $data->client_name);



        $duration = convert_seconds_to_time_format($data->total_secconds_worked);



        $row_data = array(

            $client_name,

            $data->open_projects ? $data->open_projects : 0,

            $data->completed_projects ? $data->completed_projects : 0,

            $data->hold_projects ? $data->hold_projects : 0,

            $data->open_tasks ? $data->open_tasks : 0,

            $data->completed_tasks ? $data->completed_tasks : 0,

            $duration,

            to_decimal_format(convert_time_string_to_decimal($duration)),

        );



        return $row_data;

    }



    private function client_can_view_tasks() {

        if ($this->login_user->user_type != "staff") {

            //check settings for client's project permission

            if (get_setting("client_can_view_tasks")) {

                //even the settings allow to create/edit task, the client can only create their own project's tasks

                return $this->is_clients_project;

            }

        }

    }



}



/* End of file projects.php */

/* Location: ./app/controllers/projects.php */
Editor is loading...