Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save clonemeagain/7905624 to your computer and use it in GitHub Desktop.
Save clonemeagain/7905624 to your computer and use it in GitHub Desktop.
osTicket 1.7.2+ MOD Archive Tickets. Modifies the delete ticket function to first create a PDF export of the file, includes a cron-runnable script to auto-prune the db!
<?php
/** MOD_Archive START COPY FROM THIS LINE, paste beneath /main.inc.php " require(INCLUDE_DIR.'mysql.php');"
* Which subfolder would you like to start archiving tickets into?
*/
define ( 'BACKUPDIR', ROOT_DIR . 'backups' . DIRECTORY_SEPARATOR );
/**
* Change to false to show more output, makes cron send emails which you may not care about.
*/
define ( 'CRONFRIENDLY', true );
/**
* Change to true to allow anyone on the internet to initiate..
* not recommended unless you have a remote-cron requirement.
*/
define ( 'REMOTECRONENABLED', false );
/**
* Where do you want your temporary files stored..
* could be /tmp/ostickets/ or anywhere writable by the user.
*/
define ( 'URI_FOLDER', ROOT_DIR . 'scp' . DIRECTORY_SEPARATOR . 'restricted' );
/**
* Clean up the temp directory? Do you not have a cron-job checking this folder for old files and cleaning it up?
* Hmm.. I guess you wouldn't.. I do, but I use these for in-line attachments for PDF previewing.. maybe you don't.
*/
define( 'DELETE_TEMPS_AFTER_USE', true);
require_once(INCLUDE_DIR.'class.mod_archive.php');
<?php
/**
* Removes obsolete tickets, can be run as often as you choose.
* Ideally via cron:
# Every 28 days?
* * /28 * * nobody /usr/bin/php /path/to/remove_obsolete_tickets.php
*
* Currently only tested/designed for linux, specifically Debian, but should work on others.
* Requires Ghostscript (gs) installed for PDF merging to work.
*
* @author Grizly
*
*
* YOU WILL NEED TO CONFIGURE THE FOLLLOWING:
*
* Specify your osTicket username, probably best to use an Admins username for this. DO NOT NEED PASSWORD.
*/
define ( 'MY_USER_ID', 'Grizly' );
/**
* Any closed ticket older than this many months will be archived/deleted.
*/
define ( 'DELETEMONTHS', 1 );
define ( 'DEBUG' , false); //enable to show all errors while running.. useful if you change things.
// This starts osTicket code
require_once 'main.inc.php';
//re-enable all error displays.
if(DEBUG){
error_reporting(E_ALL); //Respect whatever is set in php.ini (sysadmin knows better??)
#display errors
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
}
// Only required if you are archiving.. the PDF printer needs a userobject global to function correctly.
$thisstaff = new Staff ( MY_USER_ID );
// select old messages.. we probably don't need any of them where we're going.
$select = 'SELECT ticket_id FROM ' . TICKET_TABLE . " WHERE status='closed' AND closed < DATE_SUB(NOW(), INTERVAL " . DELETEMONTHS . ' MONTH)';
// Begin
$res = db_query ( $select );
if ($res && $num = db_num_rows ( $res )) {
$messages = db_assoc_array ( $res, true );
if ($messages && ! CRONFRIENDLY)
print "Found $num matching deletables!\nDeleting messages older than " . DELETEMONTHS . " months, using " . BACKUPDIR . " for archives.\n";
foreach ( $messages as $ticket ) {
$t = new Ticket($ticket['ticket_id']);
if(DEBUG)
$t->archive();
else
$t->delete();
}
} elseif (! CRONFRIENDLY) {
print "No matching tickets found, either expand the months to include more tickets, create some and change the dates, or ignore this as it probably isn't an actual error.";
}
<?php
/**
* Removes obsolete tickets, can be run as often as you choose.
*
* I've added a MOD to hijack the $ticket->delete() function, which first archives a ticket.. ;-)
*
* Ideally via cron:
# Every 28 days?
* * /28 * * nobody /usr/bin/php /path/to/remove_obsolete_tickets.php
*
* Currently only tested/designed for linux, specifically Debian, but should work on others.
* Requires Ghostscript (gs) installed for PDF merging to work.
*
* @author Grizly
*
*
* YOU WILL NEED TO CONFIGURE THE FOLLLOWING:
*
* Specify your osTicket username, probably best to use an Admins username for this. DO NOT NEED PASSWORD.
*/
// determine if gs (ghostscript) is installed and accessible to php process. (Yet another reason to run in CLI mode)
// Unfortunately, when processing some broken PDF's, you will see some errors even with CRONFRIENDLY enabled.
define ( 'PDF_MERGE_ENABLED', (is_readable ( exec ( 'which gs' ) )) );
// png->jpg converter requires GD
define ( 'GD_ENABLED', (function_exists ( 'imagecreatefrompng' )) );
define ( 'DPI', 64 );
define ( 'MM_IN_INCH', 25.4 );
define ( 'A4_WIDTH', 297 );
define ( 'A4_HEIGHT', 210 );
define ( 'MAX_HEIGHT', 800 );
define ( 'MAX_WIDTH', 500 );
class MOD_Archive_Ticket {
public $isArchived;
private $t, $outputfile, $name;
/**
* Create an Archive of a ticket in PDF format, based on the department.
*
* @param
* osTicket Ticket object $ticket
*/
function MOD_Archive_Ticket($ticket) {
$this->t = $ticket;
// Create an output file for the exported PDF.
// Add department subfolder name
$folder = BACKUPDIR . $ticket->getDeptName () . DIRECTORY_SEPARATOR;
// Department name might have a space in it.. this apparently b0rks the pdf merger
$folder = str_replace ( ' ', '_', $folder );
// Department might be new, or we haven't made a folder for it yet.
if (! is_writable ( $folder ))
mkdir ( $folder, 0777, true );
if (! is_writable ( $folder ))
throw new Exception ( "Permissions problem with the folder.. fix ASAP" );
// Create a filename for the PDF
$name = 'Ticket-' . $ticket->getExtId ();
$name .= ' ' . $ticket->getSubject ();
// if you don't have getOrderNumber, its because you didn't modify class.tickets.php to create it, good for you!
if (method_exists ( $ticket, 'getOrderNumber' ))
$on = $ticket->getOrderNumber ();
else
$on = false;
$name .= ($on) ? ' Order-' . $on : '';
// Some subjects may contain names that are unsuitable for filenames, and would therefore break the script, or the filesystem :-(
// currently set to remove all non-alphanumerics.
$name = $this->sanitize ( $name, False, False );
// Put the PDF's name at the end of the filename, this is the final filename.
$this->outputfile = $folder . $name . '.pdf';
if (is_readable ( $this->outputfile )) {
// we already have the file, lets just proceed straight to deleting the ticket
$this->isArchived = true;
} else {
$this->isArchived = false;
}
$this->name = $name;
}
/**
* Convert the ticket into a PDF, much like the normal PDF function, except, this includes all attachments!
*
* @return boolean
*/
public function export_ticket_to_PDF() {
if ($this->isArchived) {
return true;
}
$ticket = $this->t;
if ($ticket->isOpen () || ! $ticket->getCloseDate ()) {
throw new Exception ( "Unable to delete open tickets!" );
}
$name = $this->name;
// Create a PDF object to represent the ticket.
$pdf = new Ticket2PDF ( $ticket, 'A4', true ); // specifying TRUE will save all notes/responses & messages to the start of the PDF.
// An array of pdf's which will be merged together with the created one at the end.
$merge_queue = array ();
// Append any attachments to the pdf.. ;-)
// Start by getting the tickets thread of messages.
$thread = $ticket->getThread ();
// Find all the attachments to the thread.
if ($total = $thread->getNumAttachments ()) {
if (! CRONFRIENDLY)
print "Found $total Attachments for $name!\n ";
// Attachments are actually stored in relation to the messages in the thread, not the ticket.
// So, get the entries to the thread themselves
$thread = $ticket->getThreadEntries ( array ('M','R','N' ) ); // Specify Notes, Messages and Responses.. all entries!
foreach ( $thread as $entry ) {
// This line almost word-for-word copied from the normal ticket-view page for staff.
if ($entry ['attachments'] && ($tentry = $ticket->getThreadEntry ( $entry ['id'] )) && ($attachments = $tentry->getAttachments ())) {
foreach ( $attachments as $attachment ) {
// By default, saves the file into a filesystem addressable container.. this means we can import it into the PDF
// Note, this $attachment, is simply an array at this point.
$attached_file = new Export_Parse_Attachment_File ( $attachment );
if (! CRONFRIENDLY)
print ("Attachment is a " . $attached_file->type . " file. \n") ;
switch ($attached_file->type) {
case 'pdf' :
{
$merge_queue [] = $attached_file->uri;
break;
}
case 'text' :
{
// Text/HTML can be added as a Cell of text. We don't even need a new page for it! (Should flow into a new page if required)
$pdf->Cell ( 0, 0, $attached_file->str, 0, 1, 'C' );
break;
}
case 'image' :
{
if (strlen ( $attached_file->uri ) && is_readable ( $attached_file->uri ) && filesize ( $attached_file->uri ) > 10) { // skip empty uris.. christ.
@$pdf->AddPage ();
if (! CRONFRIENDLY)
print "Creating new PDF page..\n";
// Looks better if the image is zoomed in, centered and such.
$this->centreImage ( $pdf, $attached_file->uri, $attached_file->ext );
}
break;
}
default :
if (! CRONFRIENDLY)
print ("Unable to merge attachment: " . $attached_file->uri . ' As its type was not set') ;
}
} // end foreach
} // endif
} // endforeach
} elseif (! CRONFRIENDLY) {
print "Didn't find any attachments.\n";
}
// Initiate the actual output of the PDF including our text-based and image-inserted additions.
$pdf->Output ( $this->outputfile, 'F' );
// Begin appending existing PDF attachments to the end of the ticket PDF.
if (count ( $merge_queue ) > 0 && PDF_MERGE_ENABLED) {
if (! CRONFRIENDLY)
print ("Merging PDFs! \n") ;
// We have some PDF's that need to be appended to the generated file.
$merge_me = $this->outputfile;
// Create list of pdf filenames to merge together as a string
foreach ( $merge_queue as $file ) {
if (is_readable ( $file ) && filesize ( $file ) > 10)
$merge_me .= ' ' . $file;
}
MOD_Archive_Ticket::mergePDFS ( $merge_me, $this->outputfile );
} else {
if (! CRONFRIENDLY)
print "No mergeable pdfs found. \n";
}
// Reset time & permissions on file.
$this->reDate ( $this->outputfile );
$this->isArchived = true;
}
/**
* From <a href="http://stackoverflow.com/a/2668953">StackOverflow</a>
*
* Function: sanitize
* Returns a sanitized string, typically for URLs.
*
* Parameters:
* $string - The string to sanitize.
* $force_lowercase - Force the string to lowercase?
* $anal - If set to *true*, will remove all non-alphanumeric characters.
*
* This is used to allow things like the subject line as a part of the tickets filename in the export, makes it much quicker to re-locate after archiving.
*/
private static function sanitize($string, $force_lowercase = true, $anal = false) {
$strip = array ("~","`","!","@","#","$","%","^","&","*","(",")","_","=","+","[","{","]","}","\\","|",";",":","\"","'","&#8216;","&#8217;","&#8220;","&#8221;","&#8211;","&#8212;","—","–",",","<",".",">","/","?" );
$clean = trim ( str_replace ( $strip, "", strip_tags ( $string ) ) );
$clean = preg_replace ( '/\s+/', "-", $clean );
$clean = ($anal) ? preg_replace ( "/[^a-zA-Z0-9]/", "", $clean ) : $clean;
return ($force_lowercase) ? (function_exists ( 'mb_strtolower' )) ? mb_strtolower ( $clean, 'UTF-8' ) : strtolower ( $clean ) : $clean;
}
/**
* From: <a href="http://www.php.net/manual/en/function.pcntl-setpriority.php#48430">http://www.php.net/manual/en/function.pcntl-setpriority.php#48430</a>
*
* Possibly unnecessary now.
*
* @param int $priority
* @param string $pid
* @return boolean
*/
private function _pcntl_setpriority($priority, $pid = false) {
$pid = ($pid) ? $pid : getmypid ();
$priority = ( int ) $priority;
$pid = ( int ) $pid;
// check if out of bounds
if ($priority > 20 && $priority < - 20) {
return False;
}
if ($pid == 0) {
$pid = getmypid ();
}
// check if already set.
if ($priority == pcntl_getpriority ( $pid ))
return true;
return exec ( "renice $priority -p $pid" ) != false;
}
/**
* Calculates the correct height/width in MM for the DPI and returns to centreImage.
* * From: <a href="https://gist.github.com/benshimmin/4088493">BenShimmin gisthub.com</a>
*
* @param string $imgFilename
* @return multitype:number
*
*/
static private function resizeToFit($imgFilename) {
list ( $width, $height ) = getimagesize ( $imgFilename );
if (! $width)
$width = 500;
if (! $height)
$height = 800;
$widthScale = MAX_WIDTH / $width;
$heightScale = MAX_HEIGHT / $height;
$scale = min ( $widthScale, $heightScale );
return array (round ( ($scale * $width) * MM_IN_INCH / DPI ),round ( ($scale * $height) * MM_IN_INCH / DPI ) );
}
/**
* Centers an image on a page, useful for large images embedded within a pdf page..
* ;-)
*
* From: <a href="https://gist.github.com/benshimmin/4088493">BenShimmin gisthub.com</a>
* Modified for this, removed a few function-calls, and de-OOP'd
*
* @param Ticket2PDF $pdf
* @param String $img
* the full path to the file
* @param String $ext
* extension of image.
*/
static private function centreImage(&$pdf, $img, $ext) {
list ( $width, $height ) = MOD_Archive_Ticket::resizeToFit ( $img );
// you will probably want to swap the width/height
// around depending on the page's orientation
$pdf->Image ( $img, (A4_HEIGHT - $width) / 2, (A4_WIDTH - $height) / 2, $width, $height, $ext );
}
/**
* From: <a href="http://www.linux.com/news/software/applications/8229-putting-together-pdf-files">linux.com</a>
*
* Uses Ghostscript to combine PDF files, something like the following:
*
* gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=finished.pdf file1.pdf file2.pdf
*
* Unless you're very familiar with Ghostscript, that string of commands won't mean much to you. Here's a quick breakdown:
* gs -- starts the Ghostscript program
* -dBATCH -- once Ghostscript processes the PDF files, it should exit. If you don't include this option, Ghostscript will just keep running
* -dNOPAUSE -- forces Ghostscript to process each page without pausing for user interaction
* -q -- stops Ghostscript from displaying messages while it works
* -sDEVICE=pdfwrite -- tells Ghostscript to use its built-in PDF writer to process the files
* -sOutputFile=finished.pdf -- tells Ghostscript to save the combined PDF file with the name that you specified
*
* @param String $pdfs
* @param String $outputname
* @throws Exception
*/
static private function mergePDFS($pdfs, $outputname) {
if ($pdfs && $outputname) {
// Create temporary file to merge them into
$tmpfile = tempnam ( '/tmp', 'pdfmerge_' );
// let GhostScript do the merge, fast!
system ( "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=$tmpfile $pdfs" );
// Rename merged file (this will move as well) back to the original file, overwriting it.
rename ( $tmpfile, $outputname );
} else {
throw new Exception ( "incorrect argument, no system call for you!" );
}
}
/**
* Change the file modification time to match the date on the ticket..
* makes it easier to locate a ticket from 3 weeks ago etc.
*
* @param String $this->outputfile
* @throws Exception
*/
private function reDate() {
if (is_writable ( $this->outputfile ) && $d = strtotime ( $this->t->getCloseDate () )) {
if (! CRONFRIENDLY)
print "New timestamp for $this->outputfile = $d";
touch ( $this->outputfile, $d );
// oddly, it was only setting 600 permissions on most files, some had 622.. don't know why.
// I was immediately moving these to the fileserver, which of course, locked them to me!
chmod ( $this->outputfile, 0666 );
} else {
throw new Exception ( "Unable to modify file we just created!" );
}
}
}
/**
* Basically an attachment type checker..
*
* But downloads the attachment itself into a named file, maintains an association between the attachment_id and the URI for the file.. etc.
*
* If file is text based, contains the string representing the contents of the file instead.
*
* @author Grizly
*
*/
class Export_Parse_Attachment_File {
public $uri, $str, $type, $ext;
function Export_Parse_Attachment_File($attachment) {
$this->uri = array ();
$this->str = '';
$id = $attachment ['attach_id'];
// Make the folder if it doesn't exist.
$dir = URI_FOLDER;
if (! is_dir ( $dir )) {
error_log ( "Making dir: $dir" );
mkdir ( $dir );
}
// Locate osTickets attachment object for this id
$a = Attachment::lookup ( $id );
// Get the file object itself, which is also an osTicket object/db entity etc.
$f = $a->getFile ();
// Route by attachment I wish I could remember where I found this majestic bitty.. ;-)
$this->ext = $ext = strtolower ( substr ( strrchr ( $f->getName (), '.' ), 1 ) );
$filename = $dir . DIRECTORY_SEPARATOR . "t_$id.$ext";
switch ($ext) {
case 'pdf' :
{
if (file_exists ( $filename ) != TRUE) {
file_put_contents ( $filename, $f->getData () );
}
$this->uri = $filename;
$this->type = 'pdf';
break;
}
case 'txt' :
case 'htm' :
case 'html' :
{
$this->type = 'text';
$this->str = ($ext == 'txt') ? '<pre>' . $f->getData () . '</pre>' : Format::safe_html ( $f->getData () );
// we don't set the URI here, as we are using the string value itself. No need to create a file.
$this->uri = false;
break;
}
case 'png' :
{
if (! GD_ENABLED)
break;
// gotta save the file first.. doh!
if (file_exists ( $filename ) != TRUE) {
file_put_contents ( $filename, $f->getData () );
}
// PDF doesn't support alpha channels for some reason, so we need to quickly and painlessly remove them.
$tmp = tempnam ( '/tmp/osticket', '_png_converter_' );
$jpeg_version = $this->png2jpg ( $filename, $tmp );
// need to change its file-extension to match its new colorful format ;-)
$this->ext = $ext = 'jpg';
$filename = str_replace ( '.png', '.jpg', $filename );
rename ( $tmp, $filename );
// don't break, let the image section continue to work.
}
case 'jpg' :
case 'gif' :
case 'jpeg' :
case 'tif' :
{
$this->type = 'image';
if (file_exists ( $filename ) != TRUE) {
file_put_contents ( $filename, $f->getData () );
}
$this->uri = $filename;
break;
}
case 'doc' :
case 'docx' : // stupid word docs
case 'xls' :
case 'xlsx' : // stupid excel docs
{
$this->type = 'text';
if (file_exists ( $filename ) != TRUE) {
file_put_contents ( $filename, $f->getData () );
}
// We don't really have time to format this shite, but we can at least grok some of the contents.. ;-)
$docObj = new DocxConversion ( $filename );
$this->str = $docObj->convertToText ();
break;
}
default :
}
}
/**
* I'm starting to wonder how much is original code, and how much I just ripped off from StackOverflow!
*
* from: <a href="http://stackoverflow.com/a/1201823/276663">StackOverflow!</a>
*
* @param string $originalFile
* The full path to the PNG file.
* @param string $this->outputfile
* where does he go?
* @param int $quality
* (default should be 75 on imagejpeg anyway)
*/
private function png2jpg($originalFile, $outputfile, $quality = 75) {
$image = imagecreatefrompng ( $originalFile );
imagejpeg ( $image, $outputfile, $quality );
imagedestroy ( $image );
}
}
/**
* Here is simple and easily understandable class which does the right job for .
*
* doc/.docx , PHP docx reader: Convert MS Word Docx files to text.
* http://www.phpclasses.org/package/7934-PHP-Convert-MS-Word-Docx-files-to-text.html
* http://stackoverflow.com/a/19503654
*/
class DocxConversion {
private $filename;
public function __construct($filePath) {
$this->filename = $filePath;
}
private function read_doc() {
$fileHandle = fopen ( $this->filename, "r" );
$line = @fread ( $fileHandle, filesize ( $this->filename ) );
$lines = explode ( chr ( 0x0D ), $line );
$outtext = "";
foreach ( $lines as $thisline ) {
$pos = strpos ( $thisline, chr ( 0x00 ) );
if (($pos !== FALSE) || (strlen ( $thisline ) == 0)) {
} else {
$outtext .= $thisline . " ";
}
}
$outtext = preg_replace ( "/[^a-zA-Z0-9\s\,\.\-\n\r\t@\/\_\(\)]/", "", $outtext );
return $outtext;
}
private function read_docx() {
$striped_content = '';
$content = '';
$zip = zip_open ( $this->filename );
if (! $zip || is_numeric ( $zip ))
return false;
while ( $zip_entry = zip_read ( $zip ) ) {
if (zip_entry_open ( $zip, $zip_entry ) == FALSE)
continue;
if (zip_entry_name ( $zip_entry ) != "word/document.xml")
continue;
$content .= zip_entry_read ( $zip_entry, zip_entry_filesize ( $zip_entry ) );
zip_entry_close ( $zip_entry );
} // end while
zip_close ( $zip );
$content = str_replace ( '</w:r></w:p></w:tc><w:tc>', " ", $content );
$content = str_replace ( '</w:r></w:p>', "\r\n", $content );
$striped_content = strip_tags ( $content );
return $striped_content;
}
/**
* **********************excel sheet***********************************
*/
private function xlsx_to_text($input_file) {
$xml_filename = "xl/sharedStrings.xml"; // content file name
$zip_handle = new ZipArchive ();
$output_text = "";
if (true === $zip_handle->open ( $input_file )) {
if (($xml_index = $zip_handle->locateName ( $xml_filename )) !== false) {
$xml_datas = $zip_handle->getFromIndex ( $xml_index );
$xml_handle = DOMDocument::loadXML ( $xml_datas, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING );
$output_text = strip_tags ( $xml_handle->saveXML () );
} else {
$output_text .= "";
}
$zip_handle->close ();
} else {
$output_text .= "";
}
return $output_text;
}
/**
* ***********************power point files****************************
*/
private function pptx_to_text($input_file) {
$zip_handle = new ZipArchive ();
$output_text = "";
if (true === $zip_handle->open ( $input_file )) {
$slide_number = 1; // loop through slide files
while ( ($xml_index = $zip_handle->locateName ( "ppt/slides/slide" . $slide_number . ".xml" )) !== false ) {
$xml_datas = $zip_handle->getFromIndex ( $xml_index );
$xml_handle = DOMDocument::loadXML ( $xml_datas, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING );
$output_text .= strip_tags ( $xml_handle->saveXML () );
$slide_number ++;
}
if ($slide_number == 1) {
$output_text .= "";
}
$zip_handle->close ();
} else {
$output_text .= "";
}
return $output_text;
}
public function convertToText() {
if (isset ( $this->filename ) && ! file_exists ( $this->filename )) {
return "File Not exists";
}
$fileArray = pathinfo ( $this->filename );
$file_ext = $fileArray ['extension'];
if ($file_ext == "doc" || $file_ext == "docx" || $file_ext == "xlsx" || $file_ext == "pptx") {
if ($file_ext == "doc") {
return $this->read_doc ();
} elseif ($file_ext == "docx") {
return $this->read_docx ();
} elseif ($file_ext == "xlsx") {
return $this->xlsx_to_text ( $this->filename );
} elseif ($file_ext == "pptx") {
return $this->pptx_to_text ( $this->filename );
}
} else {
return "Invalid File Type";
}
}
}
?>
/**
* MOD_Archive_Ticket function to hook
* @return boolean
*/
function archive(){
//Due to the way the files are "touched", if the ticket isn't closed, it generates all sorts of errors.
if($this->isOpen())
$this->close();
$archived_ticket = new MOD_Archive_Ticket($this);
$archived_ticket->export_ticket_to_PDF();
if(DELETE_TEMPS_AFTER_USE) //All uses of the Temporary files should be final here.
array_map('unlink', glob(URI_FOLDER."/t_*"));
if($archived_ticket->isArchived)
return true;
return false;
}
function delete() {
//MOD_Archive_Ticket, wrap entire delete call initiator in archive call..
if($this->archive()){
$sql = 'DELETE FROM '.TICKET_TABLE.' WHERE ticket_id='.$this->getId().' LIMIT 1';
if(!db_query($sql) || !db_affected_rows())
return false;
//delete just orphaned ticket thread & associated attachments.
$this->getThread()->delete();
return true;
}else{
return false;
}
}
@clonemeagain
Copy link
Author

Modified to hook into normal delete methods, works for staff who can delete tickets now.

Basically, modify the main.inc.php file, to include the new class.mod_archive.php and setup a few global defines, add it after the require_once('class.mysqli.php') bit.

Save the new file: /include/class.mod_archive.php

Modify the file /include/class.ticket.php, replace the delete function with the new archive() and delete() which simply wraps delete in an archive call. ;-)

Enjoy your newly archived tickets. Great if your staff insist on deleting tickets despite that not being a good thing.

See forum topic about it here: http://osticket.com/forum/discussion/comment/75406

@clonemeagain
Copy link
Author

Found a few problems, changed the setup directories to use osTicket's "ROOT_DIR" starting point, need to check permissions twice, and close tickets before archiving them.. can't archive an open ticket AND use the closedate for the touch-time and expect it to work.. this has been sorted, the archive() ticket function now closes the ticket before archiving it.

Kind of fun "returning early" from the delete code while testing, you get an archived copy of the ticket with all attachments, then you get the pleasure of opening the ticket, attaching the export of itself, deleting the pdf, then deleting the ticket all over again! TicketCeptionPDF!!! :-)

MUST MAKE SURE THE PERMISSIONS ARE RIGHT.. you need the webserver to have write permissions to the subfolder you want to save backups in, and the subfolder you are saving temporary files in.

// New Features
If you guys aren't using previews, you should probably enable "DELETE_TEMPS_AFTER_USE". (new default)
Set it to rewrite the folder names for Departments containing spaces.. FPDF/gs doesn't like spaces in filenames..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment