Sindbad~EG File Manager
<?php
/*
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2007 Bharat Mediratta
*
* This program 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 2 of the License, or (at
* your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* Database storage mechanism. This object provides the hooks for saving and restoring objects in
* the persistent store.
* @package GalleryCore
* @subpackage Classes
* @author Bharat Mediratta <bharat@menalto.com>
* @version $Revision: 15927 $
*/
/* Require the ADOdb libraries */
GalleryCoreApi::requireOnce('lib/adodb/adodb.inc.php');
/**
* This will let ADOdb know which error handler function we want to use.
* (if embedded in an application also using ADOdb it may be defined already)
*/
if (!defined('ADODB_ERROR_HANDLER')) {
define('ADODB_ERROR_HANDLER', 'GalleryAdodbErrorHandler');
}
/**
* Name of the sequence we'll use for GalleryEntity ids
*/
define('DATABASE_SEQUENCE_ID', 'SequenceId');
/**
* Name of the sequence we'll use for lock ids
*/
define('DATABASE_SEQUENCE_LOCK', 'SequenceLock');
/**
* Default prefix to prepend to table names
*/
define('DATABASE_TABLE_PREFIX', 'g2_');
/**
* Default prefix to prepend to column names
*/
define('DATABASE_COLUMN_PREFIX', 'g_');
/**
* Database storage mechanism. This object provides the hooks for saving and restoring objects in
* the persistent store.
*/
class GalleryStorage {
/**
* Internal pointer to ADOdb database object
* @var object ADOdb
* @access protected
*/
var $_db;
/**
* Internal pointer to a non-transactional ADOdb database object
* @var object ADOdb
* @access protected
*/
var $_nonTransactionalDb;
/**
* Internal pointer to our DatabaseStorageExtras object
* @var object DatabaseStorageExtras
* @access protected
*/
var $_extras;
/**
* Database type (ADOdb driver name)
* @var string
* @access protected
*/
var $_type;
/**
* Name of the database user
* @var string
* @access protected
*/
var $_username;
/**
* Password for the database user
* @var string
* @access protected
*/
var $_password;
/**
* Name of the database to use
* @var string
* @access protected
*/
var $_database;
/**
* Host the database runs on
* @var string
* @access protected
*/
var $_hostname;
/**
* Are we attempting to be transactional?
* @var boolean
* @access protected
*/
var $_isTransactional;
/**
* A string to prepend to table names
* @var string
* @access protected
*/
var $_tablePrefix;
/**
* A string to prepend to column names
* @var string
* @access protected
*/
var $_columnPrefix;
/**
* A cache of member info that we've discovered about various classes
* @var array
* @access protected
*/
var $_entityInfoCache;
/**
* Whether or not we should use persistent database connections
* @var boolean
* @access protected
*/
var $_usePersistentConnections;
/**
* Whether this storage accepts empty (string) values for NOT NULL columns. Some DBMS
* implicitly converts empty values to NULL on INSERT/UPDATE and then the value would violate a
* NOT NULL condition.
* @var boolean
* @access protected
*/
var $_isEmptyAllowedForNotNullColumn;
/**
* @param array $config database configuration values
*/
function GalleryStorage($config) {
$this->_type = $config['type'];
$this->_username = $config['username'];
$this->_password = $config['password'];
$this->_hostname = $config['hostname'];
$this->_database = $config['database'];
$this->_isTransactional = false;
$this->_isEmptyAllowedForNotNullColumn = true;
$this->_entityInfoCache = array();
/* We use persistent connections if the value is left out, or if it's non empty. */
$this->_usePersistentConnections = !isset($config['usePersistentConnections'])
|| !empty($config['usePersistentConnections']);
$this->_tablePrefix = isset($config['tablePrefix']) ? $config['tablePrefix']
: DATABASE_TABLE_PREFIX;
$this->_columnPrefix = isset($config['columnPrefix']) ? $config['columnPrefix']
: DATABASE_COLUMN_PREFIX;
}
/**
* Connect to the database
*
* @return array object GalleryStatus a status code
* object a database resource
* @access protected
*/
function _getConnection($forceNew=false) {
global $gallery;
$this->_traceStart();
$db =& ADONewConnection($this->getAdoDbType());
$this->_traceStop();
if (empty($db)) {
return array(GalleryCoreApi::error(ERROR_STORAGE_FAILURE), null);
}
/* Turn on debugging in the database connection if Gallery is in debug mode */
if ($gallery->getDebug()) {
$db->debug = true;
}
/* Configure transliteration for COM based DB drivers */
if (defined('CP_UTF8')) {
$db->charPage = CP_UTF8;
}
$this->_traceStart();
$connectMethod = ($forceNew || !$this->_usePersistentConnections) ? 'NConnect' : 'PConnect';
$ret = $db->$connectMethod($this->_hostname,
$this->_username,
$this->_password,
$this->_database);
$this->_traceStop();
if (!$ret) {
return array(GalleryCoreApi::error(ERROR_STORAGE_FAILURE), null);
}
if ($gallery->isProfiling('sql')) {
$this->_traceStart();
$db->LogSQL();
$this->_traceStop();
}
$ret = $this->_setConnectionSettings($db);
if ($ret) {
return array(GalleryCoreApi::error(ERROR_STORAGE_FAILURE), null);
}
return array(null, $db);
}
/**
* Return the type of this database (ADOdb driver name)
*
* @return string
*/
function getAdoDbType() {
return $this->_type;
}
/**
* Set runtime settings for the given database connection.
* Set the connection/client/server encoding and other parameters
*
* @param object adodb $db database handle
* @return object GalleryStatus a status code
* @access protected
*/
function _setConnectionSettings(&$db) {
return null;
}
/**
* Get the reference to our GalleryStorageExtras instance where we put less frequently used code
* (most code that's used to modify the database).
*
* @return object GalleryStorageExtras
* @access protected
*/
function &_getExtras() {
if (!isset($this->_extras)) {
GalleryCoreApi::requireOnce(
'modules/core/classes/GalleryStorage/GalleryStorageExtras.class', true);
$this->_extras =& new GalleryStorageExtras($this);
}
return $this->_extras;
}
/**
* Is the database transactional?
*
* @return boolean true if transactional
*/
function isTransactional() {
return $this->_isTransactional;
}
/**
* Load the GalleryEntities with the ids specified
*
* @param mixed $ids array of ids of the GalleryEntities to load or a single int id
* @return array object GalleryStatus a status code,
* mixed one GalleryEntity or an array of GalleryEntities
*/
function loadEntities($ids) {
$extras =& $this->_getExtras();
return $extras->loadEntities($ids);
}
/**
* Save the changes to the GalleryEntity
*
* @param object GalleryEntity $entity the GalleryEntity to save
* @return object GalleryStatus a status code
*/
function saveEntity(&$entity) {
$extras =& $this->_getExtras();
return $extras->saveEntity($entity);
}
/**
* Delete the GalleryEntity
*
* @param object GalleryEntity $entity the GalleryEntity to delete
* @return object GalleryStatus a status code
*/
function deleteEntity(&$entity) {
$extras =& $this->_getExtras();
return $extras->deleteEntity($entity);
}
/**
* Create a new GalleryEntity
*
* @param object GalleryEntity $entity the GalleryEntity to put the data in
* @return object GalleryStatus a status code
*/
function newEntity(&$entity) {
$extras =& $this->_getExtras();
return $extras->newEntity($entity);
}
/**
* Get a new, unique id
*
* @return array object GalleryStatus a status code
* int an id
*/
function getUniqueId() {
$extras =& $this->_getExtras();
return $extras->getUniqueId();
}
/**
* Refresh a GalleryEntity from the database if it has changed
*
* @param object GalleryEntity $entity the object to refresh
* @return array object GalleryStatus a status code,
* object GalleryEntity the fresh entity
*/
function refreshEntity($entity) {
$extras =& $this->_getExtras();
return $extras->refreshEntity($entity);
}
/**
* Acquire read locks on the given items
*
* @param mixed $entityIds array of ids or single int id
* @param int $timeout timeout before giving up on the lock
* @return array object GalleryStatus a status code
* array lock data
*/
function acquireReadLock($entityIds, $timeout) {
$extras =& $this->_getExtras();
return $extras->acquireReadLock($entityIds, $timeout);
}
/**
* Acquire write locks on the given items
*
* @param mixed $entityIds array of ids or single int id
* @param int $timeout timeout before giving up on the lock
* @return array object GalleryStatus a status code
* array lock data
*/
function acquireWriteLock($entityIds, $timeout) {
$extras =& $this->_getExtras();
return $extras->acquireWriteLock($entityIds, $timeout);
}
/**
* Refresh all the locks that we hold so that they aren't accidentally considered expired
*
* @param array $lockIds the lock ids
* @param int $freshUntil the new "fresh until" timestamp
* @return object GalleryStatus a status code
*/
function refreshLocks($lockIds, $freshUntil) {
$extras =& $this->_getExtras();
return $extras->refreshLocks($lockIds, $freshUntil);
}
/**
* Release the given locks.
*
* @param mixed $lockIds array of lock ids or single id
* @return object GalleryStatus a status code
*/
function releaseLocks($lockIds) {
$extras =& $this->_getExtras();
return $extras->releaseLocks($lockIds);
}
/**
* Remove ids from a lock.
*
* @param array $lock lock data
* @param array $ids ids to remove
* @return object GalleryStatus a status code
*/
function removeIdsFromLock($lock, $ids) {
$extras =& $this->_getExtras();
return $extras->removeIdsFromLock($lock, $ids);
}
/**
* Move ids between locks.
*
* @param array $relock of lockId => object ids
* @param int $newLockId the new lockId
* @param int $lockType LOCK_READ or LOCK_WRITE
* @return object GalleryStatus a status code
*/
function moveIdsBetweenLocks($relock, $newLockId, $lockType) {
$extras =& $this->_getExtras();
return $extras->moveIdsBetweenLocks($relock, $newLockId, $lockType);
}
/**
* Generate a new lock id.
*
* @return array object GalleryStatus a status code
* int lock id
*/
function newLockId() {
$extras =& $this->_getExtras();
return $extras->newLockId();
}
/**
* Search the persistent store for the target values matching the given criteria
*
* @param string $query the search query
* @param array $data any explicit data values required by the query
* @param array $optional optional arguments (eg. limits)
* @return array object GalleryStatus a status code,
* object GallerySearchResults the result values
*/
function search($query, $data=array(), $optional=array()) {
if (!isset($this->_db)) {
list ($ret, $this->_db) = $this->_getConnection();
if ($ret) {
return array($ret, null);
}
}
$query = $this->_translateQuery($query);
/* Run it with the right limits and return the results */
$GLOBALS['ADODB_FETCH_MODE'] = ADODB_FETCH_NUM;
if (!empty($optional['limit'])) {
$count = empty($optional['limit']['count']) ? -1 : $optional['limit']['count'];
$offset = empty($optional['limit']['offset']) ? -1 : $optional['limit']['offset'];
$this->_traceStart();
$recordSet = $this->_db->SelectLimit($query, $count, $offset, $data);
$this->_traceStop();
} else {
$this->_traceStart();
$recordSet = $this->_db->Execute($query, $data);
$this->_traceStop();
}
if ($recordSet) {
return array(null, new GallerySearchResults($recordSet));
} else {
return array(GalleryCoreApi::error(ERROR_STORAGE_FAILURE), null);
}
}
/**
* Execute a database statement
*
* @param string $statement the SQL statement
* @param array $data any explicit data values required by the query
* @return object GalleryStatus a status code,
*/
function execute($statement, $data=array()) {
$extras =& $this->_getExtras();
return $extras->execute($statement, $data);
}
/**
* Add a new entry to a map
*
* @param string $mapName the map we're working on
* @param array $entry an associative array of data about the entry
* @return object GalleryStatus a status code
*/
function addMapEntry($mapName, $entry) {
$extras =& $this->_getExtras();
return $extras->addMapEntry($mapName, $entry);
}
/**
* Remove entries from a map
*
* @param string $mapName the map we're working on
* @param array $entry an associative array of data about the entries to match
* @return object GalleryStatus a status code
*/
function removeMapEntry($mapName, $entry) {
$extras =& $this->_getExtras();
return $extras->removeMapEntry($mapName, $entry);
}
/**
* Remove ALL entries from a map. Use with caution!
*
* @param string $mapName the map we're working on
* @return object GalleryStatus a status code
*/
function removeAllMapEntries($mapName) {
$extras =& $this->_getExtras();
return $extras->removeAllMapEntries($mapName);
}
/**
* Get entries in a map that match a criteria and return selected fields
*
* @param string $mapName the map we're working on
* @param array $select the columns to return
* @param array $match the entries to match
* @param array $optional optional arguments (eg. limit, orderBy)
* array('limit' => array('count' => #, 'offset' => #),
* 'orderBy' => array(columnName => ORDER_ASCENDING|ORDER_DESCENDING, ...))
* @return array object GalleryStatus a status code
* object GallerySearchResults the results
*/
function getMapEntry($mapName, $select, $match=array(), $optional=array()) {
if (!isset($this->_db)) {
list ($ret, $this->_db) = $this->_getConnection();
if ($ret) {
return array($ret, null);
}
}
/* Get Map information to check against */
list ($ret, $mapInfo) = $this->describeMap($mapName);
if ($ret) {
return array($ret, null);
}
list ($tableName, $unused) = $this->_translateTableName($mapName);
$data = $selectColumns = $where = $wheredata = $orderBy = array();
/* SELECT */
foreach ($select as $columnName) {
if (!array_key_exists($columnName, $mapInfo)) {
break;
}
$selectColumns[] = $this->_translateColumnName($columnName);
}
/* WHERE */
foreach ($match as $columnName => $matchValue) {
if (!array_key_exists($columnName, $mapInfo)) {
break;
}
$this->_getWhereSql($columnName, $mapInfo[$columnName], $matchValue,
$where, $wheredata);
}
/* ORDER BY */
if (!empty($optional['orderBy'])) {
foreach ($optional['orderBy'] as $columnName => $orderDirection) {
if (!array_key_exists($columnName, $mapInfo)) {
break;
}
$orderDirection = ($orderDirection == ORDER_DESCENDING) ? 'DESC' : 'ASC';
$orderBy[] = $this->_translateColumnName($columnName) . ' ' . $orderDirection;
}
}
/* Check if all parameters are correct */
if (empty($selectColumns) || count($selectColumns) != count($select)
|| count($where) != count($match)
|| (!empty($optional['orderBy'])
&& count($optional['orderBy']) != count($orderBy))) {
return array(GalleryCoreApi::error(ERROR_BAD_PARAMETER), null);
}
/* Generate SQL query */
$query = 'SELECT ' . implode(', ', $selectColumns) . ' FROM ' . $tableName;
if (!empty($where)) {
$query .= ' WHERE ' . implode(' AND ', $where);
$data = array_merge($data, $wheredata);
}
if (!empty($orderBy)) {
$query .= ' ORDER BY ' . implode(', ', $orderBy);
}
/* Run it with the right limits and return the results */
$GLOBALS['ADODB_FETCH_MODE'] = ADODB_FETCH_NUM;
if (!empty($optional['limit'])) {
$count = empty($optional['limit']['count']) ? -1 : $optional['limit']['count'];
$offset = empty($optional['limit']['offset']) ? -1 : $optional['limit']['offset'];
$this->_traceStart();
$recordSet = $this->_db->SelectLimit($query, $count, $offset, $data);
$this->_traceStop();
} else {
$this->_traceStart();
$recordSet = $this->_db->Execute($query, $data);
$this->_traceStop();
}
if ($recordSet) {
return array(null, new GallerySearchResults($recordSet, $select, $mapInfo));
} else {
return array(GalleryCoreApi::error(ERROR_STORAGE_FAILURE), null);
}
}
/**
* Update entries in a map
*
* @param string $mapName the map we're working on
* @param array $match the entries to match
* @param array $change the values to change
* @return object GalleryStatus a status code
*/
function updateMapEntry($mapName, $match, $change) {
if (!isset($this->_db)) {
list ($ret, $this->_db) = $this->_getConnection();
if ($ret) {
return $ret;
}
}
$ret = $this->_guaranteeTransaction();
if ($ret) {
return $ret;
}
list ($ret, $mapInfo) = $this->describeMap($mapName);
if ($ret) {
return $ret;
}
list ($tableName, $unused) = $this->_translateTableName($mapName);
$data = $set = $where = $wheredata = array();
foreach ($mapInfo as $memberName => $memberData) {
if (array_key_exists($memberName, $match)) {
$this->_getWhereSql($memberName, $memberData, $match[$memberName],
$where, $wheredata);
}
if (array_key_exists($memberName, $change)) {
if (GalleryUtilities::isA($change[$memberName], 'GallerySqlFragment')) {
$set[] = $this->_translateColumnName($memberName) . ' ' .
$this->_translateQuery($change[$memberName]->getFragment());
foreach ($change[$memberName]->getValues() as $value) {
$setdata[] = $value;
}
} else {
$set[] = $this->_translateColumnName($memberName) . '=?';
$setdata[] = $this->_normalizeValue($change[$memberName], $memberData);
}
}
}
if (count($set) == 0 || count($where) == 0) {
return GalleryCoreApi::error(ERROR_BAD_PARAMETER);
}
$query = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $set) . ' ';
$data = array_merge($data, $setdata);
$query .= 'WHERE ' . implode(' AND ', $where);
$data = array_merge($data, $wheredata);
$this->_traceStart();
$recordSet = $this->_db->Execute($query, $data);
$this->_traceStop();
if (!$recordSet) {
return GalleryCoreApi::error(ERROR_STORAGE_FAILURE);
}
return null;
}
/**
* Accepts a $mapInfo pair and $match value to add match information to supplied array.
*
* @param string $memberName Name of map field
* @param array $memberData Data about map field
* @param mixed $matchValue Values that the map should match on
* @param array $where All match keys
* @param array $wheredata All match values
*/
function _getWhereSql($memberName, $memberData, $matchValue, &$where, &$wheredata) {
if (GalleryUtilities::isA($matchValue, 'GallerySqlFragment')) {
$where[] = $this->_translateColumnName($memberName) . ' ' .
$this->_translateQuery($matchValue->getFragment());
foreach ($matchValue->getValues() as $value) {
$wheredata[] = $value;
}
} else if (is_array($matchValue)) {
$qs = array();
foreach ($matchValue as $value) {
$qs[] = '?';
$wheredata[] = $this->_normalizeValue($value, $memberData);
}
$where[] = $this->_translateColumnName($memberName) . ' IN ('
. implode(',', $qs) . ')';
} else {
$value = $this->_normalizeValue($matchValue, $memberData);
if (is_null($value)) {
$where[] = $this->_translateColumnName($memberName) . ' IS NULL';
} else {
$where[] = $this->_translateColumnName($memberName) . '=?';
$wheredata[] = $value;
}
}
}
/**
* Configure the persistent store for this strategy, for the given module.
*
* @param string $moduleId
* @param array $upgradeInfo (optional) of (classname => old schema version)
* @return object GalleryStatus a status code
*/
function configureStore($moduleId, $upgradeInfo=array()) {
$extras =& $this->_getExtras();
return $extras->configureStore($moduleId, $upgradeInfo);
}
/**
* Perform any cleanup necessary after installing or upgrading the given module.
*
* @param string $moduleId
* @return object GalleryStatus a status code
*/
function configureStoreCleanup($moduleId) {
$extras =& $this->_getExtras();
return $extras->configureStoreCleanup($moduleId);
}
/**
* Uninstall the database schema for the given module
*
* @return object GalleryStatus a status code
*/
function unconfigureStore($moduleId) {
$extras =& $this->_getExtras();
return $extras->unconfigureStore($moduleId);
}
/**
* Clean out and reset the persistent store for this strategy.
*
* @return object GalleryStatus a status code
*/
function cleanStore() {
$extras =& $this->_getExtras();
return $extras->cleanStore();
}
/**
* Begin a new transaction, if the storage layer supports them.
*
* @return object GalleryStatus a status code
*/
function beginTransaction() {
if (!isset($this->_db)) {
list ($ret, $this->_db) = $this->_getConnection();
if ($ret) {
return $ret;
}
}
if ($this->_isTransactional) {
$this->_traceStart();
$ok = $this->_db->BeginTrans();
$this->_traceStop();
if (!$ok) {
return GalleryCoreApi::error(ERROR_STORAGE_FAILURE);
}
}
return null;
}
/**
* Commit our transaction, if the storage layer supports them.
*
* @return object GalleryStatus a status code
*/
function commitTransaction() {
if ($this->_isTransactional && isset($this->_db) && $this->_db->transCnt > 0) {
$this->_traceStart();
$ok = $this->_db->CommitTrans();
$this->_traceStop();
if (!$ok) {
return GalleryCoreApi::error(ERROR_STORAGE_FAILURE);
}
}
if ($this->_isTransactional) {
/* Release any queued locks */
global $gallery;
$result =& $gallery->getLockSystem(false);
$ret = $result[0];
if ($ret) {
return $ret;
}
$lockSystem =& $result[1];
if (isset($lockSystem)) {
$ret = $lockSystem->releaseQueue();
if ($ret) {
return $ret;
}
}
}
return null;
}
/**
* Mark a storage checkpoint, which will commit pending transactions and perform any future
* tied-tasks
*
* @return object GalleryStatus a status code
*/
function checkPoint() {
if (!isset($this->_db)) {
return null;
}
$ret = $this->commitTransaction();
if ($ret) {
return $ret;
}
return null;
}
/**
* Rollback our transaction, if the storage layer supports them.
*
* @return object GalleryStatus a status code
*/
function rollbackTransaction() {
if ($this->_isTransactional && isset($this->_db) && $this->_db->transCnt > 0) {
$this->_traceStart();
$ok = $this->_db->RollbackTrans();
$this->_traceStop();
if (!$ok) {
return GalleryCoreApi::error(ERROR_STORAGE_FAILURE);
}
}
if ($this->_isTransactional) {
/* Release any queued locks */
global $gallery;
$result =& $gallery->getLockSystem(false);
$ret = $result[0];
if ($ret) {
return $ret;
}
$lockSystem =& $result[1];
if (isset($lockSystem)) {
$ret = $lockSystem->releaseQueue();
if ($ret) {
return $ret;
}
}
}
return null;
}
/**
* Begin transaction if not already in one.
*
* @return object GalleryStatus a status code
* @access protected
*/
function _guaranteeTransaction() {
if ($this->_isTransactional && !$this->_db->transCnt) {
$ret = $this->beginTransaction();
if ($ret) {
return $ret;
}
}
return null;
}
/**
* Convert an integer into something that the database will accept into a bit column
*
* @param int $intVal integer value
* @return mixed bit value
*/
function convertIntToBits($intVal) {
return $intVal;
}
/**
* Convert value from a database bit column into an integer
*
* @param mixed $bitsVal bit value
* @return int integer value
*/
function convertBitsToInt($bitsVal) {
return $bitsVal;
}
/**
* Return database specific syntax to replace in schema SQL
*
* @return array (string => replacement)
* @access protected
*/
function _getSqlReplacements() {
return array();
}
/**
* Get sql to optimize a table
*
* @return array(string sql statement with %s token for table name, ...)
* @access protected
*/
function _getOptimizeStatements() {
return array();
}
/**
* Return a customized function for this database platform
*
* @param string $functionName
* @param array $args mixed the function arguments
* @return array GalleryStatus a status code
* string the function SQL
*/
function getFunctionSql($functionName, $args) {
return array(GalleryCoreApi::error(ERROR_NOT_IMPLEMENTED), null);
}
/**
* Extracts the class names from a given query
*
* Query should be something like
* '[GalleryItem::id] = ? AND [GalleryPhotoItem::id] = ?'
* Results would be: array('[GalleryItem]', '[GalleryPhotoItem]')
*
* @param string $query
* @return array GalleryStatus a status code
* array strings table names
*/
function extractClasses($query) {
preg_match_all('/\[([^:]*)::[^\]]*\]/', $query, $matches, PREG_PATTERN_ORDER);
$classes = array();
foreach ($matches[1] as $match) {
$classes['[' . $match . ']'] = 1;
}
return array(null, array_keys($classes));
}
/**
* Return storage profiling information in HTML format
* @return string HTML
*/
function getProfilingHtml() {
$extras =& $this->_getExtras();
return $extras->getProfilingHtml();
}
/**
* Return true if enough of this storage system is installed that there'll be a conflict if you
* try to do another install.
*
* @return array object GalleryStatus a status code
* boolean true if the tables are installed
*/
function isInstalled() {
$extras =& $this->_getExtras();
return $extras->isInstalled();
}
/**
* Optimize the database.
* @param array $tableNames (optional) of string tableName 1, tableName 2. Leave null to
* optimize all tables.
* @return object GalleryStatus a status code
*/
function optimize($tableNames=null) {
$extras =& $this->_getExtras();
return $extras->optimize($tableNames);
}
/**
* Return the number of rows that were affected by the last UPDATE/DELETE. Note that MySQL
* treats this a little differently than other databases; if you do an UPDATE operation and
* nothing is actually changed (eg. in the situation where the SET clauses in your UPDATE match
* the existing values) then mysql will return 0 affected rows.
*
* @return array object GalleryStatus a status code
* int the number of affected rows
*/
function getAffectedRows() {
$extras =& $this->_getExtras();
return $extras->getAffectedRows();
}
/**
* Describe the members, modules and parent of an entity
*
* @param string $entityName a class name
* @param boolean $tryAllModules true if we should scan all modules, not just the active ones
* @return array object GalleryStatus a status code
* entity associative array
*/
function describeEntity($entityName, $tryAllModules=false) {
$extras =& $this->_getExtras();
return $extras->describeEntity($entityName);
}
/**
* Execute a given SQL file against the database. Prefix table and column names as necessary.
* Split multiple commands in the file into separate Execute() calls.
*
* @param string $fileName absolute path of the sql file
* @return object GalleryStatus a status code
* @access protected
*/
function _executeSqlFile($fileName) {
$extras =& $this->_getExtras();
return $extras->executeSqlFile($fileName);
}
/**
* Translate all table and column names from [Entity::member] notation to table.column notation.
*
* @param string $query the raw query
* @return string the translated query
* @access protected
*/
function _translateQuery($query) {
/* Change '[Class::member]' to 'table.column' or 'alias.column' */
while (ereg('\[([[:alnum:]_=]*)::([[:alnum:]_]*)\]', $query, $regs)) {
$class = $regs[1];
$member = $regs[2];
list ($table, $alias) = $this->_translateTableName($class);
$column = $this->_translateColumnName($member);
if ($alias) {
$query = str_replace("[${class}::${member}]", "$alias.$column", $query);
} else if ($class) {
$query = str_replace("[${class}::${member}]", "$table.$column", $query);
} else {
$query = str_replace("[::${member}]", "$column", $query);
}
}
/* Change '[Class]' to 'table' */
while (ereg('\[([[:alnum:]_=]*)\]', $query, $regs)) {
$class = $regs[1];
list ($table, $alias) = $this->_translateTableName($class);
if ($alias == null) {
$query = str_replace("[${class}]", "$table", $query);
} else {
list ($ret, $as) = $this->getFunctionSql('AS', array());
if ($ret) {
/* XXX TODO: propagate this back up as a GalleryStatus */
return 'QUERY ERROR';
}
$query = str_replace("[${class}]", "$table $as $alias", $query);
}
}
return $query;
}
/**
* Translate a potentially unsafe column name into a safe one
*
* @param string $columnName the name of a column
* @return string a safe column name
* @access protected
*/
function _translateColumnName($columnName) {
return $this->_columnPrefix . $columnName;
}
/**
* Translate a potentially unsafe table name into a safe one by adding a prefix or suffix to
* avoid conflicting with a reserved word.
*
* eg:
* Comment => array(g2_Comment, null, Comment)
* Comment=1 => array(g2_Comment, C0, Comment)
*
* @param string $tableName the name of a table
* @return array string a safe table name
* an alias for this table
* the unsafe, but translated, table name
* @access protected
*/
function _translateTableName($tableName) {
/*
* Remove the the ubiquitous "Gallery" prefix, since it's not part of the schema name. For
* now we automatically translate the class name into the schema name by doing this. If
* this ever becomes a problem, we should start hand-writing the schema name instead and
* then pushing that into the interface classes so that we don't have to automatically
* generate the schema name (and get it wrong).
*/
$tableName = str_replace('Gallery', '', $tableName);
/* Other abbreviations to keep table names under Oracle's 30 character limit. */
$tableName = str_replace('Preferences', 'Prefs', $tableName);
$tableName = str_replace('Toolkit', 'Tk', $tableName);
$tableName = str_replace('TkOperation', 'TkOperatn', $tableName);
/*
* Deal with aliases, which will be in the form of "table=1", "table=2",
* etc. Translate "1" into "A", "2" into "B", etc.
*/
$split = explode('=', $tableName);
$alias = '';
if (count($split) > 1) {
list ($tableName, $number) = $split;
for ($i = 0; $i < strlen($tableName); $i++) {
$chr = $tableName[$i];
if ($chr >= 'A' && $chr <= 'Z') {
$alias .= $chr;
}
}
$alias = GalleryUtilities::strToLower($alias) . ($number - 1);
} else {
$tableName = $split[0];
$alias = null;
}
return array($this->_tablePrefix . $tableName, $alias, $tableName);
}
/**
* Describe all the members of a map
*
* @param string $mapName a map name
* @param boolean $tryAllModules try all modules, not just active ones
* @return array GalleryStatus a status code,
* array member name => member type
*/
function describeMap($mapName, $tryAllModules=false) {
global $gallery;
/* Note: keep these cache keys in sync with _clearMapMemberCache() */
$cacheKey = "GalleryStorage::describeMap()";
$cacheParams = array('type' => 'module',
'itemId' => 'GalleryStorage_describeMap',
'id' => '_all');
/* We only cache the results for active modules */
if (!$tryAllModules) {
if (!GalleryDataCache::containsKey($cacheKey)) {
$mapInfo =& GalleryDataCache::getFromDisk($cacheParams);
if (!empty($mapInfo)) {
GalleryDataCache::put($cacheKey, $mapInfo);
}
} else {
$mapInfo = GalleryDataCache::get($cacheKey);
}
}
if (!isset($mapInfo)) {
list ($ret, $moduleStatus) = GalleryCoreApi::fetchPluginStatus('module');
if ($ret) {
return array($ret, null);
}
$mapInfo = array();
foreach ($moduleStatus as $moduleId => $moduleInfo) {
if (!$tryAllModules && empty($moduleInfo['active'])) {
continue;
}
/*
* Don't use GalleryPlatform here because it can cause difficult-to-eliminate issues
* in the testing code when we use mock platforms. Once we have an abstraction layer
* around GalleryCoreApi we can change this.
*/
$moduleDir = GalleryCoreApi::getPluginBaseDir('module', $moduleId);
if ($ret) {
return array($ret, null);
}
$mapsFile = sprintf('%smodules/%s/classes/Maps.inc', $moduleDir, $moduleId);
if (file_exists($mapsFile)) {
include($mapsFile);
}
}
if (!$tryAllModules) {
GalleryDataCache::putToDisk($cacheParams, $mapInfo);
GalleryDataCache::put($cacheKey, $mapInfo);
}
}
if (!$tryAllModules && !isset($mapInfo[$mapName])) {
list ($ret, $mapInfo[$mapName]) = $this->describeMap($mapName, true);
if ($ret) {
return array($ret, null);
}
}
if (!isset($mapInfo[$mapName])) {
return array(GalleryCoreApi::error(ERROR_MISSING_VALUE, __FILE__, __LINE__,
"Undefined map: $mapName"),
null);
}
return array(null, $mapInfo[$mapName]);
}
/**
* Encode a blob of binary data into a form that's safe for a varchar column
*
* @param string $blob binary data
* @return database safe string
*/
function encodeBlob($blob) {
return $blob;
}
/**
* Decode a blob of binary data into a form that's safe for a varchar column
*
* @param string $blob database safe string
* @return binary data
*/
function decodeBlob($blob) {
return $blob;
}
/**
* Start tracing. If Gallery is in debug, this method will begin storing all output and routing
* it into Gallery's debug system.
* @access protected
*/
function _traceStart() {
global $gallery;
if ($gallery->getDebug()) {
ob_start();
}
}
/**
* Stop tracing. If Gallery is in debug, this will method will stop tracing.
* @access protected
*/
function _traceStop() {
global $gallery;
if ($gallery->getDebug()) {
$buf = ob_get_contents();
ob_end_clean();
$gallery->debug($buf);
}
}
/**
* Set Adodb debug mode.
* @param bool $debug
*/
function setDebug($debug) {
if (isset($this->_db)) {
$this->_db->debug = $debug;
}
if (isset($this->_nonTransactionalDb)) {
$this->_nonTransactionalDb->debug = $debug;
}
}
/**
* Cast the value to the proper member type when interacting with the database. Optionally also
* perform a UTF-8-safe truncation for strings.
*
* @todo CAST empty to NULL ? values from DB too?
*
* @param mixed $value the value
* @param array $memberData ('type' => STORAGE_TYPE_XXX constant,
* 'size' => STORAGE_SIZE_XXX constant)
* @param boolean $fromDb (optional) false if value is for SQL, true if value is from the DB
* @return mixed correctly typed value
*/
function _normalizeValue($value, $memberData, $fromDb=false) {
$notNull = !empty($memberData['notNull']) ||
( !empty($memberData['notNullEmptyAllowed']) && $this->_isEmptyAllowedForNotNullColumn);
if (is_null($value) && !$notNull) {
return $value;
}
$type = $memberData['type'];
if ($type & STORAGE_TYPE_BOOLEAN) {
/* Convert booleans to 1 : 0 */
return (int)!empty($value);
} else if ($type & STORAGE_TYPE_TIMESTAMP) {
/* Convert timestamps to the database representation */
return $this->_db->DBTimeStamp($value);
} else if ($type & STORAGE_TYPE_INTEGER) {
return (int)$value;
} else if ($type & STORAGE_TYPE_STRING) {
if (isset($value) && !$fromDb) {
$value = $this->_truncateString($value, $memberData['size']);
}
return (string)$value;
} else if ($type & STORAGE_TYPE_TEXT) {
return (string)$value;
} else if ($type & STORAGE_TYPE_BIT) {
if ($fromDb) {
return (int)$this->convertBitsToInt($value);
} else {
return $this->convertIntToBits((int)$value);
}
} else {
return $value;
}
}
/**
* Perform a UTF-8-safe truncation of the string to a size
*
* @param string $value the value
* @param int $size (as a STORAGE_SIZE_XXX constant)
* @param bool $lengthInBytes (optional) whether to interpret the size in bytes or in characters
* @return the truncated string
*/
function _truncateString($value, $size, $lengthInBytes=false) {
switch ($size) {
case STORAGE_SIZE_SMALL:
$size = 32;
break;
case STORAGE_SIZE_MEDIUM:
$size = 128;
break;
case STORAGE_SIZE_LARGE:
$size = 255;
break;
}
if ($lengthInBytes) {
return GalleryCoreApi::utf8Strcut($value, 0, $size);
} else {
return GalleryCoreApi::utf8Substring($value, 0, $size);
}
}
}
/**
* Container for database result set
* @package GalleryCore
* @subpackage Storage
*/
class GallerySearchResults {
/**
* Internal record set object
*
* @access private
* @var object ADORecordSet $_recordSet
*/
var $_recordSet;
/**
* Field names describing the record set
*
* @access private
* @var array(string name of field 1, ...) $fieldNames
*/
var $_fieldNames;
/**
* Gallery data type info array
*
* @access private
* @var array (string columnName => array data type info, ...) $mapInfo
*/
var $_mapInfo;
function GallerySearchResults($recordSet, $fieldNames=null, $mapInfo=null) {
$this->_recordSet = $recordSet;
$this->_fieldNames = $fieldNames;
$this->_mapInfo = $mapInfo;
}
/**
* The number of results from this search
*
* @return int the number of results
*/
function resultCount() {
return $this->_recordSet->RecordCount();
}
/**
* Return the next search result, as an associative array
*
* @return array the next result or false if EOF
*/
function nextResult() {
$row =& $this->_recordSet->FetchRow();
if (!empty($row) && !empty($this->_fieldNames) && !empty($this->_mapInfo)) {
/* Normalize fetched values */
global $gallery;
$storage =& $gallery->getStorage();
for ($i = 0; $i < count($row) && $i < count($this->_fieldNames); $i++) {
$field = $this->_fieldNames[$i];
$row[$i] = $storage->_normalizeValue($row[$i], $this->_mapInfo[$field], true);
}
}
return $row;
}
}
/**
* Container to store an sql fragment that can be passed to the db abstraction layer.
* @package GalleryCore
* @subpackage Storage
*/
class GallerySqlFragment {
/**
* Internal sql fragment
* @var string
* @access private
*/
var $_fragment;
/**
* Internal sql values
* @var array
* @access private
*/
var $_values;
/**
* @param string $fragment sql fragment
* @param array $values values.. variable number of parameters must match
* number of ? markers in sql fragment
*/
function GallerySqlFragment($fragment, $values) {
$this->_values = func_get_args();
$this->_fragment = array_shift($this->_values);
}
/**
* The sql fragment
* @return string
*/
function getFragment() {
return $this->_fragment;
}
/**
* Return the values that map into the sql fragment's ? markers
* @return array
*/
function getValues() {
return $this->_values;
}
}
/**
* Default Error Handler for ADOdb. Derived from adodb-errorhandler.inc.php which is
* (c) 2000, 2001 John Lim (jlim@natsoft.com.my). All rights reserved.
*
* @param string $dbms the RDBMS you are connecting to
* @param string $fn the name of the calling function (in uppercase)
* @param int $errno the native error number from the database
* @param string $errmsg the native error msg from the database
* @param mixed $p1 $fn specific parameter - see below
* @param mixed $p2 $fn specific parameter - see below
*/
function GalleryAdodbErrorHandler($dbms, $fn, $errno, $errmsg, $p1=false, $p2=false) {
switch($fn) {
case 'EXECUTE':
$sql = $p1;
$inputparams = $p2;
$s = "$dbms error: [$errno: $errmsg] in $fn(\"$sql\")\n";
break;
case 'PCONNECT':
case 'CONNECT':
$host = $p1;
$database = $p2;
$s = "$dbms error: [$errno: $errmsg] in $fn($host, ?, ?, $database)\n";
break;
default:
$s = "$dbms error: [$errno: $errmsg] in $fn($p1, $p2)\n";
break;
}
/*
* Log connection error somewhere
* 0 message is sent to PHP's system logger, using the Operating System's system
* logging mechanism or a file, depending on what the error_log configuration
* directive is set to.
* 1 message is sent by email to the address in the destination parameter.
* This is the only message type where the fourth parameter, extra_headers is used.
* This message type uses the same internal function as mail() does.
* 2 message is sent through the PHP debugging connection.
* This option is only available if remote debugging has been enabled.
* In this case, the destination parameter specifies the host name or IP address
* and optionally, port number, of the socket receiving the debug information.
* 3 message is appended to the file destination
*/
if (defined('ADODB_ERROR_LOG_TYPE')) {
$t = date('Y-m-d H:i:s');
if (defined('ADODB_ERROR_LOG_DEST'))
error_log("($t) $s", ADODB_ERROR_LOG_TYPE, ADODB_ERROR_LOG_DEST);
else
error_log("($t) $s", ADODB_ERROR_LOG_TYPE);
}
global $gallery;
if ($gallery->getDebug()) {
$gallery->debug($s);
}
}
/**
* MySQL extension of the GalleryStorage class.
* This object implements the hooks for saving and restoring objects in a MySQL database.
*
* @package GalleryCore
* @subpackage Storage
*/
class MySqlStorage extends GalleryStorage {
function MySqlStorage($config) {
$this->GalleryStorage($config);
if ($this->_type != 'mysql' /* mysqlt and mysqli are transactional */) {
$this->_isTransactional = true;
}
}
/**
* Return the type of this database
* @return string
*/
function getType() {
return 'mysql';
}
/**
* @see GalleryStorage::_setConnectionSettings
*/
function _setConnectionSettings(&$db) {
/* MySQL 4.1.0+ support UTF-8, for details, see: http://drupal.org/node/40515 */
$this->_serverInfo = ($this->_type == 'mysqli')
? mysqli_get_server_info($db->_connectionID) : mysql_get_server_info();
if ($this->_serverInfo && version_compare($this->_serverInfo, '4.1.0', '>=')) {
$this->_traceStart();
$recordSet = $db->execute('SET NAMES "utf8"');
$this->_traceStop();
if (!$recordSet) {
return GalleryCoreApi::error(ERROR_STORAGE_FAILURE);
}
}
return null;
}
/**
* @see GalleryStorage::_getSqlReplacements
*/
function _getSqlReplacements() {
/* 3.23.0-4.0.17 used TYPE, 5.1+ only accepts ENGINE, between accepts either */
$typeKeyword = ($this->_serverInfo && version_compare($this->_serverInfo, '4.0.18', '>='))
? 'ENGINE' : 'TYPE';
/* Use InnoDB for transactional tables */
$tableType = $this->_isTransactional ? 'InnoDB' : 'MyISAM';
/**
* @todo On next major bump of Module API, remove 'TYPE=DB_TABLE_TYPE' entry below.
* In revision 14158, we switched from replacing DB_TABLE_TYPE with the tableType to
* replacing it with {ENGINE|TYPE}=tableType. Handle both cases else installing older
* modules will fail.
*/
return array('TYPE=DB_TABLE_TYPE' => $typeKeyword . '=' . $tableType,
'DB_TABLE_TYPE' => $typeKeyword . '=' . $tableType);
}
/**
* @see GalleryStorage::getFunctionsSql
*/
function getFunctionSql($functionName, $args) {
switch($functionName) {
case 'CONCAT':
$sql = sprintf('CONCAT(%s)', implode(', ', $args));
break;
case 'BITAND':
$sql = $args[0] . ' & ' . $args[1];
break;
case 'BIT_OR':
$sql = 'BIT_OR(' . $args[0] . ')';
break;
case 'UNIX_TIMESTAMP':
$sql = sprintf('UNIX_TIMESTAMP(%s)', $args[0]);
break;
case 'AS':
$sql = 'AS';
break;
case 'SUBSTRING':
$sql = sprintf('SUBSTRING(%s)', implode(', ', $args));
break;
case 'RAND':
$sql = sprintf('RAND(%s)', empty($args) ? '' : $args[0]);
break;
case 'LIMIT':
$sql = $args[1] . ' LIMIT ' . $args[0];
break;
case 'CASE': // condition value (condition value)* else-value
if (count($args) == 3) {
$sql = sprintf('IF(%s)', implode(', ', $args));
} else {
$sql = array();
while (count($args) > 1) {
$sql[] = 'WHEN ' . array_shift($args) . ' THEN ' . array_shift($args);
}
$sql = 'CASE ' . implode(' ', $sql) . ' ELSE ' . $args[0] . ' END';
}
break;
case 'LIKE':
$sql = $args[0] . ' LIKE ' . $args[1];
break;
case 'MULTI_INSERT':
/*
* 0 - table name
* 1 - array of column names
* 2 - number of rows
*/
$markers = GalleryUtilities::makeMarkers(sizeof($args[1]));
$rowList = rtrim(str_repeat('(' . $markers . '), ', $args[2]), ', ');
$sql = 'INSERT INTO ' . $args[0] . ' (';
$sql .= join(', ', $args[1]);
$sql .= ') VALUES ' . $rowList;
break;
case 'AVG':
$sql = sprintf('AVG(%s)', $args[0]);
break;
default:
return array(GalleryCoreApi::error(ERROR_UNIMPLEMENTED, __FILE__, __LINE__,
$functionName . ' ' . implode(' ', $args)), null);
}
return array(null, $sql);
}
/**
* @see GalleryStorage::encodeBlob
*/
function encodeBlob($blob) {
if (!isset($this->_db)) {
list ($ret, $this->_db) = $this->_getConnection();
}
if ($this->_serverInfo && version_compare($this->_serverInfo, '4.1.0', '>=')) {
/* See: http://www.postgresql.org/docs/8.1/interactive/datatype-binary.html */
$blob = addcslashes($blob, "\000..\037\047\134\177..\377");
}
return $blob;
}
/**
* @see GalleryStorage::decodeBlob
*/
function decodeBlob($blob) {
if (!isset($this->_db)) {
list ($ret, $this->_db) = $this->_getConnection();
}
if ($this->_serverInfo && version_compare($this->_serverInfo, '4.1.0', '>=')) {
$blob = stripcslashes($blob);
}
return $blob;
}
/**
* Get database version.
* @return string version
*/
function getVersion() {
if (!isset($this->_db)) {
list ($ret, $this->_db) = $this->_getConnection();
if ($ret) {
return ($this->_type == 'mysqli'
? mysqli_get_client_info() : mysql_get_client_info()) . ' client';
}
}
return $this->_serverInfo;
}
/**
* @see GalleryStorage::_getOptimizeStatements
*/
function _getOptimizeStatements() {
return array('OPTIMIZE TABLE `%s`;', 'ANALYZE TABLE `%s`;');
}
/**
* @see GalleryStorage::getUniqueId
*/
function getUniqueId() {
/* Make sure we're using a non-transactional connection to avoid duplicating sequence ids */
$extras =& $this->_getExtras();
list ($ret, $db) = $extras->_getNonTransactionalDatabaseConnection();
if ($ret) {
return array($ret, null);
}
return $extras->_getUniqueIdWithConnection($db);
}
/**
* Truncate UTF-8 strings either to given character or to byte length depending on the MySQL
* version.
* @see GalleryStorage::_truncateString
*/
function _truncateString($value, $size, $lengthInBytes=false) {
static $lengthInBytes;
if (!isset($lengthInBytes)) {
$lengthInBytes =
$this->_serverInfo && version_compare($this->_serverInfo, '4.1.0', '<');
}
return parent::_truncateString($value, $size, $lengthInBytes);
}
}
?>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists