Commit 9ba4f397 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Dirty.php, QuickFormQuery.php: getCheckDirty() implemented.

parent 0a92783b
......@@ -258,6 +258,8 @@ const ERROR_SUBSTITUTE_FOUND = 2100;
const ERROR_DIRTY_MISSING_FORM_IN_SIP = 2200;
const ERROR_DIRTY_DELETE_RECORD = 2201;
const ERROR_DIRTY_UNKNOWN_ACTION = 2202;
const ERROR_DIRTY_MISSING_LOCK = 2203;
const ERROR_DIRTY_ALREADY_LOCKED = 2204;
//
// Store Names: Identifier
......@@ -1118,3 +1120,6 @@ const DIRTY_API_ACTION = 'action'; // Name of parameter in API call of dirty.ph
const DIRTY_API_ACTION_LOCK = 'lock';
const DIRTY_API_ACTION_RELEASE = 'release';
const DIRTY_API_ACTION_EXTEND = 'extend';
const LOCK_NOT_FOUND = 0;
const LOCK_FOUND_OWNER = 1;
const LOCK_FOUND_CONFLICT = 2;
\ No newline at end of file
......@@ -266,11 +266,22 @@ class QuickFormQuery {
$this->fillStoreWithRecord($this->formSpec[F_TABLE_NAME], $recordId, STORE_BEFORE);
}
// Check and release dirtyRecord.
// Check (and release) dirtyRecord.
if ($formMode === FORM_DELETE || $formMode === FORM_SAVE) {
$recordDirty = array();
$dirty = new Dirty();
$dirty->checkDirtyAndRelease($formMode, $this->formSpec[F_RECORD_LOCK_TIMEOUT_SECONDS],
$this->formSpec[F_DIRTY_MODE], $this->formSpec[F_TABLE_NAME], $recordId);
// $dirty->checkDirtyAndRelease($formMode, $this->formSpec[F_RECORD_LOCK_TIMEOUT_SECONDS],
// $this->formSpec[F_DIRTY_MODE], $this->formSpec[F_TABLE_NAME], $recordId);
$lockStatus = $dirty->getCheckDirty( $this->formSpec[F_TABLE_NAME], $recordId, $recordDirty, $msg);
switch($lockStatus) {
case LOCK_NOT_FOUND:
throw new UserFormException("Missing record lock: please reload the form, edit and save again.", ERROR_DIRTY_MISSING_LOCK);
case LOCK_FOUND_CONFLICT:
throw new UserFormException($msg, ERROR_DIRTY_ALREADY_LOCKED);
case LOCK_FOUND_OWNER:
break;
default:
}
}
if ($formMode === FORM_DELETE) {
......
......@@ -87,7 +87,6 @@ class Dirty {
}
return $answer;
}
/**
......@@ -146,7 +145,6 @@ class Dirty {
ROW_EXPECT_0_1, [$tableName, $recordId]);
return $recordDirty;
}
/**
......@@ -160,7 +158,7 @@ class Dirty {
$at = "at " . $recordDirty[COLUMN_CREATED] . " from " . $recordDirty[DIRTY_REMOTE_ADDRESS];
// Compare modified timestamp
if(false==$this->checkModified($recordDirty[DIRTY_TABLE_NAME],$recordDirty[DIRTY_RECORD_ID], $recordDirty[DIRTY_RECORD_MODIFIED])){
if (false == $this->checkModified($recordDirty[DIRTY_TABLE_NAME], $recordDirty[DIRTY_RECORD_ID], $recordDirty[DIRTY_RECORD_MODIFIED])) {
return [API_STATUS => API_ANSWER_STATUS_CONFLICT, API_MESSAGE => 'The record has been modified in the meantime. Please reload the form, edit and save again.'];
}
......@@ -237,13 +235,48 @@ class Dirty {
$record = $this->db->sql("SELECT * FROM $tableName WHERE id=?", ROW_EXPECT_1, [$recordId], "Record to lock not found.");
if(isset($record[COLUMN_MODIFIED])) {
$status = ($modified==$record[COLUMN_MODIFIED]) ? true : false;
if (isset($record[COLUMN_MODIFIED])) {
$status = ($modified == $record[COLUMN_MODIFIED]) ? true : false;
}
return $status;
}
/**
* Check if a lock exist for the current table, recordId and session.
*
* @param string $tableName
* @param int $recordId
* @param array $recordDirty - return dirty record if one exist.
* @param string $msg - return preformatted message in case of conflict
* @return int LOCK_NOT_FOUND | LOCK_FOUND_OWNER | LOCK_FOUND_CONFLICT,
*/
public function getCheckDirty($tableName, $recordId, array &$recordDirty, &$msg) {
$msg = '';
if ($recordId == 0) {
return LOCK_NOT_FOUND; // New records never have a recordDirty nor a conflict.
}
$recordDirty = $this->getRecordDirty($tableName, $recordId);
if (empty($recordDirty)) {
return LOCK_NOT_FOUND;
}
// Is the dirtyRecord mine?
if ($recordDirty[DIRTY_QFQ_USER_SESSION_COOKIE] == $this->client[CLIENT_COOKIE_QFQ]) {
return LOCK_FOUND_OWNER;
} else {
$msgUser = (empty($recordDirty[DIRTY_FE_USER])) ? "another user" : "user '$recordDirty[DIRTY_FE_USER]'";
$msgAt = "at " . $recordDirty[COLUMN_CREATED] . " from " . $recordDirty[DIRTY_REMOTE_ADDRESS];
$msg = "The record has been locked by $msgUser $msgAt";
return LOCK_FOUND_CONFLICT;
}
}
/**
* Release a dirtyRecord. This is only possible if the current user owns the dirtyRecord.
* In case of not owner, throws an exception and the save should break.
......@@ -259,41 +292,39 @@ class Dirty {
*/
public function checkDirtyAndRelease($formMode, $lockTimeout, $dirtyMode, $tableName, $recordId) {
$recordDirty = array();
$msg = '';
$answer = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
if ($recordId == 0) {
return $answer; // New records never have a recordDirty nor a conflict.
}
$recordDirty = $this->getRecordDirty($tableName, $recordId);
if (empty($recordDirty) && $dirtyMode == DIRTY_MODE_NONE) {
return $answer; // only situation where it's ok that there is no dirtyRecord.
}
// $recordDirty = $this->getRecordDirty($tableName, $recordId);
$lockStatus = $this->getCheckDirty($tableName, $recordId, $recordDirty, $msg);
if (empty($recordDirty)) {
if ($dirtyMode == DIRTY_MODE_NONE) {
return $answer; // only situation where it's ok that there is no dirtyRecord.
}
if ($formMode == FORM_DELETE) {
return $answer;
}
// This is pessimistic, but secure.
throw new UserFormException("Missing record lock: please reload the form, edit and save again.");
throw new UserFormException("Missing record lock: please reload the form, edit and save again.", ERROR_DIRTY_MISSING_LOCK);
}
$msgUser = (empty($recordDirty[DIRTY_FE_USER])) ? "another user" : "user '$recordDirty[DIRTY_FE_USER]'";
$msgAt = "at " . $recordDirty[COLUMN_CREATED] . " from " . $recordDirty[DIRTY_REMOTE_ADDRESS];
if ($formMode == FORM_DELETE) {
throw new UserFormException("The record has been locked by $msgUser $msgAt");
throw new UserFormException($msg, ERROR_DIRTY_ALREADY_LOCKED);
}
// Is the dirtyRecord mine?
if ($recordDirty[DIRTY_QFQ_USER_SESSION_COOKIE] == $this->client[CLIENT_COOKIE_QFQ]) {
if(false==$this->checkModified($tableName, $recordId, $recordDirty[DIRTY_RECORD_MODIFIED])){
return [API_STATUS => API_ANSWER_STATUS_CONFLICT, API_MESSAGE => 'The record has been modified in the meantime. Please reload the form, edit and save again.'];
}
if ($lockStatus == LOCK_FOUND_OWNER) {
// if (false == $this->checkModified($tableName, $recordId, $recordDirty[DIRTY_RECORD_MODIFIED])) {
// return [API_STATUS => API_ANSWER_STATUS_CONFLICT, API_MESSAGE => 'The record has been modified in the meantime. Please reload the form, edit and save again.'];
// }
// Clear the lock
$this->deleteDirtyRecord($recordDirty[COLUMN_ID]);
......@@ -309,15 +340,12 @@ class Dirty {
}
// Check if the record is timed out
if ($lockTimeout > 0) {
$datetimeExpire = date_add(date_create($recordDirty[COLUMN_MODIFIED]), date_interval_create_from_date_string($lockTimeout . " second"));
if ($datetimeExpire <= date_create("now")) {
$this->deleteDirtyRecord($recordDirty[COLUMN_ID]);
return $answer;
}
if ($lockTimeout > 0 && $recordDirty[DIRTY_EXPIRE] < date('Y-m-d H:i:s')) {
$this->deleteDirtyRecord($recordDirty[COLUMN_ID]);
return $answer;
}
throw new UserFormException("Save not possible - the record has been locked by $msgUser $msgAt");
throw new UserFormException($msg, ERROR_DIRTY_ALREADY_LOCKED);
}
/**
......@@ -328,6 +356,7 @@ class Dirty {
* @throws DbException
*/
private function deleteDirtyRecord($recordDirtyId) {
$cnt = $this->db->sql('DELETE FROM Dirty WHERE id=? LIMIT 1', ROW_REGULAR, [$recordDirtyId]);
if ($cnt != 1) {
throw new CodeException("Failed to delete dirty record id=" . $recordDirtyId, ERROR_DIRTY_DELETE_RECORD);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment