Canned Responses) - making, changing, copying,
* archiving and deleting them, with their files, sharing an agent's own
* with everyone, and exporting and importing them as a file. What is made
* here is shared. The work is in functions/canned_response.php and
* functions/canned_response_transfer.php; an agent's own are made in Your
* account (agent/user/post/canned_response.php).
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
/* The response a request is for - or back to the list, saying it is gone. */
function cannedResponseForRequest($canned_response_id) {
$row = cannedResponseRow($canned_response_id);
if (!$row) {
flashAlert('That canned response no longer exists.', 'error');
redirect('canned_responses.php');
}
return $row;
}
if (isset($_POST['add_canned_response']) || isset($_POST['edit_canned_response'])) {
validateCSRFToken();
$editing = isset($_POST['edit_canned_response']);
$canned_response_id = $editing ? intval(cannedResponseForRequest($_POST['canned_response_id'] ?? 0)['canned_response_id']) : 0;
$fields = cannedResponseFields($_POST, $error);
if (!$fields) {
flashAlert(escapeHtml($error), 'error');
redirect();
}
$canned_response_id = cannedResponseSave($canned_response_id, $fields);
$name = escapeSql($fields['name']);
// Its files - the ones ticked off go, the new ones are kept
cannedResponseRemoveFiles($canned_response_id, (array) ($_POST['remove_file_ids'] ?? []));
$refused = cannedResponseSaveFiles($canned_response_id);
logAudit("Canned Response", $editing ? "Edit" : "Create", "$session_name " . ($editing ? 'edited' : 'created') . " canned response $name", 0, $canned_response_id);
flashAlert("Canned response " . escapeHtml($fields['name']) . " " . ($editing ? 'saved' : 'created')
. ($refused ? " - but not " . escapeHtml(implode(', ', $refused)) . ", a kind a ticket doesn't take or more than " . CANNED_RESPONSE_MAX_FILES . " files" : ''), $refused ? 'error' : 'success');
redirect();
}
if (isset($_GET['copy_canned_response'])) {
validateCSRFToken();
// A copy made here is shared, an agent's own copied too
$row = cannedResponseForRequest($_GET['copy_canned_response']);
$new_id = cannedResponseCopy($row['canned_response_id'], 0);
$new_name = (string) cannedResponseRow($new_id)['canned_response_name'];
logAudit("Canned Response", "Create", "$session_name copied canned response " . escapeSql($row['canned_response_name']) . " as " . escapeSql($new_name), 0, $new_id);
flashAlert("Canned response " . escapeHtml($row['canned_response_name']) . " copied as " . escapeHtml($new_name) . "");
redirect();
}
if (isset($_GET['archive_canned_response']) || isset($_GET['restore_canned_response'])) {
validateCSRFToken();
$archive = isset($_GET['archive_canned_response']);
$row = cannedResponseForRequest($archive ? $_GET['archive_canned_response'] : $_GET['restore_canned_response']);
$canned_response_id = intval($row['canned_response_id']);
$name = escapeSql($row['canned_response_name']);
// Archived, it isn't offered on tickets; restoring it is all it takes to offer it again
mysqli_query($mysqli, "UPDATE canned_responses SET canned_response_archived_at = " . ($archive ? 'NOW()' : 'NULL') . " WHERE canned_response_id = $canned_response_id");
logAudit("Canned Response", $archive ? "Archive" : "Restore", "$session_name " . ($archive ? 'archived' : 'restored') . " canned response $name", 0, $canned_response_id);
flashAlert("Canned response " . escapeHtml($row['canned_response_name']) . " " . ($archive ? 'archived' : 'restored'), $archive ? 'error' : 'success');
redirect();
}
if (isset($_GET['delete_canned_response'])) {
validateCSRFToken();
$row = cannedResponseForRequest($_GET['delete_canned_response']);
$canned_response_id = intval($row['canned_response_id']);
// Its categories, files and record of use go with it, by their foreign keys, and its folder of files; replies it went into stay as they are
mysqli_query($mysqli, "DELETE FROM canned_responses WHERE canned_response_id = $canned_response_id");
cannedResponseDeleteFiles($canned_response_id);
logAudit("Canned Response", "Delete", "$session_name deleted canned response " . escapeSql($row['canned_response_name']));
flashAlert("Canned response " . escapeHtml($row['canned_response_name']) . " deleted", 'error');
redirect();
}
if (isset($_GET['share_canned_response'])) {
validateCSRFToken();
// An agent's own, offered to everyone from now on - it becomes a shared one, made here
$row = cannedResponseForRequest($_GET['share_canned_response']);
$owner = intval($row['canned_response_user_id']);
if ($owner) {
$owner_name = (string) getFieldById('users', $owner, 'user_name');
mysqli_query($mysqli, "UPDATE canned_responses SET canned_response_user_id = 0 WHERE canned_response_id = " . intval($row['canned_response_id']));
logAudit("Canned Response", "Edit", "$session_name shared " . escapeSql(($owner_name !== '' ? "$owner_name's" : "a removed agent's") . " canned response " . $row['canned_response_name']) . " with everyone", 0, intval($row['canned_response_id']));
flashAlert("Canned response " . escapeHtml($row['canned_response_name']) . " is shared with everyone now");
}
redirect();
}
if (isset($_POST['export_canned_responses']) || isset($_GET['export_canned_response'])) {
// A file of responses to import into another ITFlow - every current shared one, the ones picked, or one
validateCSRFToken();
if (isset($_GET['export_canned_response'])) {
$row = cannedResponseForRequest($_GET['export_canned_response']);
$export = cannedResponsesExport([intval($row['canned_response_id'])]);
} else {
$export = cannedResponsesExport((array) ($_POST['canned_response_ids'] ?? []), !empty($_POST['archived']), !empty($_POST['files']));
}
if (!$export['responses']) {
flashAlert('There are no canned responses to export.', 'error');
redirect();
}
$count = count($export['responses']);
logAudit("Canned Response", "Export", "$session_name exported $count canned response" . ($count === 1 ? ' ' . escapeSql($export['responses'][0]['name']) : 's'));
header('Content-Type: application/json; charset=utf-8');
header('Content-Disposition: attachment; filename="' . cannedResponsesExportFileName($export) . '"');
header('Cache-Control: no-store');
echo json_encode($export, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
exit;
}
if (isset($_POST['import_canned_responses'])) {
// Responses from a file exported by ITFlow, as shared ones
validateCSRFToken();
$file = $_FILES['file'] ?? null;
if ($file && in_array($file['error'], [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE], true)) {
flashAlert('That file is bigger than this server takes in an upload - export them without their files, or a few at a time.', 'error');
redirect('canned_responses.php');
}
if (!$file || $file['error'] !== UPLOAD_ERR_OK || !is_uploaded_file($file['tmp_name'])) {
flashAlert('Pick the file to import.', 'error');
redirect('canned_responses.php');
}
if ($file['size'] > 200 * 1024 * 1024) {
flashAlert('That file is too big to be canned responses.', 'error');
redirect('canned_responses.php');
}
$responses = cannedResponsesImportRead(file_get_contents($file['tmp_name']), $import_error);
if ($responses === null) {
flashAlert(escapeHtml($import_error), 'error');
redirect('canned_responses.php');
}
$existing = in_array($_POST['existing'] ?? '', ['skip', 'copy', 'replace'], true) ? $_POST['existing'] : 'skip';
$counts = cannedResponsesImport($responses, $existing);
logAudit("Canned Response", "Import", "$session_name imported canned responses from " . escapeSql(mb_substr((string) $file['name'], 0, 100)) . " - {$counts['added']} added, {$counts['replaced']} replaced, {$counts['skipped']} skipped");
flashAlert(cannedResponsesImportSummary($counts), $counts['files_refused'] ? 'warning' : ($counts['added'] || $counts['replaced'] ? 'success' : 'info'));
redirect('canned_responses.php');
}