Save To Files Developer Guide - edmozley/freeitsm GitHub Wiki

Save to Files โ€” Developer Guide

5.2.0 ยท #303 (from pappas81) ยท User guide: Files ยง Saving into Files from a ticket ยท Related: Attach from Files, Files versions

๐Ÿ”‘ The one-line version

An email attachment on a ticket, or a stored document on any record, can be copied into a Files folder with Save to Files. One small dialog (assets/js/save-to-files.js) serves both, and one endpoint (api/files/save_from.php) checks the source as downloading it is checked and the folder as uploading to it is. A name that is already there gets the same choices as an upload.

It is the opposite direction to Attach from Files: that puts a Files file on a record; this puts a record's file into Files.

๐Ÿ“ Files

File What
includes/services/files_save.php FilesSave::targets(), saveDefaults(), source(), ticketFolder(), save(); filesStoreLocalFile()
api/files/save_from.php GET ?targets=1; POST {source, id, folder_id, ticket_folder?, on_conflict?}
assets/js/save-to-files.js (v=1), assets/css/save-to-files.css (v=1) window.SaveToFiles.open({source, id, name, ticket})
assets/js/inbox.js (v=201), tickets/index.php A Save to Files column in the ticket's attachment list (not for inline images), and a Save to Files button beside each stored file on a ticket's notes (source: 'document'); window.TICKET_SAVE_TO_FILES
assets/js/documents.js (v=10), api/documents/list.php, includes/documents_panel.php A Save to Files button on each stored document (kind file); can_save_to_files in the list answer
api/files/settings.php, files/settings/index.php, assets/js/files-settings.js (v=8) Sharing tab: Suggested folder and A folder for each ticket
lang/en/common.php (save_to_files.*, documents.save_to_files), lang/en/tickets.php, lang/en/files.php Labels

Settings: files_save_default_folder (a folder id, or empty) and files_save_ticket_folder (1 to start with the ticket-folder box ticked).

๐Ÿ”’ The two checks

// FilesSave::source() - the file, checked AS DOWNLOADING IT IS
// An email attachment: through its ticket (company AND department, 5.1.0) ...
$st = $conn->prepare("SELECT a.filename, a.file_path, e.ticket_id FROM email_attachments a JOIN emails e ON e.id = a.email_id WHERE a.id = ?");
if (!$a || !$a['ticket_id'] || !analystCanAccessTicket($conn, $analystId, (int)$a['ticket_id'])) throw $none;
// ... and the same containment as get_attachment.php: the file must be INSIDE tickets/attachments
$base = realpath(<app>/tickets/attachments); $real = realpath(<app>/tickets/attachments/<file_path>);
if ($base === false || $real === false || strncmp($real, $base . DIRECTORY_SEPARATOR, strlen($base) + 1) !== 0) throw $none;

// A document: documentCanView(), and only kind 'file' - a web link or a Files link has no bytes here
if (!documentCanView($conn, $analystId, $allowed, $id)) throw $none;
// FilesSave::save() - the folder, checked AS UPLOADING TO IT IS
if (FilesAcl::level($conn, $analystId, $folderId) < FilesAcl::UPLOAD) throw new ServiceError('forbidden', ...);

"No such attachment" and "not yours" are the same not_found, so an id cannot be probed.

๐Ÿ—‚๏ธ A folder for each ticket

FilesSave::ticketFolder($conn, $analystId, $parentId, $ticketId)
// "ABC-123 - Printer jammed" (ticket number, " - ", subject; cleaned, 120 characters at most)
// inside the chosen folder - REUSED when it is already there, made (inherit_permissions = 1) when not

It inherits the parent's permissions, as any folder made in Files does. Making it needs only Upload on the parent, which the save has already checked. It is audited as create_folder with for_ticket.

๐Ÿ’พ Writing into Files

filesStoreLocalFile($conn, $analystId, $folderId, $localPath, $name, $onConflict)
// 'ask' and the name is taken -> ['conflict' => true, 'name', 'choices', 'default']   (the dialog asks)
// 'skip'                      -> ['skipped' => true]
// otherwise                   -> ['item_id', 'version_no', 'name']

In order:

  1. The choice must be one Files โ†’ Settings โ†’ Versions offers. Overwrite needs Modify, and the size must be within the largest upload.
  2. The bytes are copied with putFile($rel, $localPath, false). The source attachment stays exactly where it was.
  3. The item and version rows are written in one transaction. If that fails, the bytes just written are deleted.
  4. queueIfMoved() runs, then the audit (upload or new_version, saved_from: freeitsm), then overwrite or keep-N.

โš ๏ธ Traps

  • TRAP: filesStoreLocalFile() and api/files/upload.php's finish step write a file the same way. Bytes, rows, queueIfMoved, then keep-N or overwrite. Change one and you change the other.
  • TRAP: FilesSave uses filesFreeName() and filesNameTaken() from includes/files/api_bootstrap.php. That file also signs the request in, so a caller must be a Files endpoint. That is why the tests for this go through HTTP.
  • TRAP: files_save.php needs service_context.php for ServiceError. The first live run failed with Class "ServiceError" not found. files_attach.php only had it by luck, through services/documents.php.
  • TRAP: in a test script, __DIR__ is the script's folder, not the install's. Use the working directory when writing fixture files into the install.

๐Ÿงช Tests

  • A live run on the test install through the real endpoints (12):
    • the folder offered as a target;
    • an email attachment saved, with exactly its contents, and the original untouched;
    • saving again asking, with the upload's choices;
    • Keep both giving (2);
    • "In a folder for this ticket" making <number> - <subject>, and reusing it next time;
    • an attachment that does not exist refused;
    • a stored document saved;
    • the panel told can_save_to_files;
    • the Sharing defaults saved and read back.
  • Chrome:
    • the ticket page loads inbox.js and save-to-files.js;
    • the attachment list shows Save to Files;
    • clicking it opens the dialog. With no folder the person can upload to, the dialog says so.

See also: Attach from Files - Developer Guide ยท Files versions - Developer Guide ยท Files - Developer Guide

โš ๏ธ **GitHub.com Fallback** โš ๏ธ