Archive

Posts Tagged ‘check’

[osTicket][1.7][MOD] Auto Save

July 11th, 2013 No comments

Many CMS like WordPress have draft or even revision feature. This crucial feature is still absent in osTicket and this mod is to make up for this defects.
In the company I am working, every staff would deal with multiple of tickets as well as phone call and processing orders. Most of the time they may leave the reply in the middle and lose the things they typed after accident browser shut down. This mod can auto save the typed reply and even remind the staff for the unsent draft which they might already forgot.

Features:

  1. Auto save typed reply into database in fixed time-frame
  2. Added Drafted Ticket tag to quick access to all drafted ticket reply, tag is hidden when there’s no drafted ticket
  3. Reminder emails will be sent to staffs who have their draft in record
  4. Ticket setting to disable / set time interval for auto save and reminder emails
  5. Email template for the email reminders

How to:

  1. Once staff clicked the reply ticket text-area, a record of the reply message and the check-box options is inserted into database.
  2. Staffs can view all their own drafted tickets under the Drafted Tickets tag
  3. When staffs return to the drafted tickets, the reply message and the check-box options is loaded from database
  4. System will check and send reminder emails to the staffs having drafted ticket every fixed time-frame. (Rely on cron, if you set autocron they alert only be checked and sent when there’s staff activities)
  5. Drafts will be deleted once the drafted reply are sent, or staff can manually select and discard the them in Drafted Tickets tag
  6. Admin can set the disable / set time interval for auto save and reminder emails in Ticket Settings
  7. Admin can change the reminder emails template in Email Template

Preview:

Auto save in post reply

Auto save in post reply

Drafted Ticket tag

Drafted Ticket tag

Auto save in ticket settings

Auto save in ticket settings

Email template

Email template

Email template message

Email template message

Install:

This is a bit heavily modified mod, having sql update, 2 new file and 28 modifications, please be patient 😀

  1. MySQL
    ALTER TABLE `ost_email_template` ADD `draft_alert_subj` VARCHAR( 255 ) NOT NULL AFTER `ticket_reply_body` , 
    ADD `draft_alert_body` TEXT NOT NULL AFTER `draft_alert_subj`;
     
    UPDATE `ost_email_template` SET `draft_alert_subj` = 'Unsent Ticket Draft [#%{ticket.number}] %{ticket.subject}',
    `draft_alert_body` = '%{recipient},
     
    You was replying [#%{ticket.number}] %{ticket.subject} and a draft has been saved. Please review and send.
     
    -------------------
    Created on: %{draft.create_date}
    Last updated on: %{draft.update_date}
    Last reminded on: %{draft.remind_date}
     
    %{message}
    -------------------
     
    To view the draft, please login to the support ticket system.
     
    %{draft.staff_link}
     
    - Your friendly Customer Support System - powered by osTicket.'
    WHERE tpl_id = '1';
     
    ALTER TABLE `ost_config` ADD `autosave_seconds` INT( 5 ) UNSIGNED NOT NULL DEFAULT '1' AFTER `schema_signature` ,
    ADD `draft_alert_minutes` INT( 5 ) UNSIGNED NOT NULL DEFAULT '60' AFTER `autosave_seconds`;
     
    CREATE TABLE IF NOT EXISTS `ost_ticket_draft` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `ticket_id` int(11) unsigned NOT NULL DEFAULT '0',
      `staff_id` int(11) unsigned NOT NULL DEFAULT '0',
      `email_reply` tinyint(1) NOT NULL DEFAULT '1',
      `body` text NOT NULL,
      `signature` varchar(4) NOT NULL DEFAULT 'none',
      `reply_ticket_status` tinyint(1) NOT NULL DEFAULT '0',
      `created` datetime NOT NULL,
      `updated` datetime NOT NULL,
      `reminded` datetime NOT NULL,
      PRIMARY KEY (`id`),
      KEY `ticket_id` (`ticket_id`),
      KEY `staff_id` (`staff_id`),
      KEY `created` (`created`),
      KEY `updated` (`updated`),
      KEY `reminded` (`reminded`),
      FULLTEXT KEY `body` (`body`)
    ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;

    change ost_email_template, ost_config, ost_ticket_draft if you have different table prefix

  2. new file
    include/class/class.draft.php

    <?php
    /*********************************************************************
        class.draff.php
     
        Ticket draft
        MOD: Auto Save
     
        Richard Fu <richardfu71@gmail.com>
        Copyright (c)  2006-2013 osTicket
        http://www.osticket.com
     
        Released under the GNU General Public License WITHOUT ANY WARRANTY.
        See LICENSE.TXT for details.
     
        vim: expandtab sw=4 ts=4 sts=4:
    **********************************************************************/
    include_once(INCLUDE_DIR.'class.ticket.php');
     
    //Ticket thread.
    class Draft {
     
        var $id;
        var $ht;
     
        var $ticket;
     
        function Draft($ticket, $staffId=0) {
     
            $this->ticket = $ticket;
     
            $this->id = 0;
     
            $this->load($staffId);
        }
     
        function load($staffId=0) {
     
            if(!$this->getTicketId())
                return null;
     
            $sql='SELECT draft.* '
                .' FROM '.TICKET_DRAFT_TABLE.' draft '
                .' LEFT JOIN '.TICKET_TABLE.' ticket ON ('
                    ."draft.ticket_id=ticket.ticket_id) "
                .' WHERE ticket.ticket_id='.db_input($this->getTicketId());
     
            if($staffId)
                $sql.=' AND draft.staff_id='.db_input($staffId);
     
            $sql.=' GROUP BY draft.id';
     
            if(!($res=db_query($sql)) || !db_num_rows($res))
                return false;
     
            $this->ht = db_fetch_array($res);
     
            $this->id = $this->ht['id'];
     
            return true;
        }
     
        function reload() {
            return $this->load();
        }
     
        function getId() {
            return $this->id;
        }
     
        function getEmailReplay() {
            return $this->ht['email_reply'];
        }
     
        function getBody() {
            return $this->ht['body'];
        }
     
        function getSignature() {
            return $this->ht['signature'];
        }
     
        function getReplyTicketStatus() {
            return $this->ht['reply_ticket_status'];
        }    
     
        function getCreateDate() {
            return $this->ht['created'];
        }
     
        function getUpdateDate() {
            return $this->ht['updated'];
        }
     
        function getRemindDate() {
            return $this->ht['reminded'];
        }    
     
        function getTicketId() {
            return $this->getTicket()?$this->getTicket()->getId():0;
        }
     
        function getTicket() {
            return $this->ticket;
        }    
     
        function getStaffId() {
            return $this->ht['staff_id'];
        }
     
        function getStaff() {
     
            if(!$this->staff && $this->getStaffId())
                $this->staff = Staff::lookup($this->getStaffId());
     
            return $this->staff;
        }
     
        /* variables */
     
        function asVar() {
            return $this->getBody();
        }
     
        function getVar($tag) {
            global $cfg;
     
            if($tag && is_callable(array($this, 'get'.ucfirst($tag))))
                return call_user_func(array($this, 'get'.ucfirst($tag)));
     
            switch(strtolower($tag)) {
                case 'staff_link':
                    return sprintf('%s/scp/tickets.php?id=%d', $cfg->getBaseUrl(), $this->getTicket()->getId());
                    break;
                case 'create_date':
                    return Format::date(
                            $cfg->getDateTimeFormat(),
                            Misc::db2gmtime($this->getCreateDate()),
                            $cfg->getTZOffset(),
                            $cfg->observeDaylightSaving());
                    break;
                case 'update_date':
                    return Format::date(
                            $cfg->getDateTimeFormat(),
                            Misc::db2gmtime($this->getUpdateDate()),
                            $cfg->getTZOffset(),
                            $cfg->observeDaylightSaving());
                    break;
                case 'remind_date':
                    if($this->getRemindDate() == '0000-00-00 00:00:00')
                        return 'N/A';
                    return Format::date(
                            $cfg->getDateTimeFormat(),
                            Misc::db2gmtime($this->getRemindDate()),
                            $cfg->getTZOffset(),
                            $cfg->observeDaylightSaving());
                    break;                
            }
     
            return false;
        }
     
        /* static calls */
     
        function lookup($ticket, $staffId=0) {
     
            return ($ticket
                    && is_object($ticket)
                    && ($draft = new Draft($ticket, $staffId))
                    && $draft->getId()
                    )?$draft:null;
     
        }
     
        //new entry ... we're trusting the caller to check validity of the data.
        function create($vars) {
     
            //Must have...
            if(!$vars['ticketId'] && !$vars['staffId'])
                return false;
     
            $sql=' INSERT INTO '.TICKET_DRAFT_TABLE.' SET created=NOW() '
                .' ,ticket_id='.db_input($vars['ticketId'])
                .' ,staff_id='.db_input($vars['staffId'])
                .', email_reply='.db_input($vars['email_reply'])
                .', body='.db_input($vars['response'])
                .', signature='.db_input($vars['signature'])
                .', reply_ticket_status='.db_input($vars['reply_ticket_status']);
     
            //echo $sql;
            if(!db_query($sql) || !($entry=self::lookup($vars['ticketId'], $vars['staffId'])))
                return false;
     
            return $entry;
        }
     
        function add($vars) {
            return ($entry=self::create($vars))?$entry->getId():0;
        }
     
        function delete() {
     
            $sql = 'DELETE FROM '.TICKET_DRAFT_TABLE.' WHERE id='.$this->getId().' LIMIT 1';
            if(!db_query($sql) || !db_affected_rows())
                return false;
     
            return true;
        }    
     
        function update($vars) {
            if(!$vars || !is_array($vars))
                return false;
            $sql='UPDATE '.TICKET_DRAFT_TABLE.' SET updated=NOW() '
            .', email_reply='.db_input($vars['email_reply'])
            .', body='.db_input($vars['response'])
            .', signature='.db_input($vars['signature'])
            .', reply_ticket_status='.db_input($vars['reply_ticket_status'])
            .' WHERE id='.db_input($this->getId());
     
            return (($res=db_query($sql)) && db_affected_rows($res));
        }
     
        function sendReminder($interval) {
            global $cfg;
     
            $sql='SELECT id, ticket_id, staff_id, body FROM '.TICKET_DRAFT_TABLE
                .' WHERE (reminded != "0000-00-00 00:00:00" AND reminded<NOW()-INTERVAL '.db_input($interval).' MINUTE) '
                .' OR (reminded = "0000-00-00 00:00:00" AND updated<NOW()-INTERVAL '.db_input($interval).' MINUTE) '
                .' ORDER BY created LIMIT 50'; //Remind upto 50 drafts at a time?
     
            if(($res=db_query($sql)) && db_num_rows($res)) {
                while(list($id, $ticket_id, $staff_id, $message)=db_fetch_row($res)) {
                    if(($ticket=Ticket::lookup($ticket_id)) && ($staff=Staff::lookup($staff_id)) && ($draft=Draft::lookup($ticket, $staff_id))) {
     
                        $tpl= $cfg->getDefaultTemplate();
     
                        if(!($email=$cfg->getAlertEmail()))
                            $email =$cfg->getDefaultEmail();
     
                        if($tpl && $email && ($msg=$tpl->getDraftAlertMsgTemplate())) {
     
                            $msg = $draft->replaceVars($msg, array('message' => $message));
                            if($staff->getEmail() && $staff->isAvailable()) {
                                $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
                                $email->sendAlert($staff->getEmail(), $msg['subj'], $alert);
                            }
                        }
                    } else { // remove invalid draft
                        $sql = 'DELETE FROM '.TICKET_DRAFT_TABLE.' WHERE id='.db_input($id).' LIMIT 1';
                        db_query($sql);
                    }
                    // update remind date
                    $sql='UPDATE '.TICKET_DRAFT_TABLE.' SET reminded=NOW()'
                        .' WHERE id='.db_input($id);
                    db_query($sql);
                }
            }
        }
     
        //Replace base variables.
        function replaceVars($input, $vars = array()) {
            global $ost;
     
            $vars = array_merge($vars, array('draft' => $this, 'ticket' => $this->ticket));
     
            return $ost->replaceTemplateVariables($input, $vars);
        }    
    }
    ?>
  3. new file
    include/ajax.draft.php

    <?php
    /*********************************************************************
        ajax.kbase.php
     
        AJAX interface for knowledge base related...allowed methods.
     
        Peter Rotich <peter@osticket.com>
        Copyright (c)  2006-2013 osTicket
        http://www.osticket.com
     
        Released under the GNU General Public License WITHOUT ANY WARRANTY.
        See LICENSE.TXT for details.
     
        vim: expandtab sw=4 ts=4 sts=4:
    **********************************************************************/
    if(!defined('INCLUDE_DIR')) die('!');
     
     
    class DraftAjaxAPI extends AjaxController {
     
        function autoSave($tid, $format='') {
            global $thisstaff, $_GET;
     
            include_once(INCLUDE_DIR.'class.ticket.php');
            include_once(INCLUDE_DIR.'class.draft.php');
     
            if(!$tid || !($ticket=Ticket::lookup($tid)))
                Http::response(404, 'No such ticket');
     
            $ticket = Ticket::lookup($tid);
     
            $vars = $_POST;
     
            if(!($draft=Draft::lookup($ticket, $thisstaff->getId()))){
                $vars['ticketId'] = $tid;
                $vars['staffId'] = $thisstaff->getId();
                $draft = Draft::create($vars);
            }else{
                $draft->update($vars);
            }
            Http::response(200, 'Draft saved on ' . date('Y-m-d H:i:s'));
        }
     
    }
    ?>
  4. include/staff/settings-tickets.inc.php
    FIND

            <tr>
                <td>Ticket Auto-lock Time:</td>
                <td>
                    <input type="text" name="autosave_seconds" size=4 value="<?php echo $config['autosave_seconds']; ?>">
                    <font class="error"><?php echo $errors['autosave_seconds']; ?></font>
                    <em>(Minutes to lock a ticket on activity - enter 0 to disable locking)</em>
                </td>
            </tr>

    ADD AFTER

            <!-- MOD: BOF - Auto Save -->
            <tr>
                <td>Ticket Auto-save Time:</td>
                <td>
                    <input type="text" name="autosave_seconds" size=4 value="<?php echo $config['autosave_seconds']; ?>">
                    <font class="error"><?php echo $errors['autosave_seconds']; ?></font>
                    <em>(Seconds to auto save a replying ticket - enter 0 to disable auto save)</em>
                </td>
            </tr>
            <tr>
                <td>Saved Draft Alert Time:</td>
                <td>
                    <input type="text" name="draft_alert_minutes" size=4 value="<?php echo $config['draft_alert_minutes']; ?>">
                    <font class="error"><?php echo $errors['draft_alert_minutes']; ?></font>
                    <em>(Minutes to alert staff on saved replying ticket draft - enter 0 to disable draft alert)</em>
                </td>
            </tr>
            <!-- MOD: EOF - Auto Save -->
  5. include/staff/ticket-view.inc.php
    FIND

        </ul>
        <?php
        if($thisstaff->canPostReply()) { ?>

    ADD AFTER

        <!-- MOD: BOF - Auto Save -->
        <?php $draft = $ticket->getDraft($thisstaff->getId());?>
        <!-- MOD: EOF - Auto Save -->

    FIND

                        <label><input type='checkbox' value='1' name="emailreply" id="remailreply"
                            <?php echo ((!$info['emailreply'] && !$errors) || isset($info['emailreply']))?'checked="checked"':''; ?>> Email Reply</label>

    REPLACE WITH

                        <!-- MOD: BOF - Auto Save -->
                        <label><input type='checkbox' value='1' name="emailreply" id="remailreply"
                            <?php echo ((!$info['emailreply'] && !$errors) || isset($info['emailreply']) || ($draft && $draft->getEmailReplay()))?'checked="checked"':''; ?>> Email Reply</label>
                        <!-- MOD: EOF - Auto Save -->

    FIND

                        <textarea name="response" id="response" cols="50" rows="9" wrap="soft"><?php echo $info['response']; ?></textarea>

    REPLACE WITH

                        <!-- MOD: BOF - Auto Save -->
                        <textarea name="response" id="response" cols="50" rows="9" wrap="soft"><?php
                        echo $info['response'] ? $info['response'] : ($draft ? $draft->getBody() : ''); 
                        ?></textarea>
                        <!-- MOD: EOF - Auto Save -->

    FIND

                        <label><input type="radio" name="signature" value="mine"
                            <?php echo ($info['signature']=='mine')?'checked="checked"':''; ?>> My signature</label>

    REPLACE WITH

                        <!-- MOD: BOF - Auto Save -->
                        <label><input type="radio" name="signature" value="mine"
                            <?php echo ($info['signature']=='mine' || ($draft && $draft->getSignature()=='mine'))?'checked="checked"':''; ?>> My signature</label>
                        <!-- MOD: EOF - Auto Save -->

    FIND

                        <label><input type="radio" name="signature" value="dept"
                            <?php echo ($info['signature']=='dept')?'checked="checked"':''; ?>>
                            Dept. Signature (<?php echo Format::htmlchars($dept->getName()); ?>)</label>

    REPLACE WITH

                        <!-- MOD: BOF - Auto Save -->
                        <label><input type="radio" name="signature" value="dept"
                            <?php echo ($info['signature']=='dept' || ($draft && $draft->getSignature()=='dept'))?'checked="checked"':''; ?>>
                            Dept. Signature (<?php echo Format::htmlchars($dept->getName()); ?>)</label>
                        <!-- MOD: EOF - Auto Save -->

    FIND

                        $statusChecked=isset($info['reply_ticket_status'])?'checked="checked"':'';

    REPLACE WITH

                        $statusChecked=isset($info['reply_ticket_status']) || ($draft && $draft->getReplyTicketStatus())?'checked="checked"':'';

    ADD TO BOTTOM

    <!-- MOD: BOF - Auto save -->
    <?php if($interval = $cfg->getDraftSaveInterval()) { ?>
    <script type="text/javascript">
    var autoSaveOn = false;
    $(document).ready(function() {
        $('#response').click(function(){
            if(!autoSaveOn){
                autoSaveOn = true;
                autoSave();
            }
        });
    });
     
    function autoSave() {
        var response = $('#response');
        var fObj=response.closest('form');
        var ticketId = $(':input[name=id]',fObj).val();
     
        $.ajax({
            type: "POST",
            url: 'ajax.php/draft/'+ticketId+'/autosave',
            data: {response: response.val(),
                   email_reply: $('#remailreply').is(':checked')?'1':'0',
                   signature: $('input[name="signature"]:checked').val(),
                   reply_ticket_status: $('#reply_ticket_status').is(':checked')?'1':'0'},
            cache: false,
            success: function(msg){
                if(msg) {
                    response.nextAll().remove();
                    response.after($('<span id="auto_save_response">'+msg+'</span>'));
                }
            }
        })
        .done(function() { })
        .fail(function() { });
     
        setTimeout('autoSave()', <?php echo ($interval * 1000); ?>);
    }
    </script>
    <?php } ?>
    <!-- MOD: EOF - Auto save -->
  6. include/staff/tickets.inc.php
    FIND

        case 'assigned':
            $status='open';
            $staffId=$thisstaff->getId();
            $results_type='My Tickets';
            break;

    ADD AFTER

    // MOD: BOF - Auto Save
        case 'drafted':
            $showdrafted=true;
            break;
    // MOD: EOF - Auto Save

    FIND

    $sjoin='';
    if($search && $deep_search) {
        $sjoin=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )';
    }

    ADD AFTER

    // MOD: BOF - Auto Save
    if($showdrafted && ($staffId=$thisstaff->getId())) {
        $results_type='Drafted Tickets';
        $qfrom.=' LEFT JOIN '.TICKET_DRAFT_TABLE.' draft ON (ticket.ticket_id=draft.ticket_id )';
        $qwhere.=' AND draft.staff_id='.db_input($staffId);
    }
    // MOD: EOF - Auto Save

    FIND

                if($thisstaff->canDeleteTickets()) { ?>
                    <input class="button" type="submit" name="delete" value="Delete">
                <?php } ?>
            </p>
            <?php
           }
        } ?>

    REPLACE

                if($thisstaff->canDeleteTickets()) { ?>
                    <input class="button" type="submit" name="delete" value="Delete">
                <?php } ?>
            <?php } ?>
            <!-- MOD: BOF - Auto Save -->
            if(strtolower($status) == 'drafted') { ?>
                    <input class="button" type="submit" name="discard_drafted" value="Discard Drafted Replys" >
            <?php } ?>
            <!-- MOD: EOF - Auto Save -->        
            </p>
        } ?>

    FIND

        <p class="confirm-action" style="display:none;" id="delete-confirm">
            <font color="red"><strong>Are you sure you want to DELETE selected tickets?</strong></font>
            <br><br>Deleted tickets CANNOT be recovered, including any associated attachments.
        </p>

    ADD AFTER

        <!-- MOD: BOF - Auto Save -->
        <p class="confirm-action" style="display:none;" id="discard_drafted-confirm">
            Are you sure want to <font color="red"><b>discard</b></font> the selected tickets drafted replys?
        </p>
        <!-- MOD: EOF - Auto Save -->
  7. include/class.config.php
    FIND

        function alertONMailParseError() {
            return ($this->config['send_mailparse_errors']);
        }

    ADD AFTER

    // MOD: BOF - Auto Save
        function getDraftSaveInterval() {
            return ($this->config['autosave_seconds']);
        }
     
        function getDraftAlertInterval() {
            return ($this->config['draft_alert_minutes']);
        }
    // MOD: EOF - Auto Save

    FIND

            $f['autolock_minutes']=array('type'=>'int',   'required'=>1, 'error'=>'Enter lock time in minutes');

    ADD AFTER

            // MOD: BOF - Auto Save
            $f['autosave_seconds']=array('type'=>'int',   'required'=>1, 'error'=>'Enter auto save time in seconds');
            $f['draft_alert_minutes']=array('type'=>'int',   'required'=>1, 'error'=>'Enter draft alert time in minutes');
            // MOD: EOF - Auto Save

    FIND

                .',autolock_minutes='.db_input($vars['autolock_minutes'])

    ADD AFTER

                // MOD: BOF - Auto Save
                .',autosave_seconds='.db_input($vars['autosave_seconds'])
                .',draft_alert_minutes='.db_input($vars['draft_alert_minutes'])
                // MOD: EOF - Auto Save
  8. include/class.cron.php
    FIND

            TicketLock::cleanup(); //Remove expired locks

    ADD AFTER

            // MOD: BOF - Auto Save
            global $cfg;
            if($cfg->getDraftSaveInterval() && ($interval = $cfg->getDraftAlertInterval())){
            	require_once(INCLUDE_DIR.'class.draft.php');
            	Draft::sendReminder($interval);
            }
            // MOD: EOF - Auto Save
  9. include/class.template.php
    FIND

                case 'overdue_alert':
                     $tpl=array('subj'=>$this->ht['ticket_overdue_subj'],'body'=>$this->ht['ticket_overdue_body']);
                     break;

    ADD AFTER

                // MOD: BOF - Auto Save
                case 'draft_alert':
                     $tpl=array('subj'=>$this->ht['draft_alert_subj'],'body'=>$this->ht['draft_alert_body']);
                     break;
                // MOD: EOF - Auto Save

    FIND

        function getOverdueAlertMsgTemplate() {
            return $this->getMsgTemplate('overdue_alert');
        }

    ADD AFTER

        // MOD: BOF - Auto Save
        function getDraftAlertMsgTemplate() {
        	return $this->getMsgTemplate('draft_alert');
        }
        // MOD: EOF - Auto Save

    FIND

                case 'overdue_alert':
                    $sql.=',ticket_overdue_subj='.db_input($vars['subj']).',ticket_overdue_body='.db_input($vars['body']);
                    break;

    ADD AFTER

                // MOD: BOF - Auto Save
                case 'draft_alert':
                    $sql.=',draft_alert_subj='.db_input($vars['subj']).',draft_alert_body='.db_input($vars['body']);
                    break;
                // MOD: EOF - Auto Save

    FIND

                            'overdue_alert'=>array('name'=>'Overdue Ticket Alert',
                                                   'desc'=>'Alert sent to staff on stale or overdue tickets.')

    ADD AFTER

                            // MOD: BOF - Auto Save
                            ,
                            'draft_alert'=>array('name'=>'Unsent Draft Alert',
                                                 'desc'=>'Alert sent to staff on unsent ticket drafts.')
                            // MOD: EOF - Auto Save
  10. include/class.ticket.php
    FIND

    include_once(INCLUDE_DIR.'class.canned.php');

    ADD AFTER

    // MOD: BOF - Auto Save
    include_once(INCLUDE_DIR.'class.draft.php');
    // MOD: EOF - Auto Save

    FIND

        var $thread; //Thread obj.

    ADD AFTER

        // MOD: BOF - Auto Save
        var $draft;
        // MOD: EOF - Auto Save

    FIND

            $this->thread = null;

    ADD AFTER

            // MOD: BOF - Auto Save
            $this->draft = null;
            // MOD: BOF - Auto Save

    FIND

        function getThreadEntries($type, $order='') {
            return $this->getThread()->getEntries($type, $order);
        }

    ADD AFTER

        // MOD: BOF - Auto Save
        function getDraft($staffId) {
     
            if(!$this->draft)
                $this->draft = Draft::lookup($this, $staffId);
     
            return $this->draft;
        }
        // MOD: EOF - Auto Save

    FIND

                .' ,count(overdue.ticket_id) as overdue, count(assigned.ticket_id) as assigned, count(closed.ticket_id) as closed '

    ADD AFTER

    // MOD: BOF - Auto Save
                .',count(draft.id) as drafted '
    // MOD: EOF - Auto Save

    FIND

                .' LEFT JOIN '.TICKET_TABLE.' closed
                    ON (closed.ticket_id=ticket.ticket_id
                            AND closed.status='closed' )'

    ADD AFTER

    // MOD: BOF - Auto Save
                .'LEFT JOIN '.TICKET_DRAFT_TABLE.' draft
                    ON (draft.ticket_id=ticket.ticket_id
                            AND draft.staff_id='.db_input($staff->getId()).')'
    // MOD: EOF - Auto Save
  11. scp/ajax.php
    FIND

        url('^/kb/', patterns('ajax.kbase.php:KbaseAjaxAPI',
            # Send ticket-id as a query arg => canned-response/33?ticket=83
            url_get('^canned-response/(?P<id>d+).(?P<format>json|txt)', 'cannedResp'),
            url_get('^faq/(?P<id>d+)', 'faq')
        )),

    ADD AFTER

    // MOD: BOF - Auto Save
    		url('^/draft/', patterns('ajax.draft.php:DraftAjaxAPI',
    				url_post('^(?P<tid>d+)/autosave', 'autoSave')
    		)),
    // MOD: EOF - Auto Save
  12. scp/tickets.php
    FIND

                    if($ticket->isClosed() && $wasOpen)
                        $ticket=null;

    ADD AFTER

                    // MOD: BOF - Auto Save
                    if($draft=Draft::lookup($ticket, $thisstaff->getId())){ //Delete any draft for the ticket and staff
                    var_dump($draft->getId());
                        $draft->delete();
                    }
                    // MOD: EOF - Auto Save

    FIND

                                } else {
                                    $errors['err'] = 'You do not have permission to delete tickets';
                                }
                                break;

    ADD AFTER

                            // MOD: BOF - Auto Save
                            case 'discard_drafted':
                                foreach($_POST['tids'] as $k=>$v) {
                                    if(($d=Draft::lookup(Ticket::lookup($v), $thisstaff->getId())) && @$d->delete()) $i++;
                                }
     
                                //Log a warning
                                if($i) {
                                    $log = sprintf('%s (%s) just discarded %d drafted reply(s)',
                                            $thisstaff->getName(), $thisstaff->getUserName(), $i);
                                    $ost->logWarning('Drafts discarded', $log, false);
     
                                }
     
                                if($i==$count)
                                    $msg = "Selected drafted reply ($i) discarded successfully";
                                elseif($i)
                                    $warn = "$i of $count selected drafted reply discarded";
                                else
                                    $errors['err'] = 'Unable to discard selected drafted replys';
                                break;
                            // MOD: EOF - Auto Save

    FIND

        $nav->addSubMenu(array('desc'=>'My Tickets ('.number_format($stats['assigned']).')',
                               'title'=>'Assigned Tickets',
                               'href'=>'tickets.php?status=assigned',
                               'iconclass'=>'assignedTickets'),
                            ($_REQUEST['status']=='assigned'));
    }

    ADD AFTER

    // MOD: BOF - Auto Save
    if($stats['drafted']) {
        $nav->addSubMenu(array('desc'=>'Drafted Tickets ('.number_format($stats['drafted']).')',
                               'title'=>'Drafted Tickets',
                               'href'=>'tickets.php?status=drafted',
                               'iconclass'=>'overdueTickets'),
                            ($_REQUEST['status']=='drafted'));
    }
    // MOD: EOF - Auto Save
  13. support/main.inc.php
    FIND

        define('TICKET_THREAD_TABLE',TABLE_PREFIX.'ticket_thread');

    ADD AFTER

    // MOD: BOF - Auto save
        define('TICKET_DRAFT_TABLE',TABLE_PREFIX.'ticket_draft');    
    // MOD: EOF - Auto save

Files Pack
Support forum
GitHub

Resize text automatically to fit into a fixed size container using JQuery

September 12th, 2012 No comments

Sometime we want to fix the height and the width of a container and fill text in it as much as it can. For example, we have a customer invoice address box, and we want the address to be shown as large as possible while fitting all in the address box.

In normal case, when the text getting longer and being excessive, it expands the container in HTML. We can put CSS overflow property so it wouldn’t expand the container, but in return, we lose the excess text. So can we resize and scale the text automatically to fit into the container without losing and text? One approach checking the text length in PHP and resize their font-size to maximum possible, but the text size can be different across browsers and this does not work on dynamic text (where the user can input and change the text). Therefore, the easier and more accurate way is using JQuery.

There is a light JQuery plugin TextFill can do the job easily. (demo)

HTML

<div id="myTextFillContainer" style="width: 200px; height: 200px;">
  <span>blah blah blah text to be auto-sized</span>
</div>

JQuery

$(document).ready(function() {
    $('#myTextFillContainer').textfill({ maxFontPixels: 36 });
});

Simply select myTextFillContainer div and use the textfill() function, input a max font size to be 36px, that’s it!

Screenshots

Long Text

Long Text

Short Text

Short Text

Variety

We can have multiple containers with different text tag other than span:

$('#myTextFillContainer1').textfill({ minFontPixels: 10, maxFontPixels: 20, innerTag: 'div' });
$('#myTextFillContainer2').textfill({ minFontPixels: 5, maxFontPixels: 10, innerTag: 'span' });
<div class="myTextFillContainer1" style="height: 180px;">
  <div>blah blah blah text to be auto-sized</div>
</div>
<div class="myTextFillContainer2" style="height: 120px;">
  <span>blah blah blah another text to be auto-sized</span>
</div>

There are few common options:
minFontPixels: the minimum font size
maxFontPixel: the maximum font size
innerTag: the text wrapper tag
complete: callback when all is done