Skip to content
Snippets Groups Projects
ticket.js 11 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jared Hancock's avatar
    Jared Hancock committed
    /*********************************************************************
        ticket.js
    
        Ticket utility loaded on ticket view!
    
        Useful for UI config settings, ticket locking ...etc
    
        Peter Rotich <peter@osticket.com>
    
        Copyright (c) 2006-2013 osTicket
    
    Jared Hancock's avatar
    Jared Hancock committed
        http://www.osticket.com
    
        Released under the GNU General Public License WITHOUT ANY WARRANTY.
    
        vim: expandtab sw=4 ts=4 sts=4:
    **********************************************************************/
    
    +function( $ ) {
      var Lock = function(element, options) {
        this.$element = $(element);
        this.options = $.extend({}, $.fn.exclusive.defaults, options);
        if (!this.$element.data('lockObjectId'))
          return;
        this.objectId = this.$element.data('lockObjectId');
        this.lockId = options.lockId || this.$element.data('lockId') || undefined;
        this.fails = 0;
    
        this.disabled = false;
        getConfig().then(function(c) {
          if (c.lock_time)
            this.setup();
        }.bind(this));
    
      }
    
      Lock.prototype = {
        constructor: Lock,
    
        setup: function() {
          // When something inside changes or is clicked which requires a lock,
          // attempt to fetch one (lazily)
          $(':input', this.$element).on('keyup, change', this.acquire.bind(this));
          $(':submit', this.$element).click(this.ensureLocked.bind(this));
    
          $(document).on('pjax:start', this.shutdown.bind(this));
    
    
          // If lock already held, assume full time of lock remains, but warn
          // user about pending expiration
          if (this.lockId) {
            getConfig().then(function(c) {
              this.lockTimeout(c.lock_time - 20);
            }.bind(this));
          }
    
        acquire: function() {
          if (this.lockId)
            return this.renew();
          if (this.nextRenew && new Date().getTime() < this.nextRenew)
            return this.locked;
    
          if (this.disabled || this.ajaxActive)
    
            return this.locked;
    
          this.ajaxActive = $.ajax({
            type: "POST",
            url: 'ajax.php/lock/'+this.objectId,
            dataType: 'json',
            cache: false,
            success: $.proxy(this.update, this),
            error: $.proxy(this.retry, this, this.acquire),
            complete: $.proxy(function() { this.ajaxActive = false; }, this)
          });
          return this.locked = $.Deferred();
    
        renew: function() {
          if (!this.lockId)
            return;
          if (this.nextRenew && new Date().getTime() < this.nextRenew)
            return this.locked;
    
          if (this.disabled || this.ajaxActive)
    
            return this.locked;
    
          this.ajaxActive = $.ajax({
            type: "POST",
            url: 'ajax.php/lock/{0}/{1}/renew'.replace('{0}',this.lockId).replace('{1}',this.objectId),
            dataType: 'json',
            cache: false,
            success: $.proxy(this.update, this),
            error: $.proxy(this.retry, this, this.renew),
            complete: $.proxy(function() { this.ajaxActive = false; }, this)
          });
          return this.locked = $.Deferred();
    
        wakeup: function(e) {
          // Click handler from message bar. Bar will be manually hidden when
          // lock is re-acquired
          this.renew();
          return false;
    
        retry: function(func, xhr, textStatus, response) {
          var json = xhr ? xhr.responseJSON : response;
    
    
          if (xhr.status == 418) {
              this.disabled = true;
              return this.destroy();
          }
    
    
          if ((typeof json == 'object' && !json.retry) || !this.options.retry)
            return this.fail(json.msg);
          if (typeof json == 'object' && json.retry == 'acquire') {
            // Lock no longer exists server-side
            this.destroy();
            setTimeout(this.acquire.bind(this), 2);
          }
          if (++this.fails > this.options.maxRetries)
            // Attempt to acquire a new lock ?
            return this.fail(json ? json.msg : null);
          this.retryTimer = setTimeout($.proxy(func, this), this.options.retryInterval * 1000);
    
        release: function() {
          if (!this.lockId)
            return false;
          if (this.ajaxActive)
            this.ajaxActive.abort();
    
          $.ajax({
            type: 'POST',
            url: 'ajax.php/lock/{0}/release'.replace('{0}', this.lockId),
            data: 'delete',
            async: false,
            cache: false,
    
            complete: this.destroy.bind(this)
    
        shutdown: function() {
    
          clearTimeout(this.warning);
          clearTimeout(this.retryTimer);
    
          $(document).off('.exclusive');
        },
    
        destroy: function() {
          this.shutdown();
    
          delete this.lockId;
          $(this.options.lockInput, this.$element).val('');
    
          if (this.locked)
            this.locked.reject();
    
        update: function(lock) {
          if (typeof lock != 'object' || lock.retry === true) {
            // Non-json response, or retry requested server-side
            return this.retry(this.renew, this.activeAjax, false, lock);
          }
          if (!lock.id) {
            // Response did not include a lock id number
            return this.fail(lock.msg);
          }
          if (!this.lockId) {
            // Set up release on away navigation
    
            $(document).off('.exclusive');
            $(document).on('pjax:click.exclusive', $.proxy(this.release, this));
    
          }
    
          this.lockId = lock.id;
          this.fails = 0;
          $.messageBar.hide();
          this.errorBar = false;
    
          // If there is an input with the name 'lockCode', then set the value
          // to the lock.code retrieved (if any)
          if (lock.code)
            $(this.options.lockInput, this.$element).val(lock.code);
    
          // Deadband renew to every 30 seconds
          this.nextRenew = new Date().getTime() + 30000;
    
          // Warn 10 seconds before expiration
          this.lockTimeout(lock.time - 10);
    
          if (this.locked)
            this.locked.resolve(lock);
    
        lockTimeout: function(time) {
          if (this.warning)
            clearTimeout(this.warning);
          this.warning = setTimeout(this.warn.bind(this), time * 1000);
    
        ensureLocked: function(e) {
          // Make sure a lock code has been fetched first
          if (!$(this.options.lockInput, this.$element).val()) {
            var $target = $(e.target),
                text = $target.text() || $target.val();
            $target.prop('disabled', true).text(__('Acquiring Lock')).val(__('Acquiring Lock'));
            this.acquire().always(function(lock) {
              $target.text(text).val(text).prop('disabled', false);
              if (typeof lock == 'object' && lock.code)
                $target.trigger(e.type, e);
            }.bind(this));
            return false;
          }
    
        warn: function() {
          $.messageBar.show(
            __('Your lock is expiring soon.'),
            __('The lock you hold on this ticket will expire soon. Would you like to renew the lock?'),
            {onok: this.wakeup.bind(this), buttonText: __("Renew")}
          ).addClass('warning');
    
        fail: function(msg) {
          // Don't retry for 5 seconds
          this.nextRenew = new Date().getTime() + 5000;
          // Resolve anything awaiting
          if (this.locked)
            this.locked.rejectWith(msg);
          // No longer locked
          this.destroy();
          // Flash the error bar if it's already on the screen
          if (this.errorBar && $.messageBar.visible)
              return this.errorBar.effect('highlight');
          // Add the error bar to the screen
          this.errorBar = $.messageBar.show(
            msg || __('Unable to lock the ticket.'),
            __('Someone else could be working on the same ticket.'),
            {avatar: 'oscar-borg', buttonClass: 'red', dismissible: true}
          ).addClass('danger');
        }
      };
    
      $.fn.exclusive = function ( option ) {
        return this.each(function () {
          var $this = $(this),
            data = $this.data('exclusive'),
            options = typeof option == 'object' && option;
          if (!data) $this.data('exclusive', (data = new Lock(this, options)));
          if (typeof option == 'string') data[option]();
        });
      };
    
      $.fn.exclusive.defaults = {
        lockInput: 'input[name=lockCode]',
        maxRetries: 2,
        retry: true,
        retryInterval: 2
      };
    
      $.fn.exclusive.Constructor = Lock;
    
    }(window.jQuery);
    
    Jared Hancock's avatar
    Jared Hancock committed
    
    /*
       UI & form events
    */
    
    $.showImagesInline = function(urls, thread_id) {
        var selector = (thread_id == undefined)
            ? '.thread-body img[data-cid]'
    
            : '.thread-body#thread-entry-'+thread_id+' img[data-cid]';
    
        $(selector).each(function(i, el) {
            var e = $(el),
                cid = e.data('cid').toLowerCase(),
                info = urls[cid];
            if (info && !e.data('wrapped')) {
                // Add a hover effect with the filename
                var timeout, caption = $('<div class="image-hover">')
                    .css({'float':e.css('float')});
                e.wrap(caption).parent()
                    .hover(
                        function() {
                            var self = this;
                            timeout = setTimeout(
                                function() { $(self).find('.caption').slideDown(250); },
                                500);
                        },
                        function() {
                            clearTimeout(timeout);
                            $(this).find('.caption').slideUp(250);
                        }
                    ).append($('<div class="caption">')
                        .append('<span class="filename">'+info.filename+'</span>')
    
                        .append($('<a href="'+info.download_url+'" class="action-button pull-right no-pjax"><i class="icon-download-alt"></i> '+__('Download')+'</a>')
                          .attr('download', info.filename)
                        )
    
                    );
                e.data('wrapped', true);
            }
        });
    };
    
    $.refreshTicketView = function(interval) {
    
        var refresh = setInterval(function() {
    
          if ($('table.list input.ckb[name=tids\\[\\]]:checked').length)
            // Skip the refresh b/c items are checked
            return;
          else if (0 < $('.dialog:visible').length)
            // Dialog open — skip refresh
            return;
    
          clearInterval(refresh);
          $.pjax({url: document.location.href, container:'#pjax-container'});
        }, interval);
    
        $(document).on('pjax:start', function() {
            clearInterval(refresh);
        });
    
    var ticket_onload = function($) {
    
        if (0 === $('#ticketThread').length)
    
        $(function(){$('.exclusive[data-lock-object-id]').exclusive();});
    
    Peter Rotich's avatar
    Peter Rotich committed
    
        /*** Ticket Actions **/
    
    Peter Rotich's avatar
    Peter Rotich committed
        //print options TODO: move to backend
    
        $('a#ticket-print').click(function(e) {
            e.preventDefault();
            $('#overlay').show();
    
    Peter Rotich's avatar
    Peter Rotich committed
            $('.dialog#print-options').show();
    
    Peter Rotich's avatar
    Peter Rotich committed
    
        $(document).off('.ticket-action');
        $(document).on('click.ticket-action', 'a.ticket-action', function(e) {
    
    Peter Rotich's avatar
    Peter Rotich committed
            e.preventDefault();
    
    Peter Rotich's avatar
    Peter Rotich committed
            var url = 'ajax.php/'
            +$(this).attr('href').substr(1)
            +'?_uid='+new Date().getTime();
    
    Peter Rotich's avatar
    Peter Rotich committed
            var $redirect = $(this).data('href');
    
            var $options = $(this).data('dialog');
    
    Peter Rotich's avatar
    Peter Rotich committed
            $.dialog(url, [201], function (xhr) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                window.location.href = $redirect ? $redirect : window.location.href;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            return false;
        });
    
        $(document).on('change', 'form[name=reply] select#emailreply', function(e) {
             var $cc = $('form[name=reply] tbody#cc_sec');
    
            if($(this).val() == 0)
                $cc.hide();
            else
                $cc.show();
         });
    
    
    };
    $(ticket_onload);
    $(document).on('pjax:success', function() { ticket_onload(jQuery); });