getMessage()."
".$error->getUserInfo(); } /** * Database class. Handles database operations. * * @todo Member visibility needs to be evaluated. Do we want strong encapsulation? */ class Database { var $conn; /** * Constructs a database and connects to the given Data Source Name. * Dies with connection error message on error. Fetch mode is set * to associative. * * @param string $dsn The data source name to connect to */ function __construct($dsn) { $this->conn =& MDB2::connect($dsn); if(PEAR::isError($this->conn)) { die($this->conn->getMessage()); } $this->conn->setFetchMode(MDB2_FETCHMODE_ASSOC); } /** * Executes a query on the database. * * @param string $query The query to execute * @return mixed */ function query($query) { $result =& $this->conn->query($query); $this->catchError($result); return $result; } /** * Executes a query and fetches all rows as 2d array. * * @param string $query The query to execute * @return mixed */ function getAll($query) { $result =& $this->conn->queryAll($query); $this->catchError($result); return $result; } /** * Executes a query and fetches the first column of first row. * * @param string $query The query to execute * @return mixed */ function getOne($query) { $result = $this->conn->queryOne($query); $this->catchError($result); return $result; } /** * Executes a query and fetches the first row as array. * * @param string $query The query to execute * @return mixed */ function getRow($query) { $result = $this->conn->queryRow($query); $this->catchError($result); return $result; } /** * Executes a query and fetches the first column of each row as array. * * @param string $query The query to execute * @return mixed */ function getColumn($query) { $result = $this->conn->queryCol($query); $this->catchError($result); return $result; } /** * Inserts values into a table. * * @param string $table The table to insert into * @param array $values Values to insert as key-value pairs * @return bool|MDB2_Error */ function insert($table, $values) { $this->conn->loadModule('Extended'); $result = $this->conn->extended->autoExecute($table, $values, MDB2_AUTOQUERY_INSERT); $this->catchError($result); return $this->conn->lastInsertID($table); } /** * Updates table with values. * * @param string $table The table to update * @param array $values Values to update as key-value pairs * @param string $where Query WHERE attributes * @return bool|MDB2_Error */ function update($table, $values, $where) { $this->conn->loadModule('Extended'); $result = $this->conn->extended->autoExecute($table, $values, MDB2_AUTOQUERY_UPDATE, $where); $this->catchError($result); } /** * Deletes from table. * * @param string $table The table to delete from * @param string $where Query WHERE attributes * @return bool|MDB2_Error */ function delete($table, $where) { $this->conn->loadModule('Extended'); $result = $this->conn->extended->autoExecute($table, NULL, MDB2_AUTOQUERY_DELETE, $where); $this->catchError($result); } /** * Begins database transaction. * * @return bool * @todo currently always returns true */ function beginTransaction() { $result = $this->conn->query("BEGIN"); $this->catchError($result); return true; } /** * Commits current transaction to database. * * @return bool * @todo currently always returns true */ function commit() { if($this->conn->inTransaction()) { $result = $this->conn->commit(); $this->catchError($result); return true; } } /** * Cancels any operations that have happened during this transaction. * * @return bool * @todo currently always returns true */ function rollback() { if($this->conn->inTransaction()) { $result = $this->conn->rollback(); $this->catchError($result); return true; } } /** * If transaction is currently open. * * @return int|bool */ function inTransaction() { return $this->conn->inTransaction(); } /** * Whether a DB implementation or its backend extension supports a given feature. * * @param string $feature Name of the feature * @return bool|string */ function supports($feature) { return $this->conn->supports($feature); } /** * Handles DB errors. If an error has occurred, dies with error message. * * @return void */ function catchError($result) { if(PEAR::isError($result)) { die($result->getMessage()); } } } ?>table = $table; $this->filename_column_name = ($this->table == 'pages')?('filename'):('url_title'); } /** * Gets tree. * * @param array $params Associative array of parameters: parent_id, level, depth, exclude_id, options * @return array * @todo better documentation */ function getTree($params = NULL) { global $db; global $site; $site_id = $site['id']; $params['parent_id'] = $params['parent_id'] ? $params['parent_id'] : 0; $params['level'] = $params['level'] ? $params['level'] : 0; $params['depth'] = $params['depth'] ? $params['depth'] : 999; $params['exclude_id'] = $params['exclude_id'] ? $params['exclude_id'] : 0; $params['options'] = $params['options'] ? $params['options'] : 'ORDER BY position ASC'; if($params['depth'] > $params['level']) { $tree = $db->getAll("SELECT * FROM " . $this->table . " WHERE parent_id = " . intval($params['parent_id']) . " " // SQLSAFE .($this->table == "pages" ? " AND deleted = 0 AND site_id = $site_id" : "") . ($params['options'] ? " " . mysql_real_escape_string($params['options']) : '')); if(count($tree)) { foreach($tree as $k => $v) { if($params['exclude_id'] != $v['id']) { $tree[$k]['path'] = $this->getPath(array('id' => $v['id'])); $tree[$k]['level'] = $params['level'] + 1; $tree[$k]['subs'] = $this->getTree(array( 'parent_id' => $v['id'], 'level' => $params['level'] + 1, 'depth' => $params['depth'], 'exclude_id' => $params['exclude_id'], 'options' => $params['options'] )); } else { unset($tree[$k]); } } } } return $tree; } /** * Gets the path. * * @param array $params Associative array of parameters: id, filename * @return array * @todo better documentation */ function getPath($params = NULL) { global $db; global $site; do { $path[] = $params[$this->filename_column_name]; $query = "SELECT $this->filename_column_name FROM " . $this->table . " WHERE id = (SELECT parent_id FROM ".$this->table." WHERE $this->filename_column_name LIKE '" . mysql_real_escape_string($params[$this->filename_column_name]) . "' AND deleted = 0 AND site_id = $site[id] LIMIT 1) AND deleted = 0 AND $this->filename_column_name NOT LIKE '".mysql_real_escape_string($params[$this->filename_column_name])."'".($this->table == 'pages' ? " AND site_id = $site[id]" :""); $params[$this->filename_column_name] = $db->getOne($query); } while ($params[$this->filename_column_name]); return array_reverse($path); } /** * Sorts children and puts them into $array. * * @param array $array The array to store children * @param int $parent_id ID of the parent page. Defaults to 0 * @param array $exclude_ids * @param string $parent_filename Filename of parent page * @return void * @todo better documentation */ function getSorted(&$array, $parent_id = 0, $exclude_ids = NULL, $parent_filename = NULL) { global $db; global $site; global $base_path; $children = $db->getAll("SELECT * FROM ".$this->table." WHERE parent_id = " . intval($parent_id) . " AND deleted = 0 AND site_id = $site[id] ORDER BY position" ); // SQLSAFE if(!count($exclude_ids)) $exclude_ids = array(); if(count($children) > 0) { foreach($children as $v) { if(array_search($v['id'], $exclude_ids) !== false) { $v['exclude'] = 1; } $v['level'] = $this->getLevel($v[$this->filename_column_name]); $v['level_space'] = str_repeat(" ", ($v['level'] - 1) * 5); $v['full_filename'] = ($parent_filename ? $parent_filename : rtrim($base_path,"/"))."/".$v[$this->filename_column_name]; $array[] = $v; $this->getSorted($array, $v['id'], $exclude_ids, $v['full_filename']); } } } /** * Sorts children and puts them into $array. * * @param array $array The array to store children * @param int $parent_id ID of the parent page. Defaults to 0 * @param array $exclude_ids * @param string $parent_filename Filename of parent page * @return void * @todo better documentation */ function getSortedForSite(&$array, $site_id, $parent_id = 0, $exclude_ids = NULL, $parent_filename = NULL) { global $db; global $base_path; $children = $db->getAll("SELECT * FROM ".$this->table." WHERE parent_id = $parent_id AND deleted = 0 AND site_id = $site_id ORDER BY position" ); if(!count($exclude_ids)) $exclude_ids = array(); if(count($children) > 0) { foreach($children as $v) { if(array_search($v['id'], $exclude_ids) !== false) { $v['exclude'] = 1; } $v['level'] = $this->getLevel($v[$this->filename_column_name]); $v['level_space'] = str_repeat(" ", ($v['level'] - 1) * 5); $v['full_filename'] = ($parent_filename ? $parent_filename : rtrim($base_path,"/"))."/".$v[$this->filename_column_name]; $array[] = $v; $this->getSortedForSite($array, $site_id, $v['id'], $exclude_ids, $v['full_filename']); } } } /** * Gets the path depth of the given file. * * @param string $filename Filename of the file * @return int */ function getLevel($filename) { return count($this->getPath(array($this->filename_column_name=>$filename))); } } ?>