diff --git a/include/ajax.config.php b/include/ajax.config.php
index 0bf0a4420411304e02d14ae639f450ddb6bdbda6..132da3daf9de1003d3c5625b723880430416f714 100644
--- a/include/ajax.config.php
+++ b/include/ajax.config.php
@@ -38,7 +38,7 @@ class ConfigAjaxAPI extends AjaxController {
         list($primary_sl, $primary_locale) = explode('_', $primary);
 
         $config=array(
-              'lock_time'       => ($cfg->getLockTime()*60),
+              'lock_time'       => $cfg->getTicketLockMode() == Lock::MODE_DISABLED ? 0 : ($cfg->getLockTime()*60),
               'html_thread'     => (bool) $cfg->isRichTextEnabled(),
               'date_format'     => $cfg->getDateFormat(true),
               'lang'            => $lang,
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index 30a8b74d74075d8e044f80d5a07ac733adfbc94e..31457ec4f80497849129f1a5f126ac7d8a4c21fb 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -92,7 +92,7 @@ class TicketsAjaxAPI extends AjaxController {
     function acquireLock($tid) {
         global $cfg, $thisstaff;
 
-        if(!$cfg || !$cfg->getLockTime())
+        if(!$cfg || !$cfg->getLockTime() || $cfg->getTicketLockMode() == Lock::MODE_DISABLED)
             Http::response(418, $this->encode(array('id'=>0, 'retry'=>false)));
 
         if(!$tid || !is_numeric($tid) || !$thisstaff)
diff --git a/include/class.config.php b/include/class.config.php
index 984a83c1619b23f1200622432333fedb5c898bf7..b3c75485c1ed9d8862656cb4e9c2e8ea1d1dc20f 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -176,6 +176,7 @@ class OsticketConfig extends Config {
         'verify_email_addrs' => 1,
         'client_avatar' => 'gravatar.mm',
         'agent_avatar' => 'gravatar.mm',
+        'ticket_lock' => 2, // Lock on activity
     );
 
     function OsticketConfig($section=null) {
@@ -424,6 +425,10 @@ class OsticketConfig extends Config {
         return $this->get('autolock_minutes');
     }
 
+    function getTicketLockMode() {
+        return $this->get('ticket_lock');
+    }
+
     function getAgentNameFormat() {
         return $this->get('agent_name_format');
     }
@@ -1204,6 +1209,7 @@ class OsticketConfig extends Config {
             'show_related_tickets'=>isset($vars['show_related_tickets'])?1:0,
             'hide_staff_name'=>isset($vars['hide_staff_name'])?1:0,
             'allow_client_updates'=>isset($vars['allow_client_updates'])?1:0,
+            'ticket_lock' => $vars['ticket_lock'],
         ));
     }
 
diff --git a/include/class.lock.php b/include/class.lock.php
index d1c5bc3655748f72ff16c092657f32e74c843365..e5da2f048e894bd60733254a0604a5ae51c6fcf9 100644
--- a/include/class.lock.php
+++ b/include/class.lock.php
@@ -38,6 +38,10 @@ class Lock extends VerySimpleModel {
         ),
     );
 
+    const MODE_DISABLED = 0;
+    const MODE_ON_VIEW = 1;
+    const MODE_ON_ACTIVITY = 2;
+
     function getId() {
         return $this->lock_id;
     }
diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php
index 8edf21818a2268df33dfbfea2c977ede1afea26f..4fe241476415916aac3d6295ee943fd12ef6caf9 100644
--- a/include/staff/settings-tickets.inc.php
+++ b/include/staff/settings-tickets.inc.php
@@ -145,6 +145,23 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
                 <span class="error"><?php echo $errors['default_help_topic']; ?></span>
             </td>
         </tr>
+        <tr>
+            <td width="180"><?php echo __('Lock Semantics'); ?>:</td>
+            <td>
+                <select name="ticket_lock" <?php if ($cfg->getLockTime() == 0) echo 'disabled="disabled"'; ?>>
+<?php foreach (array(
+    Lock::MODE_DISABLED => __('Disabled'),
+    Lock::MODE_ON_VIEW => __('Lock on view'),
+    Lock::MODE_ON_ACTIVITY => __('Lock on activity'),
+) as $v => $desc) { ?>
+                <option value="<?php echo $v; ?>" <?php
+                    if ($config['ticket_lock'] == $v) echo 'selected="selected"';
+                    ?>><?php echo $desc; ?></option>
+<?php } ?>
+                </select>
+                <div class="error"><?php echo $errors['ticket_lock']; ?></div>
+            </td>
+        </tr>
         <tr>
             <td><?php echo __('Maximum <b>Open</b> Tickets');?>:</td>
             <td>
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 6ec03ae8992c1da3ac8d7bd6aa08a789c5902d93..156b2c323556a57f4de5771baae88a64eb123fc0 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -16,6 +16,8 @@ $user  = $ticket->getOwner(); //Ticket User (EndUser)
 $team  = $ticket->getTeam();  //Assigned team.
 $sla   = $ticket->getSLA();
 $lock  = $ticket->getLock();  //Ticket lock obj
+if (!$lock && $cfg->getTicketLockMode() == Lock::MODE_ON_VIEW)
+    $lock = $ticket->acquireLock($thisstaff->getId());
 $mylock = ($lock && $lock->getStaffId() == $thisstaff->getId()) ? $lock : null;
 $id    = $ticket->getId();    //Ticket ID.
 
diff --git a/scp/js/ticket.js b/scp/js/ticket.js
index 61ba8c7b84f9364b1863042dd32a4986dacbf598..b16cc8ea386dca8cf6931a9bd1e1b6b81aabeaf9 100644
--- a/scp/js/ticket.js
+++ b/scp/js/ticket.js
@@ -20,30 +20,29 @@
     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();
+        this.setup(options.lockId || this.$element.data('lockId') || undefined);
     }.bind(this));
   }
 
   Lock.prototype = {
     constructor: Lock,
+    registry: [],
 
-    setup: function() {
+    setup: function(lockId) {
       // 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) {
+      if (lockId) {
         getConfig().then(function(c) {
-          this.lockTimeout(c.lock_time - 20);
+          this.update({id: lockId, time: c.lock_time - 10});
         }.bind(this));
       }
     },
@@ -126,12 +125,20 @@
         type: 'POST',
         url: 'ajax.php/lock/{0}/release'.replace('{0}', this.lockId),
         data: 'delete',
-        async: false,
         cache: false,
+        success: this.clearAll.bind(this),
         complete: this.destroy.bind(this)
       });
     },
 
+    clearAll: function() {
+      // Clear all other current locks with the same ID as this
+      $.each(Lock.prototype.registry, function(i, l) {
+        if (l.lockId && l.lockId == this.lockId)
+          l.shutdown();
+      }.bind(this));
+    },
+
     shutdown: function() {
       clearTimeout(this.warning);
       clearTimeout(this.retryTimer);
@@ -159,6 +166,7 @@
         // Set up release on away navigation
         $(document).off('.exclusive');
         $(document).on('pjax:click.exclusive', $.proxy(this.release, this));
+        Lock.prototype.registry.push(this);
       }
 
       this.lockId = lock.id;