Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?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
- *
- * http://pastebin.com/yjQbAPqR
- * http://osticket.com/forum/discussion/comment/75406
- *
- * YOU WILL NEED TO CONFIGURE THE FOLLLOWING:
- *
- * Specify your osTicket username, probably best to use an Admins username for this. DO NOT NEED PASSWORD.
- * this is the user account on your installation, not forum or anything like that.
- */
- define ( 'MY_USER_ID', 'Grizly' );
- /**
- * Any closed ticket older than this many months will be archived/deleted.
- */
- define ( 'DELETEMONTHS', 1 );
- /**
- * Which subfolder would you like to start archiving tickets into?
- */
- define ( 'BACKUPDIR', getcwd () . DIRECTORY_SEPARATOR . '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', getcwd () . DIRECTORY_SEPARATOR . 'scp' . DIRECTORY_SEPARATOR . 'restricted' );
- /**
- * Change this to true, to start deleting.
- * ;-) This will start actually deleting tickets, so, make sure its backing them up properly first.
- */
- define ( 'ACTUALLY_DELETE', false );
- /******************************************************************************************************************************
- * Should be able to ignore from here
- */
- // 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' )) );
- // This starts osTicket code
- require_once 'main.inc.php';
- // Code to convert osTicket db files, into filesystem objects or strings.
- // Includes code to convert msdocs into text..
- require_once 'class.attachment.file.php';
- // basically debug mode.. show all errors! Has to be set after main require, as main turns all this off.. which is great for client/user web-pages, not so great if you want to know why something is broken!
- if (! CRONFRIENDLY) {
- error_reporting ( E_ALL );
- ini_set ( 'display_errors', 1 );
- ini_set ( 'display_startup_errors', 1 );
- }
- // Disable in defines above, or leave alone for more secure mode..
- if (REMOTECRONENABLED || (! (php_sapi_name () == 'cli')))
- die ( 'Cannot be run from browser.. ' );
- // These reduce errors running in CLI mode.
- $_SERVER ['REMOTE_ADDR'] = 'localhost';
- $_SERVER ['HTTP_USER_AGENT'] = 'Script';
- // PDF image centering required info.. need to scale and center images on pages
- // especially if they were PDF's themselves originally!
- 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 );
- // 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)';
- /**
- * 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.
- */
- function sanitize($string, $force_lowercase = true, $anal = false) {
- $strip = array ("~","`","!","@","#","$","%","^","&","*","(",")","_","=","+","[","{","]","}","\\","|",";",":","\"","'","‘","’","“","”","–","—","—","–",",","<",".",">","/","?" );
- $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;
- }
- // Begin
- // We know archiving is expensive to the system, so lower it to bottom of system process priority (LINUX ONLY)
- _pcntl_setpriority ( 19 );
- $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 ) {
- if (! delete_ticket ( $ticket ['ticket_id'] ))
- print 'Failed to delete ticket_id: ' . $ticket ['ticket_id'] . "\n\n";
- }
- } 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.";
- }
- /**
- * 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>
- *
- * @param int $priority
- * @param string $pid
- * @return boolean
- */
- 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
- *
- */
- 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.
- */
- function centreImage($pdf, $img, $ext) {
- list ( $width, $height ) = 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
- * The filenames to merge
- * @param String $outputname
- * The filename to end up with. (Use the first one we generated, as that is what we really wanted)
- */
- 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!" );
- }
- }
- /**
- * Uses <a href="http://www.osticket.com">osTicket</a> functions to remove a ticket correctly.
- * By default it creates a "backup directory" for each department, saves a PDF copy of the ticket, uses PDF->IMG converter to save PDF attachements.
- * Image and Plain-text/HTML attachments are embedded directly. All at the end of the generated file.
- *
- * Useful for archival purposes and purging of obsolete tickets.
- */
- function delete_ticket($id) {
- // Create an osTicket object
- $t = new Ticket ( $id );
- $msg = " ticket $id:" . $t->getSubject () . "\n";
- if(!CRONFRIENDLY && ACTUALLY_DELETE)
- print "Deleting $msg";
- elseif (!CRONFRIENDLY)
- print "Archving $msg";
- // Add department subfolder name
- $folder = BACKUPDIR . $t->getDeptName () . '/';
- // Department might be new, or we haven't made a folder for it yet.
- if (! is_writable ( $folder ))
- mkdir ( $folder );
- // Create a filename for the PDF
- $name = 'Ticket-' . $t->getExtId ();
- $name .= ' ' . $t->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 ( $t, 'getOrderNumber' ))
- $on = $t->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 = sanitize ( $name, False, False );
- // Put the PDF's name at the end of the filename, this is the final filename.
- $outputfile = $folder . $name . '.pdf';
- if (is_readable ( $outputfile ) && ACTUALLY_DELETE) {
- // we already have the file, lets just proceed straight to deleting the ticket
- $t->delete ();
- return true;
- } elseif (is_readable ( $outputfile ))
- return true; // no point reinventing the wheel and continually re-archving a ticket thread over-and-over-and-over..
- // Create a PDF object to represent the ticket.
- $pdf = new Ticket2PDF ( $t, '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 = $t->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 = $t->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 = $t->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 TDJ_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.
- 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 ( $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 = $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;
- }
- mergePDFS ( $merge_me, $outputfile );
- } else {
- if (! CRONFRIENDLY)
- print "No mergeable pdfs found. \n";
- }
- if (ACTUALLY_DELETE)
- return $t->delete ();
- return true;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement