source: trunk/lib/Lock.inc.php @ 144

Last change on this file since 144 was 141, checked in by scdev, 18 years ago

Q - Changed <strong> to <em> in raiseMsg() calls.

File size: 13.8 KB
RevLine 
[1]1<?php
2/**
[138]3 * Lock.inc.php
[136]4 * code by strangecode :: www.strangecode.com :: this document contains copyrighted information
[1]5 *
[138]6 * The Lock class provides a system for locking abstract DB rows.
[136]7 *
[1]8 * @author  Quinn Comendant <quinn@strangecode.com>
[136]9 * @version 2.1
[1]10 */
[138]11class Lock {
[1]12
13    // Configuration of this object.
14    var $_params = array(
15        'timeout' => 600,
16        'auto_timeout' => 1800,
17        'error_url' => '/lock.php',
18        'db_table' => 'lock_tbl',
19        'create_table' => true, // Automatically create table and verify columns. Better set to false after site launch.
20    );
21
22    // Store lock data from DB.
23    var $data = array();
24
25    // Auth_SQL object from which to access a current user_id.
26    var $_auth;
27
28    /**
29     * This method enforces the singleton pattern for this class.
30     *
[138]31     * @return  object  Reference to the global Lock object.
[1]32     * @access  public
33     * @static
34     */
35    function &getInstance($auth_object)
36    {
[136]37        static $instance = null;
[42]38
[136]39        if ($instance === null) {
[138]40            $instance = new Lock($auth_object);
[1]41        }
42
[136]43        return $instance;
[1]44    }
45
46    /**
47     * Constructor. Pass an Auth object on which to perform user lookups.
48     *
[136]49     * @param mixed  $auth_object  An Auth_SQL or Auth_FILE object.
[1]50     */
[138]51    function Lock($auth_object)
[1]52    {
[136]53        $app =& App::getInstance();
54
55        if (!method_exists($auth_object, 'getVal') || !method_exists($auth_object, 'getUsername')) {
56            trigger_error('Constructor not provided a valid Auth_* object.', E_USER_ERROR);
[22]57        }
58
[1]59        $this->_auth = $auth_object;
[42]60
[1]61        // Get create tables config from global context.
[136]62        if (!is_null($app->getParam('db_create_tables'))) {
63            $this->setParam(array('create_table' => $app->getParam('db_create_tables')));
[1]64        }
65    }
66
67    /**
68     * Setup the database table for this class.
69     *
70     * @access  public
71     * @author  Quinn Comendant <quinn@strangecode.com>
72     * @since   26 Aug 2005 17:09:36
73     */
74    function initDB($recreate_db=false)
75    {
[136]76        $app =& App::getInstance();
77        $db =& DB::getInstance();
78
[1]79        static $_db_tested = false;
[42]80
[1]81        if ($recreate_db || !$_db_tested && $this->getParam('create_table')) {
82            if ($recreate_db) {
[136]83                $db->query("DROP TABLE IF EXISTS " . $this->getParam('db_table'));
84                $app->logMsg(sprintf('Dropping and recreating table %s.', $this->getParam('db_table')), LOG_DEBUG, __FILE__, __LINE__);
[1]85            }
[136]86            $db->query("CREATE TABLE IF NOT EXISTS " . $this->getParam('db_table') . " (
[1]87                lock_id int NOT NULL auto_increment,
88                record_table varchar(255) NOT NULL default '',
89                record_key varchar(255) NOT NULL default '',
90                record_val varchar(255) NOT NULL default '',
91                title varchar(255) NOT NULL default '',
92                set_by_admin_id smallint(11) NOT NULL default '0',
93                lock_datetime datetime NOT NULL default '0000-00-00 00:00:00',
94                PRIMARY KEY (lock_id),
95                KEY record_table (record_table),
96                KEY record_key (record_key),
97                KEY record_val (record_val)
98            )");
[42]99
[136]100            if (!$db->columnExists($this->getParam('db_table'), array(
[42]101                'lock_id',
102                'record_table',
103                'record_key',
104                'record_val',
105                'title',
106                'set_by_admin_id',
107                'lock_datetime',
[1]108            ), false, false)) {
[136]109                $app->logMsg(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), LOG_ALERT, __FILE__, __LINE__);
[1]110                trigger_error(sprintf('Database table %s has invalid columns. Please update this table manually.', $this->getParam('db_table')), E_USER_ERROR);
111            }
[42]112        }
[1]113        $_db_tested = true;
114    }
115
116    /**
117     * Set the params of this object.
118     *
119     * @param  array $params   Array of param keys and values to set.
120     */
121    function setParam($params=null)
122    {
123        if (isset($params) && is_array($params)) {
124            // Merge new parameters with old overriding only those passed.
125            $this->_params = array_merge($this->_params, $params);
126        }
127    }
128
129    /**
[136]130     * Return the value of a parameter, if it exists.
[1]131     *
[136]132     * @access public
133     * @param string $param        Which parameter to return.
134     * @return mixed               Configured parameter value.
[1]135     */
136    function getParam($param)
137    {
[136]138        $app =& App::getInstance();
139   
[1]140        if (isset($this->_params[$param])) {
141            return $this->_params[$param];
142        } else {
[136]143            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_NOTICE, __FILE__, __LINE__);
[1]144            return null;
145        }
146    }
147
148    /**
149     * Select the lock to manipulate.
150     *
151     * @param mixed  $record_table_or_lock_id  The table containing the record to lock,
152     *                                         or a numeric lock_id.
153     * @param string $record_key  The key column for the record to lock.
154     * @param string $record_val  The value of the key column for the record to lock.
155     * @param string $title       A title to apply to the lock, for display purposes.
156     */
157    function select($record_table_or_lock_id, $record_key=null, $record_val=null)
158    {
[136]159        $app =& App::getInstance();
160        $db =& DB::getInstance();
161
[1]162        $this->initDB();
163
164        // Expire old locks.
165        $this->_auto_timeout();
[42]166
[1]167        if (is_numeric($record_table_or_lock_id) && !isset($record_key) && !isset($record_val)) {
168            // Get lock data by lock_id.
[136]169            $qid = $db->query("
[42]170                SELECT * FROM " . $this->getParam('db_table') . "
[136]171                WHERE lock_id = '" . $db->escapeString($record_table_or_lock_id) . "'
[1]172            ");
173        } else {
174            // Get lock data by record specs
[136]175            $qid = $db->query("
[42]176                SELECT * FROM " . $this->getParam('db_table') . "
[136]177                WHERE record_table = '" . $db->escapeString($record_table_or_lock_id) . "'
178                AND record_key = '" . $db->escapeString($record_key) . "'
179                AND record_val = '" . $db->escapeString($record_val) . "'
[1]180            ");
181        }
182        if ($this->data = mysql_fetch_assoc($qid)) {
[136]183            $app->logMsg(sprintf('Selecting %slocked record: %s %s %s', ($this->data['set_by_admin_id'] == $this->_auth->getVal('user_id') ? 'self-' : ''), $record_table_or_lock_id, $record_key, $record_val), LOG_DEBUG, __FILE__, __LINE__);
[22]184            /// FIX ME: What if admin set lock, but public user is current lock user?
[1]185            $this->data['editor'] = $this->_auth->getUsername($this->data['set_by_admin_id']);
186            return true;
187        } else {
[136]188            $app->logMsg(sprintf('No locked record: %s %s %s', $record_table_or_lock_id, $record_key, $record_val), LOG_DEBUG, __FILE__, __LINE__);
[1]189            return false;
190        }
191    }
192
193    /**
194     * Returns true if the record we instantiated with is locked.
195     *
196     * @return bool            True if locked.
197     */
198    function isLocked()
199    {
200        return isset($this->data['lock_id']);
201    }
202
203    /**
[42]204     * Returns the status of who set the lock. Use this to ignore locks set by
[1]205     * the current user.
206     *
207     * @return bool            True if current user set the lock.
208     */
209    function isMine()
210    {
[136]211        $db =& DB::getInstance();
212   
[1]213        $this->initDB();
[42]214
[1]215        if (isset($this->data['lock_id'])) {
[136]216            $qid = $db->query("SELECT * FROM " . $this->getParam('db_table') . " WHERE lock_id = '" . $db->escapeString($this->data['lock_id']) . "'");
[1]217            if ($lock = mysql_fetch_assoc($qid)) {
218                return ($lock['set_by_admin_id'] == $this->_auth->getVal('user_id'));
219            } else {
220                return false;
221            }
222        } else {
223            return false;
224        }
225    }
226
227    /**
228     * Create a new lock for the specified table/key/value.
229     *
230     * @param string $record_table  The table containing the record to lock.
231     * @param string $record_key  The key column for the record to lock.
232     * @param string $record_val  The value of the key column for the record to lock.
233     * @param string $title       A title to apply to the lock, for display purposes.
234     *
235     * @return int            The id for the lock (mysql last insert id).
236     */
237    function set($record_table, $record_key, $record_val, $title='')
[42]238    {
[136]239        $db =& DB::getInstance();
240   
[1]241        $this->initDB();
242
243        // Expire old locks.
244        $this->_auto_timeout();
[42]245
[1]246        // Remove previous locks if exist. Is this better than using a REPLACE INTO?
[136]247        $db->query("
[42]248            DELETE FROM " . $this->getParam('db_table') . "
[136]249            WHERE record_table = '" . $db->escapeString($record_table) . "'
250            AND record_key = '" . $db->escapeString($record_key) . "'
251            AND record_val = '" . $db->escapeString($record_val) . "'
[1]252        ");
[42]253
[1]254        // Set new lock.
[136]255        $db->query("
[1]256            INSERT INTO " . $this->getParam('db_table') . " (
257                record_table,
258                record_key,
259                record_val,
260                title,
261                set_by_admin_id,
262                lock_datetime
263            ) VALUES (
[136]264                '" . $db->escapeString($record_table) . "',
265                '" . $db->escapeString($record_key) . "',
266                '" . $db->escapeString($record_val) . "',
267                '" . $db->escapeString($title) . "',
268                '" . $db->escapeString($this->_auth->getVal('user_id')) . "',
[1]269                NOW()
270            )
271        ");
[136]272        $lock_id = mysql_insert_id($db->getDBH());
[42]273
[1]274        // Must register this locked record as the current.
275        $this->select($lock_id);
[42]276
[1]277        return $lock_id;
278    }
279
280    /**
281     * Unlock the currently selected record.
282     */
283    function remove()
284    {
[136]285        $app =& App::getInstance();
286        $db =& DB::getInstance();
287
[1]288        $this->initDB();
289
290        // Expire old locks.
291        $this->_auto_timeout();
[42]292
[1]293        // Delete a specific lock.
[136]294        $db->query("
[42]295            DELETE FROM " . $this->getParam('db_table') . "
[136]296            WHERE lock_id = '" . $db->escapeString($this->data['lock_id']) . "'
[1]297        ");
[42]298
[136]299        $app->logMsg(sprintf('Removing lock: %s', $this->data['lock_id']), LOG_DEBUG, __FILE__, __LINE__);
[1]300    }
301
302    /**
303     * Unlock all records, or all records for a specified user.
304     */
305    function removeAll($user_id=null)
[42]306    {
[136]307        $app =& App::getInstance();
308        $db =& DB::getInstance();
309
[1]310        $this->initDB();
311
312        // Expire old locks.
313        $this->_auto_timeout();
[42]314
[1]315        if (isset($user_id)) {
316            // Delete specific user's locks.
[136]317            $db->query("DELETE FROM " . $this->getParam('db_table') . " WHERE set_by_admin_id = '" . $db->escapeString($user_id) . "'");
318            $app->logMsg(sprintf('Record locks owned by %s %s have been deleted', $this->_auth->getVal('auth_name'), $this->_auth->getUsername($user_id)), LOG_DEBUG, __FILE__, __LINE__);
[1]319        } else {
320            // Delete ALL locks.
[136]321            $db->query("DELETE FROM " . $this->getParam('db_table') . "");
322            $app->logMsg(sprintf('All record locks deleted by %s %s', $this->_auth->getVal('auth_name'), $this->_auth->getVal('username')), LOG_DEBUG, __FILE__, __LINE__);
[1]323        }
324    }
325
326    /**
327     * Delete's all locks that are older than auto_timeout.
328     */
329    function _auto_timeout()
330    {
[136]331        $db =& DB::getInstance();
332   
[1]333        static $_timeout_run = false;
334
335        $this->initDB();
[42]336
[1]337        if (!$_timeout_run) {
338            // Delete all old locks.
[136]339            $db->query("
[42]340                DELETE FROM " . $this->getParam('db_table') . "
[1]341                WHERE DATE_ADD(lock_datetime, INTERVAL '" . $this->getParam('auto_timeout') . "' SECOND) < NOW()
342            ");
343            $_timeout_run = true;
344        }
345    }
346
347    /**
348     * Redirect to record lock error page.
349     */
350    function dieErrorPage()
351    {
[136]352        $app =& App::getInstance();
353
354        $app->dieURL(sprintf('%s?lock_id=%s&boomerang=%s', $this->getParam('error_url'), $this->data['lock_id'], urlencode(absoluteMe())));
[1]355    }
356
357    /**
358     * Print error page.
359     */
360    function printErrorHTML()
361    {
[136]362        $app =& App::getInstance();
363
[1]364        ?>
365        <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
[136]366        <?php $app->printHiddenSession() ?>
[1]367        <input type="hidden" name="lock_id" value="<?php echo $this->getID(); ?>" />
[42]368
[1]369        <p><?php
[141]370        printf(_("The record %s is currently being edited by %s (%d minutes elapsed). You cannot modify the record while it is locked by another user."),
[42]371            $this->getTitle(),
372            $this->getEditor(),
[1]373            date('i', $this->getSecondsElapsed() + 60)
374        );
375        ?></p>
[42]376
[1]377        <?php if ($this->getSecondsElapsed() > $this->getParam('timeout')) { ?>
378        <p><?php printf(_("You can forcibly unlock the record if you believe the editing session has expired. You might want to confirm with %s before doing this."), $this->getEditor()) ?></p>
[28]379        <input type="submit" name="unlock" value="<?php echo _("Unlock"); ?>" />
[1]380        <?php } ?>
[42]381
[28]382        <input type="submit" name="cancel" value="<?php echo _("Cancel"); ?>" />
[1]383        </form>
384        <?php
385    }
386
387    /**
388     * Return lock_id of locked record.
389     */
390    function getID()
391    {
392        return $this->data['lock_id'];
393    }
394
395    /**
396     * Return title of locked record.
397     */
398    function getTitle()
399    {
400        return $this->data['title'];
401    }
402
403    /**
404     * Return administrator username for locked record.
405     */
406    function getEditor()
407    {
408        return $this->data['editor'];
409    }
410
411    /**
412     * Return total seconds since the record was locked.
413     */
414    function getSecondsElapsed()
415    {
416        if (isset($this->data['lock_datetime']) && $this->data['lock_datetime'] < time()) {
417            return time() - strtotime($this->data['lock_datetime']);
418        } else {
419            return 0;
420        }
421    }
422
423
424} // End of class.
425?>
Note: See TracBrowser for help on using the repository browser.