Files
2025-10-29 11:09:43 +01:00

1394 lines
76 KiB
PHP

<?php
use app\services\MergeTickets;
defined('BASEPATH') or exit('No direct script access allowed');
class Tickets_model extends App_Model {
private $piping = false;
public function __construct() {
parent::__construct();
}
public function ticket_count($status = null, $playground = false) {
$where = 'AND merged_ticket_id is NULL';
if (!is_admin()) {
$this->load->model('departments_model');
$staff_deparments_ids = $this->departments_model->get_staff_departments(get_staff_user_id(), true, $playground);
if (get_option('staff_access_only_assigned_departments') == 1) {
$departments_ids = [];
if (count($staff_deparments_ids) == 0) {
$departments = $this->departments_model->get();
foreach ($departments as $department) {
array_push($departments_ids, $department['departmentid']);
}
} else {
$departments_ids = $staff_deparments_ids;
}
if (count($departments_ids) > 0) {
$where = 'AND department IN (SELECT departmentid FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'staff_departments WHERE departmentid IN (' . implode(',', $departments_ids) . ') AND staffid="' . get_staff_user_id() . '")';
}
}
}
$_where = '';
if (!is_null($status)) {
if ($where == '') {
$_where = 'status=' . $status;
} else {
$_where = 'status=' . $status . ' ' . $where;
}
}
return total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', $_where);
}
public function insert_piped_ticket($data, $playground = false) {
$data = hooks()->apply_filters('piped_ticket_data', $data);
$this->piping = true;
$attachments = $data['attachments'];
$subject = $data['subject'];
// Prevent insert ticket to database if mail delivery error happen
// This will stop createing a thousand tickets
$system_blocked_subjects = ['Mail delivery failed', 'failure notice', 'Returned mail: see transcript for details', 'Undelivered Mail Returned to Sender', ];
$subject_blocked = false;
foreach ($system_blocked_subjects as $sb) {
if (strpos('x' . $subject, $sb) !== false) {
$subject_blocked = true;
break;
}
}
if ($subject_blocked == true) {
return;
}
$message = $data['body'];
$name = $data['fromname'];
$email = $data['email'];
$to = $data['to'];
$cc = $data['cc'] ?? [];
$subject = $subject;
$message = $message;
$this->load->model('spam_filters_model');
$mailstatus = $this->spam_filters_model->check($email, $subject, $message, 'tickets', $playground);
// No spam found
if (!$mailstatus) {
$pos = strpos($subject, '[Ticket ID: ');
if ($pos === false) {
} else {
$tid = substr($subject, $pos + 12);
$tid = substr($tid, 0, strpos($tid, ']'));
$this->db->where('ticketid', $tid);
$data = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets')->row();
$tid = $data->ticketid;
}
$to = trim($to);
$toemails = explode(',', $to);
$department_id = false;
$userid = false;
foreach ($toemails as $toemail) {
if (!$department_id) {
$this->db->where('email', trim($toemail));
$data = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'departments')->row();
if ($data) {
$department_id = $data->departmentid;
$to = $data->email;
}
}
}
if (!$department_id) {
$mailstatus = 'Department Not Found';
} else {
if ($to == $email) {
$mailstatus = 'Blocked Potential Email Loop';
} else {
$message = trim($message);
$this->db->where('active', 1);
$this->db->where('email', $email);
$result = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'staff')->row();
if ($result) {
if ($tid) {
$data = [];
$data['message'] = $message;
$data['status'] = get_option('default_ticket_reply_status');
if (!$data['status']) {
$data['status'] = 3; // Answered
}
if ($userid == false) {
$data['name'] = $name;
$data['email'] = $email;
}
if (count($cc) > 0) {
$data['cc'] = $cc;
}
$reply_id = $this->add_reply($data, $tid, $result->staffid, $attachments, $playground);
if ($reply_id) {
$mailstatus = 'Ticket Reply Imported Successfully';
}
} else {
$mailstatus = 'Ticket ID Not Found';
}
} else {
$this->db->where('email', $email);
$result = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'contacts')->row();
if ($result) {
$userid = $result->userid;
$contactid = $result->id;
}
if ($userid == false && get_option('email_piping_only_registered') == '1') {
$mailstatus = 'Unregistered Email Address';
} else {
$filterdate = date('Y-m-d H:i:s', strtotime('-15 minutes'));
$query = 'SELECT count(*) as total FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets WHERE date > "' . $filterdate . '" AND (email="' . $this->db->escape($email) . '"';
if ($userid) {
$query.= ' OR userid=' . (int)$userid;
}
$query.= ')';
$result = $this->db->query($query)->row();
if (10 < $result->total) {
$mailstatus = 'Exceeded Limit of 10 Tickets within 15 Minutes';
} else {
if (isset($tid)) {
$data = [];
$data['message'] = $message;
$data['status'] = 1;
if ($userid == false) {
$data['name'] = $name;
$data['email'] = $email;
} else {
$data['userid'] = $userid;
$data['contactid'] = $contactid;
$this->db->where('ticketid', $tid);
$this->db->group_start();
$this->db->where('userid', $userid);
// Allow CC'ed user to reply to the ticket
$this->db->or_like('cc', $email);
$this->db->group_end();
$t = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets')->row();
if (!$t) {
$abuse = true;
}
}
if (!isset($abuse)) {
if (count($cc) > 0) {
$data['cc'] = $cc;
}
$reply_id = $this->add_reply($data, $tid, null, $attachments, $playground);
if ($reply_id) {
// Dont change this line
$mailstatus = 'Ticket Reply Imported Successfully';
}
} else {
$mailstatus = 'Ticket ID Not Found For User';
}
} else {
if (get_option('email_piping_only_registered') == 1 && !$userid) {
$mailstatus = 'Blocked Ticket Opening from Unregistered User';
} else {
if (get_option('email_piping_only_replies') == '1') {
$mailstatus = 'Only Replies Allowed by Email';
} else {
$data = [];
$data['department'] = $department_id;
$data['subject'] = $subject;
$data['message'] = $message;
$data['contactid'] = $contactid;
$data['priority'] = get_option('email_piping_default_priority');
if ($userid == false) {
$data['name'] = $name;
$data['email'] = $email;
} else {
$data['userid'] = $userid;
}
$tid = $this->add($data, null, $attachments, $playground);
if ($tid && count($cc) > 0) {
// A customer opens a ticket by mail to "support@example".com, with one or many 'Cc'
// Remember those 'Cc'.
$this->db->where('ticketid', $tid);
$this->db->update(($playground ? 'playground_' : '') . 'tickets', ['cc' => implode(',', $cc) ]);
}
// Dont change this line
$mailstatus = 'Ticket Imported Successfully';
}
}
}
}
}
}
}
}
}
if ($mailstatus == '') {
$mailstatus = 'Ticket Import Failed';
}
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_pipe_log', ['date' => date('Y-m-d H:i:s'), 'email_to' => $to, 'name' => $name ? : 'Unknown', 'email' => $email ? : 'N/A', 'subject' => $subject ? : 'N/A', 'message' => $message, 'status' => $mailstatus, ]);
return $mailstatus;
}
private function process_pipe_attachments($attachments, $ticket_id, $reply_id = '', $playground = false) {
if (!empty($attachments)) {
$ticket_attachments = [];
$allowed_extensions = array_map(function ($ext) {
return strtolower(trim($ext));
}, explode(',', get_option('ticket_attachments_file_extensions')));
$path = FCPATH . 'uploads/' . ($playground ? 'playground_' : '') . 'ticket_attachments' . '/' . $ticket_id . '/';
foreach ($attachments as $attachment) {
$filename = $attachment['filename'];
$filenameparts = explode('.', $filename);
$extension = end($filenameparts);
$extension = strtolower($extension);
if (in_array('.' . $extension, $allowed_extensions)) {
$filename = implode(array_slice($filenameparts, 0, 0 - 1));
$filename = trim(preg_replace('/[^a-zA-Z0-9-_ ]/', '', $filename));
if (!$filename) {
$filename = 'attachment';
}
if (!file_exists($path)) {
mkdir($path, 0755);
$fp = fopen($path . 'index.html', 'w');
fclose($fp);
}
$filename = unique_filename($path, $filename . '.' . $extension);
file_put_contents($path . $filename, $attachment['data']);
array_push($ticket_attachments, ['file_name' => $filename, 'filetype' => get_mime_by_extension($filename), ]);
}
}
$this->insert_ticket_attachments_to_database($ticket_attachments, $ticket_id, $reply_id, $playground);
}
}
public function get($id = '', $where = [], $playground = false) {
$this->db->select('*,' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid,' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.name as from_name,' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.email as ticket_email, ' . db_prefix() . ($playground ? 'playground_' : '') . 'departments.name as department_name, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities.name as priority_name, statuscolor, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.admin, ' . db_prefix() . ($playground ? 'playground_' : '') . 'services.name as service_name, service, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status.name as status_name,' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.ticketid, ' . db_prefix() . ($playground ? 'playground_' : '') . 'contacts.firstname as user_firstname, ' . db_prefix() . ($playground ? 'playground_' : '') . 'contacts.lastname as user_lastname,' . db_prefix() . ($playground ? 'playground_' : '') . 'staff.firstname as staff_firstname, ' . db_prefix() . ($playground ? 'playground_' : '') . 'staff.lastname as staff_lastname,lastreply,message,' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.status,subject,department,priority,' . db_prefix() . ($playground ? 'playground_' : '') . 'contacts.email,adminread,clientread,date');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'departments', db_prefix() . ($playground ? 'playground_' : '') . 'departments.departmentid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.department', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status', db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status.ticketstatusid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.status', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'services', db_prefix() . ($playground ? 'playground_' : '') . 'services.serviceid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.service', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'clients', db_prefix() . ($playground ? 'playground_' : '') . 'clients.userid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'contacts', db_prefix() . ($playground ? 'playground_' : '') . 'contacts.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.contactid', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'staff', db_prefix() . ($playground ? 'playground_' : '') . 'staff.staffid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.admin', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities', db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities.priorityid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.priority', 'left');
$this->db->where($where);
if (is_numeric($id)) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.ticketid', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets')->row();
}
$this->db->order_by('lastreply', 'asc');
if (is_client_logged_in()) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.merged_ticket_id IS NULL', null, false);
}
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets')->result_array();
}
/**
* Get ticket by id and all data
* @param mixed $id ticket id
* @param mixed $userid Optional - Tickets from USER ID
* @return object
*/
public function get_ticket_by_id($id, $userid = '', $playground = false) {
$this->db->select('*, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.name as from_name, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.email as ticket_email, ' . db_prefix() . ($playground ? 'playground_' : '') . 'departments.name as department_name, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities.name as priority_name, statuscolor, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.admin, ' . db_prefix() . ($playground ? 'playground_' : '') . 'services.name as service_name, service, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status.name as status_name, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.ticketid, ' . db_prefix() . ($playground ? 'playground_' : '') . 'contacts.firstname as user_firstname, ' . db_prefix() . ($playground ? 'playground_' : '') . 'contacts.lastname as user_lastname, ' . db_prefix() . ($playground ? 'playground_' : '') . 'staff.firstname as staff_firstname, ' . db_prefix() . ($playground ? 'playground_' : '') . 'staff.lastname as staff_lastname, lastreply, message, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.status, subject, department, priority, ' . db_prefix() . ($playground ? 'playground_' : '') . 'contacts.email, adminread, clientread, date');
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'tickets');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'departments', db_prefix() . ($playground ? 'playground_' : '') . 'departments.departmentid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.department', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status', db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status.ticketstatusid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.status', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'services', db_prefix() . ($playground ? 'playground_' : '') . 'services.serviceid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.service', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'clients', db_prefix() . ($playground ? 'playground_' : '') . 'clients.userid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'staff', db_prefix() . ($playground ? 'playground_' : '') . 'staff.staffid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.admin', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'contacts', db_prefix() . ($playground ? 'playground_' : '') . 'contacts.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.contactid', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities', db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities.priorityid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.priority', 'left');
if (strlen($id) === 32) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.ticketkey', $id);
} else {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.ticketid', $id);
}
if (is_numeric($userid)) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid', $userid);
}
$ticket = $this->db->get()->row();
if ($ticket) {
$ticket->submitter = $ticket->contactid != 0 ? ($ticket->user_firstname . ' ' . $ticket->user_lastname) : $ticket->from_name;
if (!($ticket->admin == null || $ticket->admin == 0)) {
$ticket->opened_by = $ticket->staff_firstname . ' ' . $ticket->staff_lastname;
}
$ticket->attachments = $this->get_ticket_attachments($ticket->ticketid, '', $playground);
}
return $ticket;
}
/**
* Insert ticket attachments to database
* @param array $attachments array of attachment
* @param mixed $ticketid
* @param boolean $replyid If is from reply
*/
public function insert_ticket_attachments_to_database($attachments, $ticketid, $replyid = false, $playground = false) {
foreach ($attachments as $attachment) {
$attachment['ticketid'] = $ticketid;
$attachment['dateadded'] = date('Y-m-d H:i:s');
if ($replyid !== false && is_int($replyid)) {
$attachment['replyid'] = $replyid;
}
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_attachments', $attachment);
}
}
/**
* Get ticket attachments from database
* @param mixed $id ticket id
* @param mixed $replyid Optional - reply id if is from from reply
* @return array
*/
public function get_ticket_attachments($id, $replyid = '', $playground = false) {
$this->db->where('ticketid', $id);
$this->db->where('replyid', is_numeric($replyid) ? $replyid : null);
return $this->db->get(($playground ? 'playground_' : '') . 'ticket_attachments')->result_array();
}
/**
* Add new reply to ticket
* @param mixed $data reply $_POST data
* @param mixed $id ticket id
* @param boolean $admin staff id if is staff making reply
*/
public function add_reply($data, $id, $admin = null, $pipe_attachments = false, $playground = false) {
if (isset($data['assign_to_current_user'])) {
$assigned = get_staff_user_id();
unset($data['assign_to_current_user']);
}
$unsetters = ['note_description', 'department', 'priority', 'subject', 'assigned', 'project_id', 'service', 'status_top', 'attachments', 'DataTables_Table_0_length', 'DataTables_Table_1_length', 'custom_fields', ];
foreach ($unsetters as $unset) {
if (isset($data[$unset])) {
unset($data[$unset]);
}
}
if ($admin !== null) {
$data['admin'] = $admin;
$status = $data['status'];
} else {
$status = 1;
}
if (isset($data['status'])) {
unset($data['status']);
}
$cc = '';
if (isset($data['cc'])) {
$cc = $data['cc'];
unset($data['cc']);
}
// if ticket is merged
$ticket = $this->get($id, [], $playground);
$data['ticketid'] = ($ticket && $ticket->merged_ticket_id != null) ? $ticket->merged_ticket_id : $id;
$data['date'] = date('Y-m-d H:i:s');
$data['message'] = trim($data['message']);
if ($this->piping == true) {
// $data['message'] = preg_replace('/\v+/u', '<br>', $data['message']);
}
$is_html_stripped = $this->piping === true;
// admin can have html
if (!$is_html_stripped && $admin == null && hooks()->apply_filters('ticket_message_without_html_for_non_admin', true)) {
$data['message'] = _strip_tags($data['message']);
$data['message'] = nl2br_save_html($data['message']);
}
if (!isset($data['userid'])) {
$data['userid'] = 0;
}
// $data['message'] = remove_emojis($data['message']);
$data = hooks()->apply_filters('before_ticket_reply_add', $data, $id, $admin);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
/**
* When a ticket is in status "In progress" and the customer reply to the ticket
* it changes the status to "Open" which is not normal.
*
* The ticket should keep the status "In progress"
*/
$this->db->select('status');
$this->db->where('ticketid', $id);
$old_ticket_status = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets')->row()->status;
$newStatus = hooks()->apply_filters('ticket_reply_status', ($old_ticket_status == 2 && $admin == null ? $old_ticket_status : $status), ['ticket_id' => $id, 'reply_id' => $insert_id, 'admin' => $admin, 'old_status' => $old_ticket_status]);
if (isset($assigned)) {
$this->db->where('ticketid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', ['assigned' => $assigned, ]);
}
if ($pipe_attachments != false) {
$this->process_pipe_attachments($pipe_attachments, $id, $insert_id, $playground);
} else {
$attachments = $this->handle_ticket_attachments($id, 'attachments', $playground);
if ($attachments) {
$this->insert_ticket_attachments_to_database($attachments, $id, $insert_id, $playground);
}
}
$_attachments = $this->get_ticket_attachments($id, $insert_id, $playground);
log_activity('New Ticket Reply [ReplyID: ' . $insert_id . ']');
$this->db->where('ticketid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', ['lastreply' => date('Y-m-d H:i:s'), 'status' => $newStatus, 'adminread' => 0, 'clientread' => 0, ]);
if ($old_ticket_status != $newStatus) {
hooks()->do_action('after_ticket_status_changed', ['id' => $id, 'status' => $newStatus, ]);
}
$ticket = $this->get_ticket_by_id($id, '', $playground);
$userid = $ticket->userid;
$isContact = false;
$this->load->model('cilents_model');
if ($ticket->userid != 0 && $ticket->contactid != 0) {
$email = $cilents_model->get_contact($ticket->contactid, $playground)->email;
$isContact = true;
} else {
$email = $ticket->ticket_email;
}
if ($admin == null) {
$this->load->model('departments_model');
$notifiedUsers = [];
$staff = $this->getStaffMembersForTicketNotification($ticket->department, $ticket->assigned, $playground);
foreach ($staff as $staff_key => $member) {
send_mail_template('ticket_new_reply_to_staff', $ticket, $member, $_attachments);
if (get_option('receive_notification_on_new_ticket_replies') == 1) {
$notified = add_notification(['description' => 'not_new_ticket_reply', 'touserid' => $member['staffid'], 'fromcompany' => 1, 'fromuserid' => 0, 'link' => 'tickets/ticket/' . $id, 'additional_data' => serialize([$ticket->subject, ]), ]);
if ($notified) {
array_push($notifiedUsers, $member['staffid']);
}
}
}
pusher_trigger_notification($notifiedUsers);
} else {
$this->update_staff_replying($id, '', $playground);
$total_staff_replies = total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies', ['admin is NOT NULL', 'ticketid' => $ticket->ticketid]);
if ($ticket->assigned == 0 && get_option('automatically_assign_ticket_to_first_staff_responding') == '1' && $total_staff_replies == 1) {
$this->db->where('ticketid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', ['assigned' => $admin]);
}
$sendEmail = true;
if ($isContact && total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'contacts', ['ticket_emails' => 1, 'id' => $ticket->contactid]) == 0) {
$sendEmail = false;
}
if ($sendEmail) {
send_mail_template('ticket_new_reply_to_customer', $ticket, $email, $_attachments, $cc);
}
}
if ($cc) {
// imported reply
if (is_array($cc)) {
if ($ticket->cc) {
$currentCC = explode(',', $ticket->cc);
$cc = array_unique([$cc, $currentCC]);
}
$cc = implode(',', $cc);
}
$this->db->where('ticketid', $id);
$this->db->update(($playground ? 'playground_' : '') . 'tickets', ['cc' => $cc]);
}
hooks()->do_action('after_ticket_reply_added', ['data' => $data, 'id' => $id, 'admin' => $admin, 'replyid' => $insert_id, ]);
return $insert_id;
}
return false;
}
/**
* Delete ticket reply
* @param mixed $ticket_id ticket id
* @param mixed $reply_id reply id
* @return boolean
*/
public function delete_ticket_reply($ticket_id, $reply_id, $playground = false) {
hooks()->do_action('before_delete_ticket_reply', ['ticket_id' => $ticket_id, 'reply_id' => $reply_id]);
$this->db->where('id', $reply_id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies');
if ($this->db->affected_rows() > 0) {
// Get the reply attachments by passing the reply_id to get_ticket_attachments method
$attachments = $this->get_ticket_attachments($ticket_id, $reply_id, $playground);
if (count($attachments) > 0) {
foreach ($attachments as $attachment) {
$this->delete_ticket_attachment($attachment['id'], $playground);
}
}
return true;
}
return false;
}
/**
* Remove ticket attachment by id
* @param mixed $id attachment id
* @return boolean
*/
public function delete_ticket_attachment($id, $playground = false) {
$deleted = false;
$this->db->where('id', $id);
$attachment = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_attachments')->row();
$this->load->model('misc_model');
if ($attachment) {
if (unlink($this->misc_model->get_upload_path_by_type('ticket', $playground) . $attachment->ticketid . '/' . $attachment->file_name)) {
$this->db->where('id', $attachment->id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_attachments');
$deleted = true;
}
// Check if no attachments left, so we can delete the folder also
$other_attachments = list_files($this->misc_model->get_upload_path_by_type('ticket', $playground) . $attachment->ticketid);
if (count($other_attachments) == 0) {
delete_dir($this->misc_model->get_upload_path_by_type('ticket', $playground) . $attachment->ticketid);
}
}
return $deleted;
}
/**
* Get ticket attachment by id
* @param mixed $id attachment id
* @return mixed
*/
public function get_ticket_attachment($id, $playground = false) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_attachments')->row();
}
/**
* This functions is used when staff open client ticket
* @param mixed $userid client id
* @param mixed $id ticketid
* @return array
*/
public function get_user_other_tickets($userid, $id, $playground = false) {
$this->db->select(db_prefix() . ($playground ? 'playground_' : '') . 'departments.name as department_name, ' . db_prefix() . ($playground ? 'playground_' : '') . 'services.name as service_name,' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status.name as status_name,' . db_prefix() . ($playground ? 'playground_' : '') . 'staff.firstname as staff_firstname, ' . db_prefix() . ($playground ? 'playground_' : '') . 'clients.lastname as staff_lastname,ticketid,subject,firstname,lastname,lastreply');
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'tickets');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'departments', db_prefix() . ($playground ? 'playground_' : '') . 'departments.departmentid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.department', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status', db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status.ticketstatusid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.status', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'services', db_prefix() . ($playground ? 'playground_' : '') . 'services.serviceid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.service', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'clients', db_prefix() . ($playground ? 'playground_' : '') . 'clients.userid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'staff', db_prefix() . ($playground ? 'playground_' : '') . 'staff.staffid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.admin', 'left');
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid', $userid);
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.ticketid !=', $id);
$tickets = $this->db->get()->result_array();
$i = 0;
foreach ($tickets as $ticket) {
$tickets[$i]['submitter'] = $ticket['firstname'] . ' ' . $ticket['lastname'];
unset($ticket['firstname']);
unset($ticket['lastname']);
$i++;
}
return $tickets;
}
/**
* Get all ticket replies
* @param mixed $id ticketid
* @param mixed $userid specific client id
* @return array
*/
public function get_ticket_replies($id, $playground = false) {
$ticket_replies_order = get_option('ticket_replies_order');
// backward compatibility for the action hook
$ticket_replies_order = hooks()->apply_filters('ticket_replies_order', $ticket_replies_order);
$this->db->select(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies.id,' . db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies.name as from_name,' . db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies.email as reply_email, ' . db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies.admin, ' . db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies.userid,' . db_prefix() . ($playground ? 'playground_' : '') . 'staff.firstname as staff_firstname, ' . db_prefix() . ($playground ? 'playground_' : '') . 'staff.lastname as staff_lastname,' . db_prefix() . ($playground ? 'playground_' : '') . 'contacts.firstname as user_firstname,' . db_prefix() . ($playground ? 'playground_' : '') . 'contacts.lastname as user_lastname,message,date,contactid');
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'clients', db_prefix() . ($playground ? 'playground_' : '') . 'clients.userid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies.userid', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'staff', db_prefix() . ($playground ? 'playground_' : '') . 'staff.staffid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies.admin', 'left');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'contacts', db_prefix() . ($playground ? 'playground_' : '') . 'contacts.id = ' . db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies.contactid', 'left');
$this->db->where('ticketid', $id);
$this->db->order_by('date', $ticket_replies_order);
$replies = $this->db->get()->result_array();
$i = 0;
foreach ($replies as $reply) {
if ($reply['admin'] !== null || $reply['admin'] != 0) {
// staff reply
$replies[$i]['submitter'] = $reply['staff_firstname'] . ' ' . $reply['staff_lastname'];
} else {
if ($reply['contactid'] != 0) {
$replies[$i]['submitter'] = $reply['user_firstname'] . ' ' . $reply['user_lastname'];
} else {
$replies[$i]['submitter'] = $reply['from_name'];
}
}
unset($replies[$i]['staff_firstname']);
unset($replies[$i]['staff_lastname']);
unset($replies[$i]['user_firstname']);
unset($replies[$i]['user_lastname']);
$replies[$i]['attachments'] = $this->get_ticket_attachments($id, $reply['id'], $playground);
$i++;
}
return $replies;
}
/**
* Add new ticket to database
* @param mixed $data ticket $_POST data
* @param mixed $admin If admin adding the ticket passed staff id
*/
public function add($data, $admin = null, $pipe_attachments = false, $playground = false) {
if ($admin !== null) {
$data['admin'] = $admin;
unset($data['ticket_client_search']);
}
if (isset($data['assigned']) && $data['assigned'] == '') {
$data['assigned'] = 0;
}
if (isset($data['project_id']) && $data['project_id'] == '') {
$data['project_id'] = 0;
}
if ($admin == null) {
if (isset($data['email'])) {
$data['userid'] = 0;
$data['contactid'] = 0;
} else {
// Opened from customer portal otherwise is passed from pipe or admin area
if (!isset($data['userid']) && !isset($data['contactid'])) {
$data['userid'] = get_client_user_id();
$data['contactid'] = get_contact_user_id();
}
}
$data['status'] = 1;
}
if (isset($data['custom_fields'])) {
$custom_fields = $data['custom_fields'];
unset($data['custom_fields']);
}
// CC is only from admin area
$cc = '';
if (isset($data['cc'])) {
$cc = $data['cc'];
unset($data['cc']);
}
$data['date'] = date('Y-m-d H:i:s');
$data['ticketkey'] = app_generate_hash();
$data['status'] = 1;
$data['message'] = trim($data['message']);
$data['subject'] = trim($data['subject']);
// if ($this->piping == true) {
// $data['message'] = preg_replace('/\v+/u', '<br>', $data['message']);
// }
$is_html_stripped = $this->piping === true;
// Admin can have html
if (!$is_html_stripped && $admin == null && hooks()->apply_filters('ticket_message_without_html_for_non_admin', true)) {
$data['message'] = _strip_tags($data['message']);
$data['subject'] = _strip_tags($data['subject']);
$data['message'] = nl2br_save_html($data['message']);
}
if (!isset($data['userid'])) {
$data['userid'] = 0;
}
if (isset($data['priority']) && $data['priority'] == '' || !isset($data['priority'])) {
$data['priority'] = 0;
}
$tags = '';
if (isset($data['tags'])) {
$tags = $data['tags'];
unset($data['tags']);
}
// $data['message'] = remove_emojis($data['message']);
$data = hooks()->apply_filters('before_ticket_created', $data, $admin);
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', $data);
$ticketid = $this->db->insert_id();
if ($ticketid) {
$this->load->model('misc_model');
$this->misc_model->handle_tags_save($tags, $ticketid, 'ticket');
if (isset($custom_fields)) {
$this->load->model('custom_fields_model');
$this->custom_fields_model->handle_custom_fields_post($ticketid, $custom_fields, $playground);
}
if (isset($data['assigned']) && $data['assigned'] != 0) {
if ($data['assigned'] != get_staff_user_id()) {
$notified = add_notification(['description' => 'not_ticket_assigned_to_you', 'touserid' => $data['assigned'], 'fromcompany' => 1, 'fromuserid' => 0, 'link' => 'tickets/ticket/' . $ticketid, 'additional_data' => serialize([$data['subject'], ]), ]);
if ($notified) {
pusher_trigger_notification([$data['assigned']]);
}
send_mail_template('ticket_assigned_to_staff', get_staff($data['assigned'])->email, $data['assigned'], $ticketid, $data['userid'], $data['contactid']);
}
}
if ($pipe_attachments != false) {
$this->process_pipe_attachments($pipe_attachments, $ticketid, $playground);
} else {
$attachments = $this->handle_ticket_attachments($ticketid, 'attachments', $playground);
if ($attachments) {
$this->insert_ticket_attachments_to_database($attachments, $ticketid, false, $playground);
}
}
$_attachments = $this->get_ticket_attachments($ticketid, '', $playground);
$isContact = false;
if (isset($data['userid']) && $data['userid'] != false) {
$this->load->model('cilents_model');
$email = $this->clients_model->get_contact($data['contactid'], ['active' => 1], [], $playground)->email;
$isContact = true;
} else {
$email = $data['email'];
}
$template = 'ticket_created_to_customer';
if ($admin == null) {
$template = 'ticket_autoresponse';
$notifiedUsers = [];
$staffToNotify = $this->getStaffMembersForTicketNotification($data['department'], $data['assigned'] ?? 0, $playground);
foreach ($staffToNotify as $member) {
send_mail_template('ticket_created_to_staff', $ticketid, $data['userid'], $data['contactid'], $member, $_attachments);
if (get_option('receive_notification_on_new_ticket') == 1) {
$notified = add_notification(['description' => 'not_new_ticket_created', 'touserid' => $member['staffid'], 'fromcompany' => 1, 'fromuserid' => 0, 'link' => 'tickets/ticket/' . $ticketid, 'additional_data' => serialize([$data['subject'], ]), ]);
if ($notified) {
$notifiedUsers[] = $member['staffid'];
}
}
}
pusher_trigger_notification($notifiedUsers);
} else {
if ($cc) {
$this->db->where('ticketid', $ticketid);
$this->db->update(($playground ? 'playground_' : '') . 'tickets', ['cc' => is_array($cc) ? implode(',', $cc) : $cc]);
}
}
$sendEmail = true;
if ($isContact && total_rows(db_prefix() . ($playground ? 'playground_' : '') . 'contacts', ['ticket_emails' => 1, 'id' => $data['contactid']]) == 0) {
$sendEmail = false;
}
if ($sendEmail) {
$ticket = $this->get_ticket_by_id($ticketid, '', $playground);
// $admin == null ? [] : $_attachments - Admin opened ticket from admin area add the attachments to the email
send_mail_template($template, $ticket, $email, $admin == null ? [] : $_attachments, $cc);
}
hooks()->do_action('ticket_created', $ticketid);
log_activity('New Ticket Created [ID: ' . $ticketid . ']');
return $ticketid;
}
return false;
}
/**
* Get latest 5 client tickets
* @param integer $limit Optional limit tickets
* @param mixed $userid client id
* @return array
*/
public function get_client_latests_ticket($limit = 5, $userid = '', $playground = false) {
$this->db->select(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid, ticketstatusid, statuscolor, ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status.name as status_name,' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.ticketid, subject, date');
$this->db->from(db_prefix() . ($playground ? 'playground_' : '') . 'tickets');
$this->db->join(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status', db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status.ticketstatusid = ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets.status', 'left');
if (is_numeric($userid)) {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid', $userid);
} else {
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.userid', get_client_user_id());
}
$this->db->limit($limit);
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.merged_ticket_id IS NULL', null, false);
return $this->db->get()->result_array();
}
/**
* Delete ticket from database and all connections
* @param mixed $ticketid ticketid
* @return boolean
*/
public function delete($ticketid, $playground = false) {
$affectedRows = 0;
hooks()->do_action('before_ticket_deleted', $ticketid);
// final delete ticket
$this->db->where('ticketid', $ticketid);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'tickets');
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
$this->load->model('misc_model');
if ($this->db->affected_rows() > 0) {
$affectedRows++;
$this->db->where('merged_ticket_id', $ticketid);
$this->db->set('merged_ticket_id', null);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets');
$this->db->where('ticketid', $ticketid);
$attachments = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_attachments')->result_array();
if (count($attachments) > 0) {
if (is_dir($this->misc_model->get_upload_path_by_type('ticket', $playground) . $ticketid)) {
if (delete_dir($this->misc_model->get_upload_path_by_type('ticket', $playground) . $ticketid)) {
foreach ($attachments as $attachment) {
$this->db->where('id', $attachment['id']);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_attachments');
if ($this->db->affected_rows() > 0) {
$affectedRows++;
}
}
}
}
}
$this->db->where('relid', $ticketid);
$this->db->where('fieldto', 'tickets');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'customfieldsvalues');
// Delete replies
$this->db->where('ticketid', $ticketid);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies');
$this->db->where('rel_id', $ticketid);
$this->db->where('rel_type', 'ticket');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'notes');
$this->db->where('rel_id', $ticketid);
$this->db->where('rel_type', 'ticket');
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'taggables');
$this->db->where('rel_type', 'ticket');
$this->db->where('rel_id', $ticketid);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'reminders');
// Get related tasks
$this->db->where('rel_type', 'ticket');
$this->db->where('rel_id', $ticketid);
$tasks = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tasks')->result_array();
$this->load->model('tasks_model');
foreach ($tasks as $task) {
$this->tasks_model->delete_task($task['id'], true, $playground);
}
}
if ($affectedRows > 0) {
log_activity('Ticket Deleted [ID: ' . $ticketid . ']');
hooks()->do_action('after_ticket_deleted', $ticketid);
return true;
}
return false;
}
/**
* Update ticket data / admin use
* @param mixed $data ticket $_POST data
* @return boolean
*/
public function update_single_ticket_settings($data, $playground = false) {
$affectedRows = 0;
$data = hooks()->apply_filters('before_ticket_settings_updated', $data);
$ticketBeforeUpdate = $this->get_ticket_by_id($data['ticketid'], '', $playground);
if (isset($data['merge_ticket_ids'])) {
$tickets = explode(',', $data['merge_ticket_ids']);
if ($this->merge($data['ticketid'], $ticketBeforeUpdate->status, $tickets)) {
$affectedRows++;
}
unset($data['merge_ticket_ids']);
}
if (isset($data['custom_fields']) && count($data['custom_fields']) > 0) {
$this->load->model('custom_fields_model');
if ($this->custom_fields_model->handle_custom_fields_post($data['ticketid'], $data['custom_fields'])) {
$affectedRows++;
}
unset($data['custom_fields']);
}
$tags = '';
if (isset($data['tags'])) {
$tags = $data['tags'];
unset($data['tags']);
}
$this->load->model('misc_model');
if ($this->misc_model->handle_tags_save($tags, $data['ticketid'], 'ticket')) {
$affectedRows++;
}
if (isset($data['priority']) && $data['priority'] == '' || !isset($data['priority'])) {
$data['priority'] = 0;
}
if ($data['assigned'] == '') {
$data['assigned'] = 0;
}
if (isset($data['project_id']) && $data['project_id'] == '') {
$data['project_id'] = 0;
}
if (isset($data['contactid']) && $data['contactid'] != '') {
$data['name'] = null;
$data['email'] = null;
}
$this->db->where('ticketid', $data['ticketid']);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', $data);
if ($this->db->affected_rows() > 0) {
hooks()->do_action('ticket_settings_updated', ['ticket_id' => $data['ticketid'], 'original_ticket' => $ticketBeforeUpdate, 'data' => $data, ]);
$affectedRows++;
}
$sendAssignedEmail = false;
$current_assigned = $ticketBeforeUpdate->assigned;
if ($current_assigned != 0) {
if ($current_assigned != $data['assigned']) {
if ($data['assigned'] != 0 && $data['assigned'] != get_staff_user_id()) {
$sendAssignedEmail = true;
$notified = add_notification(['description' => 'not_ticket_reassigned_to_you', 'touserid' => $data['assigned'], 'fromcompany' => 1, 'fromuserid' => 0, 'link' => 'tickets/ticket/' . $data['ticketid'], 'additional_data' => serialize([$data['subject'], ]), ]);
if ($notified) {
pusher_trigger_notification([$data['assigned']]);
}
}
}
} else {
if ($data['assigned'] != 0 && $data['assigned'] != get_staff_user_id()) {
$sendAssignedEmail = true;
$notified = add_notification(['description' => 'not_ticket_assigned_to_you', 'touserid' => $data['assigned'], 'fromcompany' => 1, 'fromuserid' => 0, 'link' => 'tickets/ticket/' . $data['ticketid'], 'additional_data' => serialize([$data['subject'], ]), ]);
if ($notified) {
pusher_trigger_notification([$data['assigned']]);
}
}
}
if ($sendAssignedEmail === true) {
$this->db->where('staffid', $data['assigned']);
$assignedEmail = $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'staff')->row()->email;
send_mail_template('ticket_assigned_to_staff', $assignedEmail, $data['assigned'], $data['ticketid'], $data['userid'], $data['contactid']);
}
if ($affectedRows > 0) {
log_activity('Ticket Updated [ID: ' . $data['ticketid'] . ']');
return true;
}
return false;
}
/**
* C<ha></ha>nge ticket status
* @param mixed $id ticketid
* @param mixed $status status id
* @return array
*/
public function change_ticket_status($id, $status, $playground = false) {
$this->db->where('ticketid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', ['status' => $status, ]);
$alert = 'warning';
$message = _l('ticket_status_changed_fail');
if ($this->db->affected_rows() > 0) {
$alert = 'success';
$message = _l('ticket_status_changed_successfully');
hooks()->do_action('after_ticket_status_changed', ['id' => $id, 'status' => $status, ]);
}
return ['alert' => $alert, 'message' => $message, ];
}
// Priorities
/**
* Get ticket priority by id
* @param mixed $id priority id
* @return mixed if id passed return object else array
*/
public function get_priority($id = '', $playground = false) {
if (is_numeric($id)) {
$this->db->where('priorityid', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities')->row();
}
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities')->result_array();
}
/**
* Add new ticket priority
* @param array $data ticket priority data
*/
public function add_priority($data, $playground = false) {
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Ticket Priority Added [ID: ' . $insert_id . ', Name: ' . $data['name'] . ']');
}
return $insert_id;
}
/**
* Update ticket priority
* @param array $data ticket priority $_POST data
* @param mixed $id ticket priority id
* @return boolean
*/
public function update_priority($data, $id, $playground = false) {
$this->db->where('priorityid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Ticket Priority Updated [ID: ' . $id . ' Name: ' . $data['name'] . ']');
return true;
}
return false;
}
/**
* Delete ticket priorit
* @param mixed $id ticket priority id
* @return mixed
*/
public function delete_priority($id, $playground = false) {
$current = $this->get($id, [], $playground);
// Check if the priority id is used in tickets table
if (is_reference_in_table('priority', db_prefix() . ($playground ? 'playground_' : '') . 'tickets', $id)) {
return ['referenced' => true, ];
}
$this->db->where('priorityid', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_priorities');
if ($this->db->affected_rows() > 0) {
if (get_option('email_piping_default_priority') == $id) {
update_option('email_piping_default_priority', '');
}
log_activity('Ticket Priority Deleted [ID: ' . $id . ']');
return true;
}
return false;
}
// Predefined replies
/**
* Get predefined reply by id
* @param mixed $id predefined reply id
* @return mixed if id passed return object else array
*/
public function get_predefined_reply($id = '', $playground = false) {
if (is_numeric($id)) {
$this->db->where('id', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_predefined_replies')->row();
}
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_predefined_replies')->result_array();
}
/**
* Add new predefined reply
* @param array $data predefined reply $_POST data
*/
public function add_predefined_reply($data, $playground = false) {
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_predefined_replies', $data);
$insertid = $this->db->insert_id();
log_activity('New Predefined Reply Added [ID: ' . $insertid . ', ' . $data['name'] . ']');
return $insertid;
}
/**
* Update predefined reply
* @param array $data predefined $_POST data
* @param mixed $id predefined reply id
* @return boolean
*/
public function update_predefined_reply($data, $id, $playground = false) {
$this->db->where('id', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_predefined_replies', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Predefined Reply Updated [ID: ' . $id . ', ' . $data['name'] . ']');
return true;
}
return false;
}
/**
* Delete predefined reply
* @param mixed $id predefined reply id
* @return boolean
*/
public function delete_predefined_reply($id, $playground = false) {
$this->db->where('id', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_predefined_replies');
if ($this->db->affected_rows() > 0) {
log_activity('Predefined Reply Deleted [' . $id . ']');
return true;
}
return false;
}
// Ticket statuses
/**
* Get ticket status by id
* @param mixed $id status id
* @return mixed if id passed return object else array
*/
public function get_ticket_status($id = '', $playground = false) {
if (is_numeric($id)) {
$this->db->where('ticketstatusid', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status')->row();
}
$this->db->order_by('statusorder', 'asc');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status')->result_array();
}
/**
* Add new ticket status
* @param array ticket status $_POST data
* @return mixed
*/
public function add_ticket_status($data, $playground = false) {
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Ticket Status Added [ID: ' . $insert_id . ', ' . $data['name'] . ']');
return $insert_id;
}
return false;
}
/**
* Update ticket status
* @param array $data ticket status $_POST data
* @param mixed $id ticket status id
* @return boolean
*/
public function update_ticket_status($data, $id, $playground = false) {
$this->db->where('ticketstatusid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Ticket Status Updated [ID: ' . $id . ' Name: ' . $data['name'] . ']');
return true;
}
return false;
}
/**
* Delete ticket status
* @param mixed $id ticket status id
* @return mixed
*/
public function delete_ticket_status($id, $playground = false) {
$current = $this->get_ticket_status($id, $playground);
// Default statuses cant be deleted
if ($current->isdefault == 1) {
return ['default' => true, ];
// Not default check if if used in table
} else if (is_reference_in_table('status', db_prefix() . ($playground ? 'playground_' : '') . 'tickets', $id)) {
return ['referenced' => true, ];
}
$this->db->where('ticketstatusid', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'tickets_status');
if ($this->db->affected_rows() > 0) {
log_activity('Ticket Status Deleted [ID: ' . $id . ']');
return true;
}
return false;
}
// Ticket services
public function get_service($id = '', $playground = false) {
if (is_numeric($id)) {
$this->db->where('serviceid', $id);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'services')->row();
}
$this->db->order_by('name', 'asc');
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'services')->result_array();
}
public function add_service($data, $playground = false) {
$this->db->insert(db_prefix() . ($playground ? 'playground_' : '') . 'services', $data);
$insert_id = $this->db->insert_id();
if ($insert_id) {
log_activity('New Ticket Service Added [ID: ' . $insert_id . '.' . $data['name'] . ']');
}
return $insert_id;
}
public function update_service($data, $id, $playground = false) {
$this->db->where('serviceid', $id);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'services', $data);
if ($this->db->affected_rows() > 0) {
log_activity('Ticket Service Updated [ID: ' . $id . ' Name: ' . $data['name'] . ']');
return true;
}
return false;
}
public function delete_service($id, $playground = false) {
if (is_reference_in_table('service', db_prefix() . ($playground ? 'playground_' : '') . 'tickets', $id)) {
return ['referenced' => true, ];
}
$this->db->where('serviceid', $id);
$this->db->delete(db_prefix() . ($playground ? 'playground_' : '') . 'services');
if ($this->db->affected_rows() > 0) {
log_activity('Ticket Service Deleted [ID: ' . $id . ']');
return true;
}
return false;
}
/**
* @return array
* Used in home dashboard page
* Displays weekly ticket openings statistics (chart)
*/
public function get_weekly_tickets_opening_statistics($playground = false) {
$departments_ids = [];
if (!is_admin()) {
if (get_option('staff_access_only_assigned_departments') == 1) {
$this->load->model('departments_model');
$staff_deparments_ids = $this->departments_model->get_staff_departments(get_staff_user_id(), true, $playground);
$departments_ids = [];
if (count($staff_deparments_ids) == 0) {
$departments = $this->departments_model->get($playground);
foreach ($departments as $department) {
array_push($departments_ids, $department['departmentid']);
}
} else {
$departments_ids = $staff_deparments_ids;
}
}
}
$chart = ['labels' => get_weekdays(), 'datasets' => [['label' => _l('home_weekend_ticket_opening_statistics'), 'backgroundColor' => 'rgba(197, 61, 169, 0.5)', 'borderColor' => '#c53da9', 'borderWidth' => 1, 'tension' => false, 'data' => [0, 0, 0, 0, 0, 0, 0, ], ], ], ];
$monday = new DateTime(date('Y-m-d', strtotime('monday this week')));
$sunday = new DateTime(date('Y-m-d', strtotime('sunday this week')));
$thisWeekDays = get_weekdays_between_dates($monday, $sunday);
$byDepartments = count($departments_ids) > 0;
if (isset($thisWeekDays[1])) {
$i = 0;
foreach ($thisWeekDays[1] as $weekDate) {
$this->db->like('DATE(date)', $weekDate, 'after');
$this->db->where(db_prefix() . ($playground ? 'playground_' : '') . 'tickets.merged_ticket_id IS NULL', null, false);
if ($byDepartments) {
$this->db->where('department IN (SELECT departmentid FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'staff_departments WHERE departmentid IN (' . implode(',', $departments_ids) . ') AND staffid="' . get_staff_user_id() . '")');
}
$chart['datasets'][0]['data'][$i] = $this->db->count_all_results(db_prefix() . ($playground ? 'playground_' : '') . 'tickets');
$i++;
}
}
return $chart;
}
public function get_tickets_assignes_disctinct($playground = false) {
return $this->db->query('SELECT DISTINCT(assigned) as assigned FROM ' . db_prefix() . ($playground ? 'playground_' : '') . 'tickets WHERE assigned != 0 AND merged_ticket_id IS NULL')->result_array();
}
/**
* Check for previous tickets opened by this email/contact and link to the contact
* @param string $email email to check for
* @param mixed $contact_id the contact id to transfer the tickets
* @return boolean
*/
public function transfer_email_tickets_to_contact($email, $contact_id, $playground = false) {
// Some users don't want to fill the email
if (empty($email)) {
return false;
}
$this->load->model('cilents_model');
$customer_id = $this->clients_model->get_user_id_by_contact_id($contact_id, $playground);
$this->db->where('userid', 0)->where('contactid', 0)->where('admin IS NULL')->where('email', $email);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets', ['email' => null, 'name' => null, 'userid' => $customer_id, 'contactid' => $contact_id, ]);
$this->db->where('userid', 0)->where('contactid', 0)->where('admin IS NULL')->where('email', $email);
$this->db->update(db_prefix() . ($playground ? 'playground_' : '') . 'ticket_replies', ['email' => null, 'name' => null, 'userid' => $customer_id, 'contactid' => $contact_id, ]);
return true;
}
/**
* Check whether the given ticketid is already merged into another primary ticket
*
* @param int $id
*
* @return boolean
*/
public function is_merged($id, $playground = false) {
return total_rows(($playground ? 'playground_' : '') . 'tickets', "ticketid={$id} and merged_ticket_id IS NOT NULL") > 0;
}
/**
* @param $primary_ticket_id
* @param $status
* @param array $ids
*
* @return bool
*/
public function merge($primary_ticket_id, $status, array $ids, $playground = false) {
if ($this->is_merged($primary_ticket_id, $playground)) {
return false;
}
if (($index = array_search($primary_ticket_id, $ids)) !== false) {
unset($ids[$index]);
}
if (count($ids) == 0) {
return false;
}
return (new MergeTickets($primary_ticket_id, $ids))->markPrimaryTicketAs($status)->merge();
}
/**
* @param array $tickets id's of tickets to check
* @return array
*/
public function get_already_merged_tickets($tickets, $playground = false) {
if (count($tickets) === 0) {
return [];
}
$alreadyMerged = [];
foreach ($tickets as $ticketId) {
if ($this->is_merged((int)$ticketId, $playground)) {
$alreadyMerged[] = $ticketId;
}
}
return $alreadyMerged;
}
/**
* @param $primaryTicketId
* @return array
*/
public function get_merged_tickets_by_primary_id($primaryTicketId, $playground = false) {
return $this->db->where('merged_ticket_id', $primaryTicketId)->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets')->result_array();
}
public function update_staff_replying($ticketId, $userId = '', $playground = false) {
$ticket = $this->get($ticketId, [], $playground);
if ($userId === '') {
return $this->db->where('ticketid', $ticketId)->set('staff_id_replying', null)->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets');
}
if ($ticket->staff_id_replying !== $userId && !is_null($ticket->staff_id_replying)) {
return false;
}
if ($ticket->staff_id_replying === $userId) {
return true;
}
return $this->db->where('ticketid', $ticketId)->set('staff_id_replying', $userId)->update(db_prefix() . ($playground ? 'playground_' : '') . 'tickets');
}
public function get_staff_replying($ticketId, $playground = false) {
$this->db->select('ticketid,staff_id_replying');
$this->db->where('ticketid', $ticketId);
return $this->db->get(db_prefix() . ($playground ? 'playground_' : '') . 'tickets')->row();
}
private function getStaffMembersForTicketNotification($department, $assignedStaff = 0, $playground = false) {
$this->load->model('departments_model');
$this->load->model('staff_model');
$staffToNotify = [];
if ($assignedStaff != 0 && get_option('staff_related_ticket_notification_to_assignee_only') == 1) {
$member = $this->staff_model->get($assignedStaff, ['active' => 1], $playground);
if ($member) {
$staffToNotify[] = (array)$member;
}
} else {
$staff = $this->staff_model->get('', ['active' => 1], $playground);
foreach ($staff as $member) {
if (get_option('access_tickets_to_none_staff_members') == 0 && !is_staff_member($member['staffid'])) {
continue;
}
$staff_departments = $this->departments_model->get_staff_departments($member['staffid'], true, $playground);
if (in_array($department, $staff_departments)) {
$staffToNotify[] = $member;
}
}
}
return $staffToNotify;
}
/**
* Check for ticket attachment after inserting ticket to database
* @param mixed $ticketid
* @return mixed false if no attachment || array uploaded attachments
*/
public function handle_ticket_attachments($ticketid, $index_name = 'attachments', $playground = false) {
$hookData = hooks()->apply_filters('before_handle_ticket_attachment', [
'ticket_id' => $ticketid,
'index_name' => $index_name,
'uploaded_files' => [],
'handled_externally' => false, // e.g. module upload to s3
'files' => $_FILES
]);
if ($hookData['handled_externally']) {
return count($hookData['uploaded_files']) > 0 ? $hookData['uploaded_files'] : false;
}
$this->load->model('misc_model');
$path = $this->misc_model->get_upload_path_by_type('ticket', $playground) . $ticketid . '/';
$uploaded_files = [];
if (isset($_FILES[$index_name])) {
_file_attachments_index_fix($index_name);
for ($i = 0; $i < count($_FILES[$index_name]['name']); $i++) {
hooks()->do_action('before_upload_ticket_attachment', $ticketid);
if ($i <= get_option('maximum_allowed_ticket_attachments')) {
// Get the temp file path
$tmpFilePath = $_FILES[$index_name]['tmp_name'][$i];
// Make sure we have a filepath
if (!empty($tmpFilePath) && $tmpFilePath != '') {
// Getting file extension
$extension = strtolower(pathinfo($_FILES[$index_name]['name'][$i], PATHINFO_EXTENSION));
$allowed_extensions = explode(',', get_option('ticket_attachments_file_extensions'));
$allowed_extensions = array_map('trim', $allowed_extensions);
// Check for all cases if this extension is allowed
if (!in_array('.' . $extension, $allowed_extensions)) {
continue;
}
_maybe_create_upload_path($path);
$filename = unique_filename($path, $_FILES[$index_name]['name'][$i]);
$newFilePath = $path . $filename;
// Upload the file into the temp dir
if (move_uploaded_file($tmpFilePath, $newFilePath)) {
array_push($uploaded_files, ['file_name' => $filename, 'filetype' => $_FILES[$index_name]['type'][$i], ]);
}
}
}
}
}
if (count($uploaded_files) > 0) {
return $uploaded_files;
}
return false;
}
}