". The template, or null.
*/
function ticketTemplateFinish($ticket_id, $ticket_template_id, $defaults = false, $tasks = false, $answers = null, $history = null) {
global $mysqli, $session_name;
$ticket_id = intval($ticket_id);
$template = ticketTemplateRow($ticket_template_id);
if (!$ticket_id || !$template) {
return null;
}
$ticket_template_id = intval($template['ticket_template_id']);
mysqli_query($mysqli, "UPDATE tickets SET ticket_template_id = $ticket_template_id WHERE ticket_id = $ticket_id");
if ($answers === null) {
$answers = ticketFillInAnswers(ticketTemplateFillInsFor($ticket_template_id), [], $error, false) ?? [];
}
if ($answers) {
ticketFillInsStore('ticket', $ticket_id, $answers);
}
if ($tasks) {
ticketTemplateAddTasks($ticket_id, $ticket_template_id, $answers);
}
if ($defaults) {
$tag_ids = ticketTemplateTagIds($ticket_template_id);
if ($tag_ids && !tagIdsForRecord('ticket', $ticket_id)) {
tagSetForRecord('ticket', $ticket_id, array_map('strval', $tag_ids));
}
$due = ticketTemplateDueAt($template['ticket_template_due_hours']);
if ($due) {
mysqli_query($mysqli, "UPDATE tickets SET ticket_due_at = '$due' WHERE ticket_id = $ticket_id AND ticket_due_at IS NULL");
}
}
ticketPlaceholdersFill($ticket_id);
logTicketHistory($ticket_id, escapeSql($history ?? stripslashes((string) ($session_name ?? 'ITFlow')) . ' raised the ticket from template ' . $template['ticket_template_name']));
return $template;
}
/* ---------------------------------------------------------------- client portal */
/** The icons a request form can have in the portal - fa- name => what it suits. */
const TICKET_TEMPLATE_PORTAL_ICONS = [
'clipboard-list' => 'A request', 'user-plus' => 'Someone new', 'user-minus' => 'Someone leaving', 'user-edit' => 'A change for someone',
'key' => 'Access or a password', 'laptop' => 'A laptop', 'desktop' => 'A computer', 'mobile-alt' => 'A mobile phone', 'phone' => 'A desk phone',
'print' => 'A printer', 'envelope' => 'Email', 'wifi' => 'Wi-Fi or the network', 'shield-alt' => 'Security', 'folder-open' => 'Files and shares',
'cube' => 'Software', 'shopping-cart' => 'Something to buy', 'video' => 'Meetings', 'building' => 'An office', 'truck' => 'A move',
'calendar-alt' => 'Something to plan', 'cloud' => 'Cloud', 'server' => 'A server', 'tools' => 'A repair', 'question-circle' => 'A question',
];
/** Who may use a request form in the portal. */
const TICKET_TEMPLATE_PORTAL_WHO = [
'everyone' => 'Everyone with portal access',
'managers' => 'Primary and technical contacts only',
];
/** What a template is called in the portal - its portal title, or its name. Raw. */
function ticketTemplatePortalTitle(array $row) {
$title = trim((string) ($row['ticket_template_portal_title'] ?? ''));
return $title !== '' ? $title : (string) $row['ticket_template_name'];
}
/** A request form's icon - fa-user-plus; fa-clipboard-list when it has none. */
function ticketTemplatePortalIcon(array $row) {
$icon = (string) ($row['ticket_template_portal_icon'] ?? '');
return 'fa-' . (isset(TICKET_TEMPLATE_PORTAL_ICONS[$icon]) ? $icon : 'clipboard-list');
}
/*
* The request forms a contact may use - templates offered in the portal and
* not archived, for every client or theirs among the ones picked, and for
* everyone or, when $manager (a primary or technical contact), for them
* too. Rows, by title.
*/
function ticketTemplatePortalForms($client_id, $manager) {
global $mysqli;
$client_id = intval($client_id);
$forms = [];
$sql = mysqli_query($mysqli, "SELECT * FROM ticket_templates
WHERE ticket_template_portal = 1 AND ticket_template_archived_at IS NULL
AND (ticket_template_portal_all_clients = 1 OR EXISTS (SELECT 1 FROM ticket_template_portal_clients p WHERE p.ticket_template_id = ticket_templates.ticket_template_id AND p.client_id = $client_id))
" . ($manager ? '' : "AND ticket_template_portal_who = 'everyone'"));
while ($row = mysqli_fetch_assoc($sql)) {
$forms[] = $row;
}
usort($forms, function ($a, $b) {
return strcasecmp(ticketTemplatePortalTitle($a), ticketTemplatePortalTitle($b));
});
return $forms;
}
/** One request form a contact may use (ticketTemplatePortalForms()), or null. */
function ticketTemplatePortalForm($ticket_template_id, $client_id, $manager) {
foreach (ticketTemplatePortalForms($client_id, $manager) as $row) {
if (intval($row['ticket_template_id']) === intval($ticket_template_id)) {
return $row;
}
}
return null;
}
/** Whether a template's subject, details or tasks have [Asset] in them - its form then asks which device. */
function ticketTemplateMentionsAsset(array $row, array $tasks) {
return (bool) preg_match('/\[asset\]/i', strip_tags($row['ticket_template_subject'] . ' ' . $row['ticket_template_details'] . ' ' . implode(' ', array_column($tasks, 'name'))));
}
/*
* The answers to a request form, checked: the fill-ins the client is asked
* (required ones must be answered), and the agents' ones left unanswered -
* every one, in its order. Null with $error saying what is wrong.
*/
function ticketTemplatePortalAnswers(array $fill_ins, $posted, &$error = '') {
$asked = array_values(array_filter($fill_ins, function ($fill_in) {
return !empty($fill_in['client']);
}));
$given = ticketFillInAnswers($asked, $posted, $error);
if ($given === null) {
return null;
}
$by_name = [];
foreach ($given as $answer) {
$by_name[strtolower($answer['name'])] = $answer;
}
$answers = [];
foreach ($fill_ins as $fill_in) {
$answers[] = $by_name[strtolower($fill_in['name'])]
?? ['name' => $fill_in['name'], 'type' => $fill_in['type'], 'options' => $fill_in['options'], 'required' => !empty($fill_in['required']), 'client' => false, 'value' => ''];
}
return $answers;
}
/*
* Raise a ticket from a request form in the client portal, for the contact
* signed in: set up as the template says (category, priority, billing, agent,
* due time, tags, the tasks for these answers), with the answers kept and
* filled in, and anything else the client wrote ($notes, plain text) first
* in its details. The agent it goes to hears about it. The new ticket's id, or 0.
*/
function ticketTemplatePortalRaise(array $template, $client_id, $contact_id, $user_id, $contact_name, array $answers, $notes = '', $asset_id = 0) {
global $mysqli, $config_ticket_prefix, $config_ticket_default_billable;
$client_id = intval($client_id);
$contact_id = intval($contact_id);
$ticket_template_id = intval($template['ticket_template_id']);
$subject = trim((string) $template['ticket_template_subject']) !== '' ? (string) $template['ticket_template_subject'] : ticketTemplatePortalTitle($template);
$details = (string) $template['ticket_template_details'];
$notes = trim((string) $notes);
if ($notes !== '') {
$details = '' . escapeHtml($contact_name) . ' added:
' . nl2br(escapeHtml($notes)) . '
' . $details;
}
$priority = escapeSql(ticketPriority($template['ticket_template_priority'] ?? ''));
$category_id = ticketCategoryId($template['ticket_template_category_id']);
$billable = $template['ticket_template_billable'] !== null ? intval($template['ticket_template_billable']) : (intval($config_ticket_default_billable) === 1 ? 1 : 0);
$assigned_to = ticketAgentId($template['ticket_template_assigned_to']);
$status = $assigned_to ? TICKET_STATUS_OPEN : TICKET_STATUS_NEW;
$asset_id = intval($asset_id);
$prefix = escapeSql($config_ticket_prefix);
$url_key = randomString(32);
mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = LAST_INSERT_ID(config_ticket_next_number), config_ticket_next_number = config_ticket_next_number + 1 WHERE company_id = 1");
$ticket_number = mysqli_insert_id($mysqli);
mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$prefix', ticket_number = $ticket_number, ticket_source = 'Portal', ticket_category = $category_id,
ticket_subject = '" . escapeSql(mb_substr($subject, 0, 500)) . "', ticket_details = '" . mysqli_real_escape_string($mysqli, $details) . "', ticket_priority = '$priority',
ticket_status = $status, ticket_billable = $billable, ticket_created_by = " . intval($user_id) . ", ticket_assigned_to = $assigned_to, ticket_contact_id = $contact_id,
ticket_asset_id = $asset_id, ticket_url_key = '$url_key', ticket_client_id = $client_id");
$ticket_id = intval(mysqli_insert_id($mysqli));
if (!$ticket_id) {
return 0;
}
applyTicketSla($ticket_id);
ticketTemplateFinish($ticket_id, $ticket_template_id, true, true, $answers, "$contact_name asked for " . ticketTemplatePortalTitle($template) . " in the client portal (template " . $template['ticket_template_name'] . ")");
// The agent it goes to hears about it
if ($assigned_to) {
$row = ticketRow($ticket_id);
mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = '" . escapeSql("Ticket " . ticketReference($row) . " - " . $row['ticket_subject'] . " has been assigned to you from the client portal") . "',
notification_action = '" . escapeSql('/agent/' . ticketUrl($ticket_id, $client_id)) . "', notification_client_id = $client_id, notification_user_id = $assigned_to");
ticketMailAssigned($assigned_to, [$ticket_id], $contact_name);
}
return $ticket_id;
}
/** Tickets raised from a template's request form in the portal - [template id => how many]. */
function ticketTemplatePortalRequests(array $ticket_template_ids) {
global $mysqli;
$ids = array_values(array_filter(array_map('intval', $ticket_template_ids)));
$counts = [];
if ($ids) {
$sql = mysqli_query($mysqli, "SELECT ticket_template_id, COUNT(*) FROM tickets WHERE ticket_source = 'Portal' AND ticket_archived_at IS NULL AND ticket_template_id IN (" . implode(',', $ids) . ") GROUP BY ticket_template_id");
while ($row = mysqli_fetch_row($sql)) {
$counts[intval($row[0])] = intval($row[1]);
}
}
return $counts;
}