* Copyright 2001-2010 Strangecode, LLC * * This file is part of The Strangecode Codebase. * * The Strangecode Codebase is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) * any later version. * * The Strangecode Codebase is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * The Strangecode Codebase. If not, see . */ /** * Lock.inc.php * * The Lock class provides a system for locking abstract DB rows. * * @author Quinn Comendant * @version 2.1 */ class Lock { // Configuration of this object. var $_params = array( 'timeout' => 600, 'auto_timeout' => 1800, 'error_url' => '/lock.php', 'db_table' => 'lock_tbl', // Automatically create table and verify columns. Better set to false after site launch. 'create_table' => true, ); // Store lock data from DB. var $data = array(); // Auth_SQL object from which to access a current user_id. var $_auth; /** * This method enforces the singleton pattern for this class. * * @return object Reference to the global Lock object. * @access public * @static */ function &getInstance($auth_object) { static $instance = null; if ($instance === null) { $instance = new Lock($auth_object); } return $instance; } /** * Constructor. Pass an Auth object on which to perform user lookups. * * @param mixed $auth_object An Auth_SQL or Auth_FILE object. */ function Lock($auth_object) { $app =& App::getInstance(); if (!method_exists($auth_object, 'get') || !method_exists($auth_object, 'getUsername')) { trigger_error('Constructor not provided a valid Auth_* object.', E_USER_ERROR); } $this->_auth = $auth_object; // Get create tables config from global context. if (!is_null($app->getParam('db_create_tables'))) { $this->setParam(array('create_table' => $app->getParam('db_create_tables'))); } } /** * Setup the database table for this class. * * @access public * @author Quinn Comendant * @since 26 Aug 2005 17:09:36 */ function initDB($recreate_db=false) { $app =& App::getInstance(); $db =& DB::getInstance(); static $_db_tested = false; if ($recreate_db || !$_db_tested && $this->getParam('create_table')) { if ($recreate_db) { $db->query("DROP TABLE IF EXISTS " . $this->getParam('db_table')); $app->logMsg(sprintf('Dropping and recreating table %s.', $this->getParam('db_table')), LOG_INFO, __FILE__, __LINE__); } $db->query("CREATE TABLE IF NOT EXISTS " . $db->escapeString($this->getParam('db_table')) . " ( lock_id int NOT NULL auto_increment, record_table varchar(255) NOT NULL default '', record_key varchar(255) NOT NULL default '', record_val varchar(255) NOT NULL default '', title varchar(255) NOT NULL default '', set_by_admin_id smallint(11) NOT NULL default '0', lock_datetime datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (lock_id), KEY record_table (record_table), KEY record_key (record_key), KEY record_val (record_val) )"); if (!$db->columnExists($this->getParam('db_table'), array( 'lock_id', 'record_table', 'record_key', 'record_val', 'title', 'set_by_admin_id', 'lock_datetime', ), false, false)) { $app->logMsg(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), LOG_ALERT, __FILE__, __LINE__); trigger_error(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), E_USER_ERROR); } } $_db_tested = true; } /** * Set the params of this object. * * @param array $params Array of param keys and values to set. */ function setParam($params=null) { if (isset($params) && is_array($params)) { // Merge new parameters with old overriding only those passed. $this->_params = array_merge($this->_params, $params); } } /** * Return the value of a parameter, if it exists. * * @access public * @param string $param Which parameter to return. * @return mixed Configured parameter value. */ function getParam($param) { $app =& App::getInstance(); if (isset($this->_params[$param])) { return $this->_params[$param]; } else { $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__); return null; } } /** * Select the lock to manipulate. * * @param mixed $record_table_or_lock_id The table containing the record to lock, * or a numeric lock_id. * @param string $record_key The key column for the record to lock. * @param string $record_val The value of the key column for the record to lock. * @param string $title A title to apply to the lock, for display purposes. */ function select($record_table_or_lock_id, $record_key=null, $record_val=null) { $app =& App::getInstance(); $db =& DB::getInstance(); $this->initDB(); // Expire old locks. $this->_auto_timeout(); if (is_numeric($record_table_or_lock_id) && !isset($record_key) && !isset($record_val)) { // Get lock data by lock_id. $qid = $db->query(" SELECT * FROM " . $db->escapeString($this->getParam('db_table')) . " WHERE lock_id = '" . $db->escapeString($record_table_or_lock_id) . "' "); } else { // Get lock data by record specs $qid = $db->query(" SELECT * FROM " . $db->escapeString($this->getParam('db_table')) . " WHERE record_table = '" . $db->escapeString($record_table_or_lock_id) . "' AND record_key = '" . $db->escapeString($record_key) . "' AND record_val = '" . $db->escapeString($record_val) . "' "); } if ($this->data = mysql_fetch_assoc($qid)) { $app->logMsg(sprintf('Selecting %slocked record: %s %s %s', ($this->data['set_by_admin_id'] == $this->_auth->get('user_id') ? 'self-' : ''), $record_table_or_lock_id, $record_key, $record_val), LOG_DEBUG, __FILE__, __LINE__); /// FIX ME: What if admin set lock, but public user is current lock user? $this->data['editor'] = $this->_auth->getUsername($this->data['set_by_admin_id']); return true; } else { $app->logMsg(sprintf('No locked record: %s %s %s', $record_table_or_lock_id, $record_key, $record_val), LOG_DEBUG, __FILE__, __LINE__); return false; } } /** * Returns true if the record we instantiated with is locked. * * @return bool True if locked. */ function isLocked() { return isset($this->data['lock_id']); } /** * Returns the status of who set the lock. Use this to ignore locks set by * the current user. * * @return bool True if current user set the lock. */ function isMine() { $db =& DB::getInstance(); $this->initDB(); if (isset($this->data['lock_id'])) { $qid = $db->query("SELECT * FROM " . $db->escapeString($this->getParam('db_table')) . " WHERE lock_id = '" . $db->escapeString($this->data['lock_id']) . "'"); if ($lock = mysql_fetch_assoc($qid)) { return ($lock['set_by_admin_id'] == $this->_auth->get('user_id')); } else { return false; } } else { return false; } } /** * Create a new lock for the specified table/key/value. * * @param string $record_table The table containing the record to lock. * @param string $record_key The key column for the record to lock. * @param string $record_val The value of the key column for the record to lock. * @param string $title A title to apply to the lock, for display purposes. * * @return int The id for the lock (mysql last insert id). */ function set($record_table, $record_key, $record_val, $title='') { $db =& DB::getInstance(); $this->initDB(); // Expire old locks. $this->_auto_timeout(); // Remove previous locks if exist. Is this better than using a REPLACE INTO? $db->query(" DELETE FROM " . $db->escapeString($this->getParam('db_table')) . " WHERE record_table = '" . $db->escapeString($record_table) . "' AND record_key = '" . $db->escapeString($record_key) . "' AND record_val = '" . $db->escapeString($record_val) . "' "); // Set new lock. $db->query(" INSERT INTO " . $db->escapeString($this->getParam('db_table')) . " ( record_table, record_key, record_val, title, set_by_admin_id, lock_datetime ) VALUES ( '" . $db->escapeString($record_table) . "', '" . $db->escapeString($record_key) . "', '" . $db->escapeString($record_val) . "', '" . $db->escapeString($title) . "', '" . $db->escapeString($this->_auth->get('user_id')) . "', NOW() ) "); $lock_id = mysql_insert_id($db->getDBH()); // Must register this locked record as the current. $this->select($lock_id); return $lock_id; } /** * Unlock the currently selected record. */ function remove() { $app =& App::getInstance(); $db =& DB::getInstance(); $this->initDB(); // Expire old locks. $this->_auto_timeout(); // Delete a specific lock. $db->query(" DELETE FROM " . $db->escapeString($this->getParam('db_table')) . " WHERE lock_id = '" . $db->escapeString($this->data['lock_id']) . "' "); $app->logMsg(sprintf('Removing lock: %s', $this->data['lock_id']), LOG_DEBUG, __FILE__, __LINE__); } /** * Unlock all records, or all records for a specified user. */ function removeAll($user_id=null) { $app =& App::getInstance(); $db =& DB::getInstance(); $this->initDB(); // Expire old locks. $this->_auto_timeout(); if (isset($user_id)) { // Delete specific user's locks. $db->query("DELETE FROM " . $db->escapeString($this->getParam('db_table')) . " WHERE set_by_admin_id = '" . $db->escapeString($user_id) . "'"); $app->logMsg(sprintf('Record locks owned by user %s have been deleted', $this->_auth->getUsername($user_id)), LOG_DEBUG, __FILE__, __LINE__); } else { // Delete ALL locks. $db->query("DELETE FROM " . $db->escapeString($this->getParam('db_table')) . ""); $app->logMsg(sprintf('All record locks deleted by user %s', $this->_auth->get('username')), LOG_DEBUG, __FILE__, __LINE__); } } /** * Deletes all locks that are older than auto_timeout. */ function _auto_timeout() { $db =& DB::getInstance(); static $_timeout_run = false; $this->initDB(); if (!$_timeout_run) { // Delete all old locks. $db->query(" DELETE FROM " . $db->escapeString($this->getParam('db_table')) . " WHERE DATE_ADD(lock_datetime, INTERVAL '" . $this->getParam('auto_timeout') . "' SECOND) < NOW() "); $_timeout_run = true; } } /** * Redirect to record lock error page. */ function dieErrorPage() { $app =& App::getInstance(); $app->dieURL(sprintf('%s?lock_id=%s&boomerang=%s', $this->getParam('error_url'), $this->data['lock_id'], urlencode(absoluteMe()))); } /** * Print error page. */ function printErrorHTML() { $app =& App::getInstance(); ?>
printHiddenSession() ?>

getTitle(), $this->getEditor(), date('i', $this->getSecondsElapsed() + 60) ); ?>

getSecondsElapsed() >= $this->getParam('timeout')) { ?>

getEditor()) ?>

" /> " />
data['lock_id']; } /** * Return title of locked record. */ function getTitle() { return $this->data['title']; } /** * Return administrator username for locked record. */ function getEditor() { return $this->data['editor']; } /** * Return total seconds since the record was locked. */ function getSecondsElapsed() { if (isset($this->data['lock_datetime']) && strtotime($this->data['lock_datetime']) < time()) { return time() - strtotime($this->data['lock_datetime']); } else { return 0; } } } // End of class. ?>