- new implementation (database independent) of the transaction mechanism, that prevents a user from submitting the same form twice.

SVN:trunk[900]
This commit is contained in:
Denis Flaven
2010-10-19 13:20:23 +00:00
parent 0c34b47aac
commit e9304bbcfa

View File

@@ -17,94 +17,57 @@
/**
* This class records the pending "transactions" corresponding to forms that have not been
* submitted yet, in order to prevent double submissions. When created a transaction remains valid
* until it is "used" by calling IsTransactionValid once, or until it
* expires (delay = TRANSACTION_EXPIRATION_DELAY, defaults to 4 hours)
* until the user's session expires
* @package iTop
*/
// How long a "transaction" is considered valid, i.e. when a form is submitted
// if the form dates back from too long a time, it is considered invalid. This is
// because since HTTP is not a connected protocol, we cannot know when a user disconnects
// from the application (maybe just by closing her browser), so we keep track - in the database - of all pending
// forms that have not yet been submitted. To limit this list we consider that after some time
// a "transaction" is no loger valid an gets purged from the table
define ('TRANSACTION_EXPIRATION_DELAY', 3600*4); // default: 4h
require_once('../core/dbobject.class.php');
class privUITransaction extends DBObject
class privUITransaction
{
public static function Init()
{
$aParams = array
(
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => "expiration_date",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_transaction",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeDateTime("expiration_date", array("allowed_values"=>null, "sql"=>"expiration_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_SetZListItems('details', array('expiration_date')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('expiration_date')); // Attributes to be displayed for a list
}
/**
* Create a new transaction, store it in the database and return its id
* Create a new transaction id, store it in the session and return its id
* @param void
* @return int The identifier of the new transaction
*/
public static function GetNewTransactionId()
{
// First remove all the expired transactions...
self::CleanupExpiredTransactions();
$oTransaction = new privUITransaction();
$sDate = date('Y-m-d H:i:s', time()+TRANSACTION_EXPIRATION_DELAY);
$oTransaction->Set('expiration_date', $sDate); // 4 h delay by default
$oTransaction->DBInsert();
return sprintf("%d", $oTransaction->GetKey());
if (!isset($_SESSION['transactions']))
{
$_SESSION['transactions'] = array();
}
// Strictly speaking, the two lines below should be grouped together
// by a critical section
// sem_acquire($rSemIdentified);
$id = 1 + count($_SESSION['transactions']);
$_SESSION['transactions'][$id] = true;
// sem_release($rSemIdentified);
return sprintf("%d", $id);
}
/**
* Check whether a transaction is valid or not and remove the valid transaction from
* the database so that another call to IsTransactionValid for the same transaction
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @return bool True if the transaction is valid, false otherwise
*/
public static function IsTransactionValid($id)
{
// First remove all the expired transactions...
self::CleanupExpiredTransactions();
// TO DO put a critical section around this part to be 100% safe...
// sem_acquire(...)
$bResult = false;
$oTransaction = MetaModel::GetObject('privUITransaction', $id, false /* MustBeFound */);
if ($oTransaction)
$bResult = false;
if (isset($_SESSION['transactions']))
{
$bResult = true;
$oTransaction->DBDelete();
// Strictly speaking, the three lines below should be grouped together
// inside the same critical section as above
// sem_acquire($rSemIdentified);
if (isset($_SESSION['transactions'][$id]))
{
$bResult = true;
unset($_SESSION['transactions'][$id]);
}
// sem_release($rSemIdentified);
}
// sem_release(...)
return $bResult;
}
/**
* Remove from the database all transactions that have expired
*/
protected static function CleanupExpiredTransactions()
{
$sQuery = 'SELECT privUITransaction WHERE expiration_date < NOW()';
$oSearch = DBObjectSearch::FromOQL($sQuery);
$oSet = new DBObjectSet($oSearch);
while($oTransaction = $oSet->Fetch())
{
$oTransaction->DBDelete();
}
}
}
?>