diff --git a/extension/Classes/Core/Constants.php b/extension/Classes/Core/Constants.php
index f498003662384bfebcd7b9807b3a94000f643ee4..a8cdc1cf1eb9a6a19af9be5c89b4705ccd86c797 100644
--- a/extension/Classes/Core/Constants.php
+++ b/extension/Classes/Core/Constants.php
@@ -960,6 +960,7 @@ const API_SUBMIT_REASON_SAVE_FORCE = 'save,force';
 const API_LOCK_ACTION_LOCK = 'lock';
 const API_LOCK_ACTION_EXTEND = 'extend';
 const API_LOCK_ACTION_RELEASE = 'release';
+const API_LOCK_ACTION_CHECK = 'check';
 
 const API_ANSWER_STATUS_SUCCESS = 'success';
 const API_ANSWER_STATUS_CONFLICT = 'conflict';
diff --git a/extension/Classes/Core/Form/Dirty.php b/extension/Classes/Core/Form/Dirty.php
index 6c4c42dc6f0eaeac9720e34da74dda66873ea60e..59143fd78a158f8fef400742d1c92a8f375460f2 100644
--- a/extension/Classes/Core/Form/Dirty.php
+++ b/extension/Classes/Core/Form/Dirty.php
@@ -117,7 +117,7 @@ class Dirty {
         $recordId = empty($sipVars[SIP_RECORD_ID]) ? 0 : $sipVars[SIP_RECORD_ID];
         if ($recordId == 0) {
             // For r=0 (new) , 'dirty' will always succeed.
-            return [API_STATUS => 'success', API_MESSAGE => ''];
+            return [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
         }
 
         $this->dbIndexQfq = $this->store->getVar(SYSTEM_DB_INDEX_QFQ, STORE_SYSTEM);
@@ -138,6 +138,12 @@ class Dirty {
             case API_LOCK_ACTION_RELEASE:
                 $answer = $this->checkDirtyAndRelease(FORM_SAVE, $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS], $tableVars[F_DIRTY_MODE], $tableVars[F_TABLE_NAME], $tableVars[F_PRIMARY_KEY], $recordId);
                 break;
+            case API_LOCK_ACTION_CHECK:
+                $rcRecordDirty = array();
+                $rcMsg = '';
+                $rc = $this->getCheckDirty($tableVars[F_TABLE_NAME], $recordId, $rcRecordDirty, $rcMsg);
+                $answer = ($rc === LOCK_FOUND_CONFLICT) ? [API_STATUS => API_ANSWER_STATUS_CONFLICT, API_MESSAGE => $rcMsg] : [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
+                break;
             default;
                 throw new \CodeException("Unknown action: " . $this->client[API_LOCK_ACTION], ERROR_DIRTY_UNKNOWN_ACTION);
         }
@@ -178,7 +184,7 @@ class Dirty {
 
         if (count($recordDirty) == 0) {
             if ($formDirtyMode == DIRTY_MODE_NONE) {
-                $answer = [API_STATUS => 'success', API_MESSAGE => ''];
+                $answer = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
             } else {
                 // No dirty record found: create lock
                 $answer = $this->writeDirty($this->client[SIP_SIP], $recordId, $tableVars, $feUser, $rcMd5, $tabUniqId);
@@ -186,7 +192,7 @@ class Dirty {
         } else {
             if ($tabUniqId == $recordDirty[TAB_UNIQ_ID]) {
                 // In case it's the same tab (page reload): OK
-                $answer = [API_STATUS => 'success', API_MESSAGE => ''];
+                $answer = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
             } else {
                 // Here is probably a conflict.
                 $answer = $this->conflict($recordDirty, $formDirtyMode, $primaryKey);
@@ -419,7 +425,7 @@ class Dirty {
 
         // Check if the record has changed in the meantime.
         if ($flagCheckModifiedFirst && $this->isRecordModified($tableName, $primaryKey, $recordId, $this->client[DIRTY_RECORD_HASH_MD5], $dummy)) {
-            throw new \UserFormException ('The record has been modified in the meantime. Please reload the form, edit and save again. [3]', ERROR_DIRTY_RECORD_MODIFIED);
+            throw new \InfoException('The record has been modified in the meantime. Please reload the form, edit and save again. [3]', ERROR_DIRTY_RECORD_MODIFIED);
         }
 
         $lockStatus = $this->getCheckDirty($tableName, $recordId, $rcRecordDirty, $rcMsg);
diff --git a/extension/Classes/Core/QuickFormQuery.php b/extension/Classes/Core/QuickFormQuery.php
index faa84b184846623f21813a4b6304aeb335cd7e72..ac537cb9065593f066fb9048263255a76a2c01f4 100644
--- a/extension/Classes/Core/QuickFormQuery.php
+++ b/extension/Classes/Core/QuickFormQuery.php
@@ -559,7 +559,7 @@ class QuickFormQuery {
             $this->formSpec[F_NAME] = '';
             $this->formSpec[F_TABLE_NAME] = $table;
             $this->formSpec[F_RECORD_LOCK_TIMEOUT_SECONDS] = 1; // just indicate a timeout, the exact timeout is stored in the dirty record.
-            $this->formSpec[F_DIRTY_MODE] = DIRTY_MODE_EXCLUSIVE; // just set a mode,, the exact mode is stored in the dirty record.
+            $this->formSpec[F_DIRTY_MODE] = DIRTY_MODE_EXCLUSIVE; // just set a mode, the exact mode is stored in the dirty record.
             $this->formSpec[F_PRIMARY_KEY] = F_PRIMARY_KEY_DEFAULT;
 
             $tmpDbIndexData = $this->store->getVar(PARAM_DB_INDEX_DATA, STORE_SIP);
diff --git a/javascript/src/Dirty.js b/javascript/src/Dirty.js
index 333332e11516c3a379982fbf3b04605529df55af..1246223810ffc1065931250cb84af81e5e890c6a 100644
--- a/javascript/src/Dirty.js
+++ b/javascript/src/Dirty.js
@@ -43,16 +43,23 @@ var QfqNS = QfqNS || {};
         RENEWAL_ENDED: 'dirty.renewal.ended',
         RENEWAL_SUCCESS: 'dirty.renewal.success',
         RENEWAL_DENIED: 'dirty.renewal.denied',
-        RENEWAL_FAILED: 'dirty.renewal.failed'
+        RENEWAL_FAILED: 'dirty.renewal.failed',
+
+        CHECK_STARTED: 'dirty.check.started',
+        CHECK_SUCCESS: 'dirty.check.success',
+        CHECK_FAILED: 'dirty.check.failed',
+        CHECK_ENDED: 'dirty.check.ended',
     };
 
     n.Dirty.ENDPOINT_OPERATIONS = {
-        /** Aquire Lock */
+        /** Acquire Lock */
         LOCK: "lock",
         /** Release Lock */
         RELEASE: "release",
         /** Renew Lock */
-        RENEW: "extend"
+        RENEW: "extend",
+        /** Check Lock */
+        CHECK: "check"
     };
 
     n.Dirty.MINIMUM_TIMER_DELAY_IN_SECONDS = 5;
@@ -330,4 +337,65 @@ var QfqNS = QfqNS || {};
         queryString = $.param(mergedQueryParameterObject);
         return this.dirtyUrl + "?" + queryString;
     };
+
+    /**
+     * Check with the server if record is already locked.
+     *
+     * @param sip {string} sip.
+     * @public
+     */
+    n.Dirty.prototype.check = function (sip, optionalQueryParameters) {
+        var eventData;
+
+        if (!this.dirtyUrl) {
+            n.Log.debug("notify: cannot contact server, no dirtyUrl.");
+            return;
+        }
+        eventData = n.EventEmitter.makePayload(this, null);
+        this.eventEmitter.emitEvent(n.Dirty.EVENTS.CHECK_STARTED, eventData);
+        $.ajax({
+            url: this.makeUrl(sip, n.Dirty.ENDPOINT_OPERATIONS.CHECK, optionalQueryParameters),
+            type: 'GET',
+            cache: false
+        })
+            .done(this.ajaxCheckSuccessHandler.bind(this))
+            .fail(this.ajaxCheckErrorHandler.bind(this));
+    };
+
+    /**
+     * @private
+     * @param data
+     * @param textStatus
+     * @param jqXHR
+     */
+    n.Dirty.prototype.ajaxCheckSuccessHandler = function (data, textStatus, jqXHR) {
+        var eventData = n.EventEmitter.makePayload(this, data);
+        if (data.status && data.status === "success") {
+            this.eventEmitter.emitEvent(n.Dirty.EVENTS.CHECK_SUCCESS, eventData);
+        } else {
+            this.eventEmitter.emitEvent(n.Dirty.EVENTS.CHECK_FAILED, eventData);
+        }
+
+        this.setTimeoutIfRequired(data, eventData);
+        this.eventEmitter.emitEvent(n.Dirty.EVENTS.CHECK_ENDED, eventData);
+    };
+
+    /**
+     * @private
+     * @param jqXHR
+     * @param textStatus
+     * @param errorThrown
+     */
+    n.Dirty.prototype.ajaxCheckErrorHandler = function (jqXHR, textStatus, errorThrown) {
+        var eventData = n.EventEmitter.makePayload(this, null, {
+            textStatus: textStatus,
+            errorThrown: errorThrown,
+            jqXHR: jqXHR
+        });
+
+        this.lockTimeoutInMilliseconds = n.Dirty.NO_LOCK_TIMEOUT;
+
+        this.eventEmitter.emitEvent(n.Dirty.EVENTS.CHECK_FAILED, eventData);
+        this.eventEmitter.emitEvent(n.Dirty.EVENTS.CHECK_ENDED, eventData);
+    };
 })(QfqNS);
diff --git a/javascript/src/QfqForm.js b/javascript/src/QfqForm.js
index 105fabeb363343928568cb51a35ec1111e11e4e2..8536cf9e81811305b5c93eb1e6e8d4cca0a54650 100644
--- a/javascript/src/QfqForm.js
+++ b/javascript/src/QfqForm.js
@@ -22,7 +22,7 @@ var QfqNS = QfqNS || {};
     /**
      * Represents a QFQ Form.
      *
-     * QfqForm will autonomously fire an lock `extend` request when the lock expired, but the last change `t_c` has
+     * QfqForm will autonomously fire a lock `extend` request when the lock expired, but the last change `t_c` has
      * been made during the lock period `t_l`. I.e. let `t_{current}` be the current time, an `extend` request is made
      * when
      *
@@ -95,7 +95,8 @@ var QfqNS = QfqNS || {};
         this.dirty.on(n.Dirty.EVENTS.SUCCESS_TIMEOUT, this.dirtyTimeout.bind(this));
         this.dirty.on(n.Dirty.EVENTS.RENEWAL_DENIED, this.dirtyRenewalDenied.bind(this));
         this.dirty.on(n.Dirty.EVENTS.RENEWAL_SUCCESS, this.dirtyRenewalSuccess.bind(this));
-
+        this.dirty.on(n.Dirty.EVENTS.CHECK_SUCCESS, this.dirtyCheckSuccess.bind(this));
+        this.dirty.on(n.Dirty.EVENTS.CHECK_FAILED, this.dirtyCheckFailed.bind(this));
 
         this.form.on('form.changed', this.changeHandler.bind(this));
         this.form.on('form.reset', this.resetHandler.bind(this));
@@ -172,6 +173,8 @@ var QfqNS = QfqNS || {};
                 checkboxes[i].checked = $(this).is(':checked');
             }
         });
+
+        this.dirty.check(this.getSip(), this.getRecordHashMd5AsQueryParameter());
     };
 
     n.QfqForm.prototype.on = n.EventEmitter.onMixin;
@@ -181,6 +184,24 @@ var QfqNS = QfqNS || {};
         // Intentionally empty. May be used later on.
     };
 
+    n.QfqForm.prototype.dirtyCheckSuccess = function (obj) {
+        // Intentionally empty. May be used later on.
+    };
+
+    n.QfqForm.prototype.dirtyCheckFailed = function (obj) {
+        var message = new n.Alert({
+            message: obj.data.message,
+            type: "error",
+            timeout: n.Alert.constants.NO_TIMEOUT,
+            modal: true,
+            buttons: [{
+                label: "Close",
+                eventName: 'close'
+            }]
+        });
+        message.show();
+    };
+
     n.QfqForm.prototype.dirtyRenewalSuccess = function (obj) {
         this.lockAcquired = true;
     };
@@ -628,12 +649,12 @@ var QfqNS = QfqNS || {};
         this.lastButtonPress = "close";
         if (this.form.getFormChanged()) {
             var alert = new n.Alert({
-                message: "You have unsaved changes. Do you want to save first?",
+                message: "Unsaved Changes.",
                 type: "warning",
                 modal: true,
                 buttons: [
-                    {label: "Yes", eventName: "yes"},
-                    {label: "No", eventName: "no", focus: true},
+                    {label: "Save", eventName: "yes"},
+                    {label: "Discard", eventName: "no", focus: true},
                     {label: "Cancel", eventName: "cancel"}
                 ]
             });
@@ -735,12 +756,12 @@ var QfqNS = QfqNS || {};
         this.lastButtonPress = "new";
         if (this.form.getFormChanged()) {
             var alert = new n.Alert({
-                message: "You have unsaved changes. Do you want to save first?",
+                message: "Unsaved Changes.",
                 type: "warning",
                 modal: true,
                 buttons: [
-                    {label: "Yes", eventName: "yes", focus: true},
-                    {label: "No", eventName: "no"},
+                    {label: "Save", eventName: "yes", focus: true},
+                    {label: "Discard", eventName: "no"},
                     {label: "Cancel", eventName: "cancel"}
                 ]
             });