Commit ecce620b authored by Carsten  Rose's avatar Carsten Rose
Browse files

recordLocking

file.php, load.php, Html2Pdf.php: minor updates.
Dirty.php, QuickFormQuery.php: optional $lockTimeout as parameter during instantiating. Return $lockTimeout where necessary. Otherwise read qfq.config.ini. Refactor process(), acquireDirty(), checkDirtyAndRelease().
parent 5c92927d
......@@ -39,7 +39,7 @@ $answer[API_STATUS] = API_ANSWER_STATUS_ERROR;
try {
$fileUpload = new \qfq\File();
$fileUpload = new File();
$fileUpload->process();
......
......@@ -6,14 +6,13 @@
* Time: 6:17 PM
*/
namespace qfq;
use qfq;
require_once(__DIR__ . '/../qfq/QuickFormQuery.php');
require_once(__DIR__ . '/../qfq/store/Store.php');
require_once(__DIR__ . '/../qfq/Constants.php');
require_once(__DIR__ . '/../qfq/QuickFormQuery.php');
/**
......@@ -57,7 +56,7 @@ $answer[API_STATUS] = API_ANSWER_STATUS_ERROR;
$answer[API_MESSAGE] = '';
try {
$qfq = new \qfq\QuickFormQuery(['bodytext' => ""]);
$qfq = new QuickFormQuery(['bodytext' => '']);
$data = $qfq->updateForm();
......
......@@ -575,6 +575,8 @@ const API_STATUS = 'status';
const API_MESSAGE = 'message';
const API_REDIRECT = 'redirect';
const API_REDIRECT_URL = 'redirect-url';
const API_LOCK_ACTION = 'action';
const API_LOCK_TIMEOUT = 'lock_timeout';
const API_FIELD_NAME = 'field-name';
const API_FIELD_MESSAGE = 'field-message';
const API_FORM_UPDATE = 'form-update';
......@@ -582,6 +584,9 @@ const API_ELEMENT_UPDATE = 'element-update';
const API_ELEMENT_ATTRIBUTE = 'attr';
const API_ELEMENT_CONTENT = 'content';
const API_LOCK_ACTION_LOCK = 'lock';
const API_LOCK_ACTION_RELEASE = 'release';
const API_JSON_HIDDEN = 'hidden';
const API_JSON_DISABLED = 'disabled';
const API_JSON_REQUIRED = 'required';
......
......@@ -6,7 +6,6 @@
* Time: 6:33 PM
*/
namespace qfq;
use qfq;
......@@ -269,9 +268,10 @@ class QuickFormQuery {
// Check and release dirtyRecord.
if ($formMode === FORM_DELETE || $formMode === FORM_SAVE) {
$dirty = new Dirty();
$timeOutDurationSeconds = $this->store->getVar(STORE_SYSTEM, SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS);
$dirty->checkDirtyAndRelease($formMode, $this->formSpec[F_DIRTY_MODE], $this->formSpec[F_TABLE_NAME], $recordId, $timeOutDurationSeconds);
$lockTimeout = $this->store->getVar(STORE_SYSTEM, SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS);
$dirty = new Dirty($lockTimeout);
$dirty->checkDirtyAndRelease($formMode, $this->formSpec[F_DIRTY_MODE], $this->formSpec[F_TABLE_NAME],
$recordId);
}
if ($formMode === FORM_DELETE) {
......
......@@ -33,15 +33,26 @@ class Dirty {
*/
private $session = null;
/**
* @var array()
*/
private $lockTimeout = false;
/**
*
*/
public function __construct($phpUnit = false) {
public function __construct($lockTimeout = false, $phpUnit = false) {
$this->session = Session::getInstance($phpUnit);
$this->client = Client::getParam();
$this->db = new Database();
if($lockTimeout === false) {
$cfg = new Config();
$config = $cfg->readConfig('');
$this->lockTimeout = $config[SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS];
} else {
$this->lockTimeout = $lockTimeout;
}
}
/**
......@@ -51,40 +62,55 @@ class Dirty {
* @throws UserFormException
*/
public function process() {
$answer = array();
$tableVars = array();
$sipClass = new Sip();
$sipVars = $sipClass->getVarsFromSip($this->client[SIP_SIP]);
return $this->acquireDirty($sipVars);
if (empty($sipVars[SIP_FORM])) {
throw new CodeException("Missing 'form' in SIP. There might be something broken.", ERROR_MISSING_FORM_IN_SIP);
}
$recordId = empty($sipVars[SIP_RECORD_ID]) ? 0 : $sipVars[SIP_RECORD_ID];
if($recordId>0) {
$tableVars = $this->db->sql("SELECT tableName, dirtyMode FROM Form AS f WHERE f.name=?", ROW_EXPECT_1, [$sipVars[SIP_FORM]], "Form not found: '" . $sipVars[SIP_FORM] . "'");
}
switch($this->client[API_LOCK_ACTION]) {
case API_LOCK_ACTION_LOCK:
$answer = $this->acquireDirty($recordId, $tableVars);
break;
case API_LOCK_ACTION_RELEASE:
$answer = $this->checkDirtyAndRelease(FORM_SAVE, $tableVars[F_DIRTY_MODE], $tableVars[F_TABLE_NAME], $recordId);
break;
default;
}
return $answer;
}
/**
* Tries to get a 'DirtyRecord'. Returns an array about success or failure.
*
* @param array $sipVars
* @param int $recordId
* @param array $tableVars
* @return array
* @throws CodeException
* @throws DbException
* @internal param array $sipVars
*/
private function acquireDirty(array $sipVars) {
private function acquireDirty($recordId, array $tableVars) {
$answer = array();
if (empty($sipVars[SIP_FORM])) {
throw new CodeException("Missing 'form' in SIP. There might be something broken.", ERROR_MISSING_FORM_IN_SIP);
}
$recordId = empty($sipVars[SIP_RECORD_ID]) ? 0 : $sipVars[SIP_RECORD_ID];
// For r=0 (new) , 'dirty' will always succeed.
if ($recordId == 0) {
return [API_STATUS => 'success', API_MESSAGE => ''];
}
// Get tableName
$tableVars = $this->db->sql("SELECT tableName, dirtyMode FROM Form AS f WHERE f.name=?", ROW_EXPECT_1, [$sipVars[SIP_FORM]], "Form not found: '" . $sipVars[SIP_FORM] . "'");
$tableName = $tableVars[F_TABLE_NAME];
$formDirtyMode = $tableVars[F_DIRTY_MODE];
......@@ -159,11 +185,11 @@ class Dirty {
/**
* Write a 'Dirty'-Record.
*
* @param $s
* @param $recordId
* @param $tableName
* @param $formDirtyMode
* @param $feUser
* @param string $s SIP given by URL GET
* @param int $recordId extracted from SIP
* @param string $tableName extracted from SIP
* @param string $formDirtyMode
* @param string $feUser
* @return array
* @throws CodeException
* @throws DbException
......@@ -179,7 +205,7 @@ class Dirty {
$this->db->sql("INSERT INTO Dirty (`sip`, `tableName`, `recordId`, `recordModified`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " .
"VALUES ( ?,?,?,?,?,?,?,?,? )", ROW_REGULAR, [$s, $tableName, $recordId, $recordModified, $feUser, $this->client[CLIENT_COOKIE_QFQ], $formDirtyMode, $this->client[CLIENT_REMOTE_ADDRESS], date('YmdHis')]);
return [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
return [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => $this->lockTimeout ];
}
......@@ -187,30 +213,32 @@ class Dirty {
* 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.
*
* @param string $formMode
* @param string $dirtyMode
* @param string $formMode FORM_DELETE, FORM_SAVE
* @param string $dirtyMode DIRTY_MODE_EXCLUSIVE, DIRTY_MODE_ADVISORY, DIRTY_MODE_NONE
* @param string $tableName
* @param int $recordId
* @param int $timeOutDurationSeconds
* @return array
* @throws CodeException
* @throws UserFormException
*/
public function checkDirtyAndRelease($formMode, $dirtyMode, $tableName, $recordId, $timeOutDurationSeconds) {
public function checkDirtyAndRelease($formMode, $dirtyMode, $tableName, $recordId) {
$answer = [ API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
if ($recordId == 0) {
return; // New records never have a recordDirty nor a conflict.
return $answer; // New records never have a recordDirty nor a conflict.
}
$recordDirty = $this->getRecordDirty($tableName, $recordId);
if (empty($recordDirty) && $dirtyMode == DIRTY_MODE_NONE) {
return; // only situation where it's ok that there is no dirtyRecord.
return $answer; // only situation where it's ok that there is no dirtyRecord.
}
if (empty($recordDirty)) {
if ($formMode == FORM_DELETE) {
return;
return $answer;
}
throw new UserFormException("Missing record lock: please reload the form, edit and save again.");
......@@ -227,11 +255,9 @@ class Dirty {
if ($recordDirty[DIRTY_QFQ_USER_SESSION_COOKIE] == $this->client[CLIENT_COOKIE_QFQ]) {
//TODO Check ob der Record seit dem Lock veraendert wurde. Falls ja, eine Meldung und Frage was zu tun ist.
//TODO
// Clear the lock
$this->deleteDirtyRecord($recordDirty[COLUMN_ID]);
return;
return $answer;
}
//----------------------------------------
......@@ -239,20 +265,19 @@ class Dirty {
// Check if overwrite is allowed
if ($dirtyMode == DIRTY_MODE_ADVISORY && $recordDirty[F_DIRTY_MODE] == DIRTY_MODE_ADVISORY) {
return;
return $answer;
}
// Check if the record is timed out
if ($timeOutDurationSeconds != 0) {
$datetimeExpire = date_add(date_create($recordDirty[COLUMN_MODIFIED]), date_interval_create_from_date_string("$timeOutDurationSeconds second"));
if ($this->lockTimeout > 0) {
$datetimeExpire = date_add(date_create($recordDirty[COLUMN_MODIFIED]), date_interval_create_from_date_string($this->lockTimeout . " second"));
if ($datetimeExpire <= date_create("now")) {
$this->deleteDirtyRecord($recordDirty[COLUMN_ID]);
return;
return $answer;
}
}
throw new UserFormException("Save not possible - the record has been locked by $msgUser $msgAt");
}
/**
......
......@@ -60,7 +60,6 @@ class Html2Pdf {
if (count($config) == 0) {
$cfg = new Config();
$config = $cfg->readConfig('');
}
......
Supports Markdown
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