diff options
author | Thomas Müller <thomas.mueller@tmit.eu> | 2013-07-30 00:37:05 +0200 |
---|---|---|
committer | Thomas Müller <thomas.mueller@tmit.eu> | 2013-07-30 00:37:05 +0200 |
commit | f658852276204fbf1a76d58ac2f585cf9db8c75d (patch) | |
tree | 5fbd6d93c7435022e8539065a45469bd249799ad /lib | |
parent | 085fdfec2f298fd89d0265d0f97edfdda5a12954 (diff) | |
parent | 7c6246fa451c4646dfaa1396dd37e0b3eb9706ba (diff) | |
download | nextcloud-server-f658852276204fbf1a76d58ac2f585cf9db8c75d.tar.gz nextcloud-server-f658852276204fbf1a76d58ac2f585cf9db8c75d.zip |
Merge branch 'master' into fixing-testGetById-master
Diffstat (limited to 'lib')
66 files changed, 2365 insertions, 5101 deletions
diff --git a/lib/MDB2/Driver/Datatype/sqlite3.php b/lib/MDB2/Driver/Datatype/sqlite3.php deleted file mode 100644 index ca4c1cbceb8..00000000000 --- a/lib/MDB2/Driver/Datatype/sqlite3.php +++ /dev/null @@ -1,385 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Robin Appelman - * @copyright 2011 Robin Appelman icewind1991@gmail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -require_once 'MDB2/Driver/Datatype/Common.php'; - -/** - * MDB2 SQLite driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - */ -class MDB2_Driver_Datatype_sqlite3 extends MDB2_Driver_Datatype_Common -{ - // {{{ _getCollationFieldDeclaration() - - /** - * Obtain DBMS specific SQL code portion needed to set the COLLATION - * of a field declaration to be used in statements like CREATE TABLE. - * - * @param string $collation name of the collation - * - * @return string DBMS specific SQL code portion needed to set the COLLATION - * of a field declaration. - */ - function _getCollationFieldDeclaration($collation) - { - return 'COLLATE '.$collation; - } - - // }}} - // {{{ getTypeDeclaration() - - /** - * Obtain DBMS specific SQL code portion needed to declare an text type - * field to be used in statements like CREATE TABLE. - * - * @param array $field associative array with the name of the properties - * of the field being declared as array indexes. Currently, the types - * of supported field properties are as follows: - * - * length - * Integer value that determines the maximum length of the text - * field. If this argument is missing the field should be - * declared to have the longest length allowed by the DBMS. - * - * default - * Text value to be used as default for this field. - * - * notnull - * Boolean flag that indicates whether this field is constrained - * to not be set to null. - * @return string DBMS specific SQL code portion that should be used to - * declare the specified field. - * @access public - */ - function getTypeDeclaration($field) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - switch ($field['type']) { - case 'text': - $length = !empty($field['length']) - ? $field['length'] : false; - $fixed = !empty($field['fixed']) ? $field['fixed'] : false; - return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')') - : ($length ? 'VARCHAR('.$length.')' : 'TEXT'); - case 'clob': - if (!empty($field['length'])) { - $length = $field['length']; - if ($length <= 255) { - return 'TINYTEXT'; - } elseif ($length <= 65532) { - return 'TEXT'; - } elseif ($length <= 16777215) { - return 'MEDIUMTEXT'; - } - } - return 'LONGTEXT'; - case 'blob': - if (!empty($field['length'])) { - $length = $field['length']; - if ($length <= 255) { - return 'TINYBLOB'; - } elseif ($length <= 65532) { - return 'BLOB'; - } elseif ($length <= 16777215) { - return 'MEDIUMBLOB'; - } - } - return 'LONGBLOB'; - case 'integer': - if (!empty($field['length'])) { - $length = $field['length']; - if ($length <= 2) { - return 'SMALLINT'; - } elseif ($length == 3 || $length == 4) { - return 'INTEGER'; - } elseif ($length > 4) { - return 'BIGINT'; - } - } - return 'INTEGER'; - case 'boolean': - return 'BOOLEAN'; - case 'date': - return 'DATE'; - case 'time': - return 'TIME'; - case 'timestamp': - return 'DATETIME'; - case 'float': - return 'DOUBLE'.($db->options['fixed_float'] ? '('. - ($db->options['fixed_float']+2).','.$db->options['fixed_float'].')' : ''); - case 'decimal': - $length = !empty($field['length']) ? $field['length'] : 18; - $scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places']; - return 'DECIMAL('.$length.','.$scale.')'; - } - return ''; - } - - // }}} - // {{{ _getIntegerDeclaration() - - /** - * Obtain DBMS specific SQL code portion needed to declare an integer type - * field to be used in statements like CREATE TABLE. - * - * @param string $name name the field to be declared. - * @param string $field associative array with the name of the properties - * of the field being declared as array indexes. - * Currently, the types of supported field - * properties are as follows: - * - * unsigned - * Boolean flag that indicates whether the field - * should be declared as unsigned integer if - * possible. - * - * default - * Integer value to be used as default for this - * field. - * - * notnull - * Boolean flag that indicates whether this field is - * constrained to not be set to null. - * @return string DBMS specific SQL code portion that should be used to - * declare the specified field. - * @access protected - */ - function _getIntegerDeclaration($name, $field) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $default = $autoinc = ''; - if (!empty($field['autoincrement'])) { - $autoinc = ' PRIMARY KEY AUTOINCREMENT'; - } elseif (array_key_exists('default', $field)) { - if ($field['default'] === '') { - $field['default'] = empty($field['notnull']) ? null : 0; - } - $default = ' DEFAULT '.$this->quote($field['default'], 'integer'); - } - - $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; - $unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED'; - $name = $db->quoteIdentifier($name, true); - if($autoinc) { - return $name.' '.$this->getTypeDeclaration($field).$autoinc; - }else{ - return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc; - } - } - - // }}} - // {{{ matchPattern() - - /** - * build a pattern matching string - * - * @access public - * - * @param array $pattern even keys are strings, odd are patterns (% and _) - * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future) - * @param string $field optional field name that is being matched against - * (might be required when emulating ILIKE) - * - * @return string SQL pattern - */ - function matchPattern($pattern, $operator = null, $field = null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $match = ''; - if (!is_null($operator)) { - $field = is_null($field) ? '' : $field.' '; - $operator = strtoupper($operator); - switch ($operator) { - // case insensitive - case 'ILIKE': - $match = $field.'LIKE '; - break; - // case sensitive - case 'LIKE': - $match = $field.'LIKE '; - break; - default: - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'not a supported operator type:'. $operator, __FUNCTION__); - } - } - $match.= "'"; - foreach ($pattern as $key => $value) { - if ($key % 2) { - $match.= $value; - } else { - $match.= $db->escapePattern($db->escape($value)); - } - } - $match.= "'"; - $match.= $this->patternEscapeString(); - return $match; - } - - // }}} - // {{{ _mapNativeDatatype() - - /** - * Maps a native array description of a field to a MDB2 datatype and length - * - * @param array $field native field description - * @return array containing the various possible types, length, sign, fixed - * @access public - */ - function _mapNativeDatatype($field) - { - $db_type = strtolower($field['type']); - $length = !empty($field['length']) ? $field['length'] : null; - $unsigned = !empty($field['unsigned']) ? $field['unsigned'] : null; - $fixed = null; - $type = array(); - switch ($db_type) { - case 'boolean': - $type[] = 'boolean'; - break; - case 'tinyint': - $type[] = 'integer'; - $type[] = 'boolean'; - if (preg_match('/^(is|has)/', $field['name'])) { - $type = array_reverse($type); - } - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 1; - break; - case 'smallint': - $type[] = 'integer'; - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 2; - break; - case 'mediumint': - $type[] = 'integer'; - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 3; - break; - case 'int': - case 'integer': - case 'serial': - $type[] = 'integer'; - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 4; - break; - case 'bigint': - case 'bigserial': - $type[] = 'integer'; - $unsigned = preg_match('/ unsigned/i', $field['type']); - $length = 8; - break; - case 'clob': - $type[] = 'clob'; - $fixed = false; - break; - case 'tinytext': - case 'mediumtext': - case 'longtext': - case 'text': - case 'varchar': - case 'varchar2': - $fixed = false; - case 'char': - $type[] = 'text'; - if ($length == '1') { - $type[] = 'boolean'; - if (preg_match('/^(is|has)/', $field['name'])) { - $type = array_reverse($type); - } - } elseif (strstr($db_type, 'text')) { - $type[] = 'clob'; - $type = array_reverse($type); - } - if ($fixed !== false) { - $fixed = true; - } - break; - case 'date': - $type[] = 'date'; - $length = null; - break; - case 'datetime': - case 'timestamp': - $type[] = 'timestamp'; - $length = null; - break; - case 'time': - $type[] = 'time'; - $length = null; - break; - case 'float': - case 'double': - case 'real': - $type[] = 'float'; - break; - case 'decimal': - case 'numeric': - $type[] = 'decimal'; - $length = $length.','.$field['decimal']; - break; - case 'tinyblob': - case 'mediumblob': - case 'longblob': - case 'blob': - $type[] = 'blob'; - $length = null; - break; - case 'year': - $type[] = 'integer'; - $type[] = 'date'; - $length = null; - break; - default: - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'unknown database attribute type: '.$db_type, __FUNCTION__); - } - - if ((int)$length <= 0) { - $length = null; - } - - return array($type, $length, $unsigned, $fixed); - } - - // }}} -} diff --git a/lib/MDB2/Driver/Function/sqlite3.php b/lib/MDB2/Driver/Function/sqlite3.php deleted file mode 100644 index 4147a48199f..00000000000 --- a/lib/MDB2/Driver/Function/sqlite3.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Robin Appelman - * @copyright 2011 Robin Appelman icewind1991@gmail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -require_once 'MDB2/Driver/Function/Common.php'; - -/** - * MDB2 SQLite driver for the function modules - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - */ -class MDB2_Driver_Function_sqlite3 extends MDB2_Driver_Function_Common -{ - // {{{ constructor - - /** - * Constructor - */ - function __construct($db_index) - { - parent::__construct($db_index); - // create all sorts of UDFs - } - - // {{{ now() - - /** - * Return string to call a variable with the current timestamp inside an SQL statement - * There are three special variables for current date and time. - * - * @return string to call a variable with the current timestamp - * @access public - */ - function now($type = 'timestamp') - { - switch ($type) { - case 'time': - return 'CURRENT_TIME'; - case 'date': - return 'CURRENT_DATE'; - case 'timestamp': - default: - return 'CURRENT_TIMESTAMP'; - } - } - - // }}} - // {{{ unixtimestamp() - - /** - * return string to call a function to get the unix timestamp from a iso timestamp - * - * @param string $expression - * - * @return string to call a variable with the timestamp - * @access public - */ - function unixtimestamp($expression) - { - return 'strftime("%s",'. $expression.', "utc")'; - } - - // }}} - // {{{ substring() - - /** - * return string to call a function to get a substring inside an SQL statement - * - * @return string to call a function to get a substring - * @access public - */ - function substring($value, $position = 1, $length = null) - { - if (!is_null($length)) { - return "substr($value, $position, $length)"; - } - return "substr($value, $position, length($value))"; - } - - // }}} - // {{{ random() - - /** - * return string to call a function to get random value inside an SQL statement - * - * @return return string to generate float between 0 and 1 - * @access public - */ - function random() - { - return '((RANDOM()+2147483648)/4294967296)'; - } - - // }}} - // {{{ replace() - - /** - * return string to call a function to get a replacement inside an SQL statement. - * - * @return string to call a function to get a replace - * @access public - */ - function replace($str, $from_str, $to_str) - { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'method not implemented', __FUNCTION__); - return $error; - } - - // }}} -} diff --git a/lib/MDB2/Driver/Manager/sqlite3.php b/lib/MDB2/Driver/Manager/sqlite3.php deleted file mode 100644 index 921153c17dd..00000000000 --- a/lib/MDB2/Driver/Manager/sqlite3.php +++ /dev/null @@ -1,1362 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Robin Appelman - * @copyright 2011 Robin Appelman icewind1991@gmail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -require_once 'MDB2/Driver/Manager/Common.php'; - -/** - * MDB2 SQLite driver for the management modules - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - * @author Lorenzo Alberton <l.alberton@quipo.it> - */ -class MDB2_Driver_Manager_sqlite3 extends MDB2_Driver_Manager_Common -{ - // {{{ createDatabase() - - /** - * create a new database - * - * @param string $name name of the database that should be created - * @param array $options array with charset info - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createDatabase($name, $options = array()) - { - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $database_file = $db->_getDatabaseFile($name); - if (file_exists($database_file)) { - return $db->raiseError(MDB2_ERROR_ALREADY_EXISTS, null, null, - 'database already exists', __FUNCTION__); - } - $php_errormsg = ''; - $database_file="$datadir/$database_file.db"; - $handle=new SQLite3($database_file); - if (!$handle) { - return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, - (isset($php_errormsg) ? $php_errormsg : 'could not create the database file'), __FUNCTION__); - } - //sqlite doesn't support the latin1 we use -// if (!empty($options['charset'])) { -// $query = 'PRAGMA encoding = ' . $db->quote($options['charset'], 'text'); -// $handle->exec($query); -// } - $handle->close(); - return MDB2_OK; - } - - // }}} - // {{{ dropDatabase() - - /** - * drop an existing database - * - * @param string $name name of the database that should be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropDatabase($name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $database_file = $db->_getDatabaseFile($name); - if (!@file_exists($database_file)) { - return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null, - 'database does not exist', __FUNCTION__); - } - $result = @unlink($database_file); - if (!$result) { - return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null, - (isset($php_errormsg) ? $php_errormsg : 'could not remove the database file'), __FUNCTION__); - } - return MDB2_OK; - } - - // }}} - // {{{ _getAdvancedFKOptions() - - /** - * Return the FOREIGN KEY query section dealing with non-standard options - * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... - * - * @param array $definition - * @return string - * @access protected - */ - function _getAdvancedFKOptions($definition) - { - $query = ''; - if (!empty($definition['match'])) { - $query .= ' MATCH '.$definition['match']; - } - if (!empty($definition['onupdate']) && (strtoupper($definition['onupdate']) != 'NO ACTION')) { - $query .= ' ON UPDATE '.$definition['onupdate']; - } - if (!empty($definition['ondelete']) && (strtoupper($definition['ondelete']) != 'NO ACTION')) { - $query .= ' ON DELETE '.$definition['ondelete']; - } - if (!empty($definition['deferrable'])) { - $query .= ' DEFERRABLE'; - } else { - $query .= ' NOT DEFERRABLE'; - } - if (!empty($definition['initiallydeferred'])) { - $query .= ' INITIALLY DEFERRED'; - } else { - $query .= ' INITIALLY IMMEDIATE'; - } - return $query; - } - - // }}} - // {{{ _getCreateTableQuery() - - /** - * Create a basic SQL query for a new table creation - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition of each field of the new table - * @param array $options An associative array of table options - * @return mixed string (the SQL query) on success, a MDB2 error on failure - * @see createTable() - */ - function _getCreateTableQuery($name, $fields, $options = array()) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - if (!$name) { - return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, - 'no valid table name specified', __FUNCTION__); - } - if (empty($fields)) { - return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, - 'no fields specified for table "'.$name.'"', __FUNCTION__); - } - $query_fields = $this->getFieldDeclarationList($fields); - if (PEAR::isError($query_fields)) { - return $query_fields; - } - if (!empty($options['foreign_keys'])) { - foreach ($options['foreign_keys'] as $fkname => $fkdef) { - if (empty($fkdef)) { - continue; - } - $query_fields.= ', CONSTRAINT '.$fkname.' FOREIGN KEY ('.implode(', ', array_keys($fkdef['fields'])).')'; - $query_fields.= ' REFERENCES '.$fkdef['references']['table'].' ('.implode(', ', array_keys($fkdef['references']['fields'])).')'; - $query_fields.= $this->_getAdvancedFKOptions($fkdef); - } - } - - $name = $db->quoteIdentifier($name, true); - $result = 'CREATE '; - if (!empty($options['temporary'])) { - $result .= $this->_getTemporaryTableQuery(); - } - $result .= " TABLE $name ($query_fields)"; - return $result; - } - - // }}} - // {{{ createTable() - - /** - * create a new table - * - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition - * of each field of the new table - * @param array $options An associative array of table options - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createTable($name, $fields, $options = array()) - { - $result = parent::createTable($name, $fields, $options); - if (PEAR::isError($result)) { - return $result; - } - // create triggers to enforce FOREIGN KEY constraints - if (!empty($options['foreign_keys'])) { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - foreach ($options['foreign_keys'] as $fkname => $fkdef) { - if (empty($fkdef)) { - continue; - } - //set actions to default if not set - $fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']); - $fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']); - - $trigger_names = array( - 'insert' => $fkname.'_insert_trg', - 'update' => $fkname.'_update_trg', - 'pk_update' => $fkname.'_pk_update_trg', - 'pk_delete' => $fkname.'_pk_delete_trg', - ); - - //create the [insert|update] triggers on the FK table - $table_fields = array_keys($fkdef['fields']); - $referenced_fields = array_keys($fkdef['references']['fields']); - $query = 'CREATE TRIGGER %s BEFORE %s ON '.$name - .' FOR EACH ROW BEGIN' - .' SELECT RAISE(ROLLBACK, \'%s on table "'.$name.'" violates FOREIGN KEY constraint "'.$fkname.'"\')' - .' WHERE (SELECT '; - $aliased_fields = array(); - foreach ($referenced_fields as $field) { - $aliased_fields[] = $fkdef['references']['table'] .'.'.$field .' AS '.$field; - } - $query .= implode(',', $aliased_fields) - .' FROM '.$fkdef['references']['table'] - .' WHERE '; - $conditions = array(); - for ($i=0; $i<count($table_fields); $i++) { - $conditions[] = $referenced_fields[$i] .' = NEW.'.$table_fields[$i]; - } - $query .= implode(' AND ', $conditions).') IS NULL; END;'; - $result = $db->exec(sprintf($query, $trigger_names['insert'], 'INSERT', 'insert')); - if (PEAR::isError($result)) { - return $result; - } - - $result = $db->exec(sprintf($query, $trigger_names['update'], 'UPDATE', 'update')); - if (PEAR::isError($result)) { - return $result; - } - - //create the ON [UPDATE|DELETE] triggers on the primary table - $restrict_action = 'SELECT RAISE(ROLLBACK, \'%s on table "'.$name.'" violates FOREIGN KEY constraint "'.$fkname.'"\')' - .' WHERE (SELECT '; - $aliased_fields = array(); - foreach ($table_fields as $field) { - $aliased_fields[] = $name .'.'.$field .' AS '.$field; - } - $restrict_action .= implode(',', $aliased_fields) - .' FROM '.$name - .' WHERE '; - $conditions = array(); - $new_values = array(); - $null_values = array(); - for ($i=0; $i<count($table_fields); $i++) { - $conditions[] = $table_fields[$i] .' = OLD.'.$referenced_fields[$i]; - $new_values[] = $table_fields[$i] .' = NEW.'.$referenced_fields[$i]; - $null_values[] = $table_fields[$i] .' = NULL'; - } - $conditions2 = array(); - for ($i=0; $i<count($referenced_fields); $i++) { - $conditions2[] = 'NEW.'.$referenced_fields[$i] .' <> OLD.'.$referenced_fields[$i]; - } - $restrict_action .= implode(' AND ', $conditions).') IS NOT NULL' - .' AND (' .implode(' OR ', $conditions2) .')'; - - $cascade_action_update = 'UPDATE '.$name.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions); - $cascade_action_delete = 'DELETE FROM '.$name.' WHERE '.implode(' AND ', $conditions); - $setnull_action = 'UPDATE '.$name.' SET '.implode(', ', $null_values).' WHERE '.implode(' AND ', $conditions); - - if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete']) { - $db->loadModule('Reverse', null, true); - $default_values = array(); - foreach ($table_fields as $table_field) { - $field_definition = $db->reverse->getTableFieldDefinition($name, $field); - if (PEAR::isError($field_definition)) { - return $field_definition; - } - $default_values[] = $table_field .' = '. $field_definition[0]['default']; - } - $setdefault_action = 'UPDATE '.$name.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions); - } - - $query = 'CREATE TRIGGER %s' - .' %s ON '.$fkdef['references']['table'] - .' FOR EACH ROW BEGIN '; - - if ('CASCADE' == $fkdef['onupdate']) { - $sql_update = sprintf($query, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . $cascade_action_update. '; END;'; - } elseif ('SET NULL' == $fkdef['onupdate']) { - $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action. '; END;'; - } elseif ('SET DEFAULT' == $fkdef['onupdate']) { - $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action. '; END;'; - } elseif ('NO ACTION' == $fkdef['onupdate']) { - $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . '; END;'; - } elseif ('RESTRICT' == $fkdef['onupdate']) { - $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . '; END;'; - } - if ('CASCADE' == $fkdef['ondelete']) { - $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . $cascade_action_delete. '; END;'; - } elseif ('SET NULL' == $fkdef['ondelete']) { - $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action. '; END;'; - } elseif ('SET DEFAULT' == $fkdef['ondelete']) { - $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action. '; END;'; - } elseif ('NO ACTION' == $fkdef['ondelete']) { - $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . '; END;'; - } elseif ('RESTRICT' == $fkdef['ondelete']) { - $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . '; END;'; - } - - if (PEAR::isError($result)) { - return $result; - } - $result = $db->exec($sql_delete); - if (PEAR::isError($result)) { - return $result; - } - $result = $db->exec($sql_update); - if (PEAR::isError($result)) { - return $result; - } - } - } - if (PEAR::isError($result)) { - return $result; - } - return MDB2_OK; - } - - // }}} - // {{{ dropTable() - - /** - * drop an existing table - * - * @param string $name name of the table that should be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropTable($name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - //delete the triggers associated to existing FK constraints - $constraints = $this->listTableConstraints($name); - if (!PEAR::isError($constraints) && !empty($constraints)) { - $db->loadModule('Reverse', null, true); - foreach ($constraints as $constraint) { - $definition = $db->reverse->getTableConstraintDefinition($name, $constraint); - if (!PEAR::isError($definition) && !empty($definition['foreign'])) { - $result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']); - if (PEAR::isError($result)) { - return $result; - } - } - } - } - - $name = $db->quoteIdentifier($name, true); - return $db->exec("DROP TABLE $name"); - } - - // }}} - // {{{ vacuum() - - /** - * Optimize (vacuum) all the tables in the db (or only the specified table) - * and optionally run ANALYZE. - * - * @param string $table table name (all the tables if empty) - * @param array $options an array with driver-specific options: - * - timeout [int] (in seconds) [mssql-only] - * - analyze [boolean] [pgsql and mysql] - * - full [boolean] [pgsql-only] - * - freeze [boolean] [pgsql-only] - * - * @return mixed MDB2_OK success, a MDB2 error on failure - * @access public - */ - function vacuum($table = null, $options = array()) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = 'VACUUM'; - if (!empty($table)) { - $query .= ' '.$db->quoteIdentifier($table, true); - } - return $db->exec($query); - } - - // }}} - // {{{ alterTable() - - /** - * alter an existing table - * - * @param string $name name of the table that is intended to be changed. - * @param array $changes associative array that contains the details of each type - * of change that is intended to be performed. The types of - * changes that are currently supported are defined as follows: - * - * name - * - * New name for the table. - * - * add - * - * Associative array with the names of fields to be added as - * indexes of the array. The value of each entry of the array - * should be set to another associative array with the properties - * of the fields to be added. The properties of the fields should - * be the same as defined by the MDB2 parser. - * - * - * remove - * - * Associative array with the names of fields to be removed as indexes - * of the array. Currently the values assigned to each entry are ignored. - * An empty array should be used for future compatibility. - * - * rename - * - * Associative array with the names of fields to be renamed as indexes - * of the array. The value of each entry of the array should be set to - * another associative array with the entry named name with the new - * field name and the entry named Declaration that is expected to contain - * the portion of the field declaration already in DBMS specific SQL code - * as it is used in the CREATE TABLE statement. - * - * change - * - * Associative array with the names of the fields to be changed as indexes - * of the array. Keep in mind that if it is intended to change either the - * name of a field and any other properties, the change array entries - * should have the new names of the fields as array indexes. - * - * The value of each entry of the array should be set to another associative - * array with the properties of the fields to that are meant to be changed as - * array entries. These entries should be assigned to the new values of the - * respective properties. The properties of the fields should be the same - * as defined by the MDB2 parser. - * - * Example - * array( - * 'name' => 'userlist', - * 'add' => array( - * 'quota' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * ) - * ), - * 'remove' => array( - * 'file_limit' => array(), - * 'time_limit' => array() - * ), - * 'change' => array( - * 'name' => array( - * 'length' => '20', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 20, - * ), - * ) - * ), - * 'rename' => array( - * 'sex' => array( - * 'name' => 'gender', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 1, - * 'default' => 'M', - * ), - * ) - * ) - * ) - * - * @param boolean $check indicates whether the function should just check if the DBMS driver - * can perform the requested table alterations if the value is true or - * actually perform them otherwise. - * @access public - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - */ - function alterTable($name, $changes, $check, $options = array()) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - foreach ($changes as $change_name => $change) { - switch ($change_name) { - case 'add': - case 'remove': - case 'change': - case 'name': - case 'rename': - break; - default: - return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, - 'change type "'.$change_name.'" not yet supported', __FUNCTION__); - } - } - - if ($check) { - return MDB2_OK; - } - - if (empty($changes['remove']) and empty($changes['rename']) and empty($changes['change']) ) {//if only rename or add changes are required, we can use ALTER TABLE - $query = ''; - if (!empty($changes['name'])) { - $change_name = $db->quoteIdentifier($changes['name'], true); - $query = 'RENAME TO ' . $change_name; - $db->exec("ALTER TABLE $name $query"); - } - - if (!empty($changes['add']) && is_array($changes['add'])) { - foreach ($changes['add'] as $field_name => $field) { - $query= 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field); - $db->exec("ALTER TABLE $name $query"); - } - } - return MDB2_OK; - } - - $db->loadModule('Reverse', null, true); - - // for other operations we need to emulate them with sqlite3 - $fields = $db->manager->listTableFields($name); - if (PEAR::isError($fields)) { - return $fields; - } - - $fields = array_flip($fields); - foreach ($fields as $field => $value) { - $definition = $db->reverse->getTableFieldDefinition($name, $field); - if (PEAR::isError($definition)) { - return $definition; - } - $fields[$field] = $definition[0]; - } - - $indexes = $db->manager->listTableIndexes($name); - if (PEAR::isError($indexes)) { - return $indexes; - } - - $indexes = array_flip($indexes); - foreach ($indexes as $index => $value) { - $definition = $db->reverse->getTableIndexDefinition($name, $index); - if (PEAR::isError($definition)) { - return $definition; - } - $indexes[$index] = $definition; - } - - $constraints = $db->manager->listTableConstraints($name); - if (PEAR::isError($constraints)) { - return $constraints; - } - - if (!array_key_exists('foreign_keys', $options)) { - $options['foreign_keys'] = array(); - } - $constraints = array_flip($constraints); - foreach ($constraints as $constraint => $value) { - if (!empty($definition['primary'])) { - if (!array_key_exists('primary', $options)) { - $options['primary'] = $definition['fields']; - //remove from the $constraint array, it's already handled by createTable() - unset($constraints[$constraint]); - } - } else { - $c_definition = $db->reverse->getTableConstraintDefinition($name, $constraint); - if (PEAR::isError($c_definition)) { - return $c_definition; - } - if (!empty($c_definition['foreign'])) { - if (!array_key_exists($constraint, $options['foreign_keys'])) { - $options['foreign_keys'][$constraint] = $c_definition; - } - //remove from the $constraint array, it's already handled by createTable() - unset($constraints[$constraint]); - } else { - $constraints[$constraint] = $c_definition; - } - } - } - - $name_new = $name; - $create_order = $select_fields = array_keys($fields); - foreach ($changes as $change_name => $change) { - switch ($change_name) { - case 'add': - foreach ($change as $field_name => $field) { - $fields[$field_name] = $field; - $create_order[] = $field_name; - } - break; - case 'remove': - foreach ($change as $field_name => $field) { - unset($fields[$field_name]); - $select_fields = array_diff($select_fields, array($field_name)); - $create_order = array_diff($create_order, array($field_name)); - } - break; - case 'change': - foreach ($change as $field_name => $field) { - $fields[$field_name] = $field['definition']; - } - break; - case 'name': - $name_new = $change; - break; - case 'rename': - foreach ($change as $field_name => $field) { - unset($fields[$field_name]); - $fields[$field['name']] = $field['definition']; - $create_order[array_search($field_name, $create_order)] = $field['name']; - } - break; - default: - return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, - 'change type "'.$change_name.'" not yet supported', __FUNCTION__); - } - } - - //rename the old table so we can create the new one - $db->exec("ALTER TABLE $name RENAME TO __$name"); - $data = null; - - - $result = $this->createTable($name_new, $fields, $options); - if (PEAR::isError($result)) { - return $result; - } - - //these seem to only give errors - -// foreach ($indexes as $index => $definition) { -// $this->createIndex($name_new, $index, $definition); -// } - -// foreach ($constraints as $constraint => $definition) { -// $this->createConstraint($name_new, $constraint, $definition); -// } - - //fill the new table with data from the old one - if (!empty($select_fields)) { - $query = 'INSERT INTO '.$db->quoteIdentifier($name_new, true); - $query.= '('.implode(', ', array_slice(array_keys($fields), 0, count($select_fields))).')'; - $query .= ' SELECT '.implode(', ', $select_fields).' FROM '.$db->quoteIdentifier('__'.$name, true); - $db->exec($query); - } - -// if (!empty($select_fields) && !empty($data)) { -// $query = 'INSERT INTO '.$db->quoteIdentifier($name_new, true); -// $query.= '('.implode(', ', array_slice(array_keys($fields), 0, count($select_fields))).')'; -// $query.=' VALUES (?'.str_repeat(', ?', (count($select_fields) - 1)).')'; -// $stmt =$db->prepare($query, null, MDB2_PREPARE_MANIP); -// if (PEAR::isError($stmt)) { -// return $stmt; -// } -// foreach ($data as $row) { -// $result = $stmt->execute($row); -// if (PEAR::isError($result)) { -// return $result; -// } -// } -// } - - //remove the old table - $result = $this->dropTable('__'.$name); - if (PEAR::isError($result)) { - return $result; - } - return MDB2_OK; - } - - // }}} - // {{{ listDatabases() - - /** - * list all databases - * - * @return mixed array of database names on success, a MDB2 error on failure - * @access public - */ - function listDatabases() - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'list databases is not supported', __FUNCTION__); - } - - // }}} - // {{{ listUsers() - - /** - * list all users - * - * @return mixed array of user names on success, a MDB2 error on failure - * @access public - */ - function listUsers() - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'list databases is not supported', __FUNCTION__); - } - - // }}} - // {{{ listViews() - - /** - * list all views in the current database - * - * @return mixed array of view names on success, a MDB2 error on failure - * @access public - */ - function listViews($dummy=null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name FROM sqlite_master WHERE type='view' AND sql NOT NULL"; - $result = $db->queryCol($query); - if (PEAR::isError($result)) { - return $result; - } - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - // }}} - // {{{ listTableViews() - - /** - * list the views in the database that reference a given table - * - * @param string table for which all referenced views should be found - * @return mixed array of view names on success, a MDB2 error on failure - * @access public - */ - function listTableViews($table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; - $views = $db->queryAll($query, array('text', 'text'), MDB2_FETCHMODE_ASSOC); - if (PEAR::isError($views)) { - return $views; - } - $result = array(); - foreach ($views as $row) { - if (preg_match("/^create view .* \bfrom\b\s+\b{$table}\b /i", $row['sql'])) { - if (!empty($row['name'])) { - $result[$row['name']] = true; - } - } - } - - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_change_key_case($result, $db->options['field_case']); - } - return array_keys($result); - } - - // }}} - // {{{ listTables() - - /** - * list all tables in the current database - * - * @return mixed array of table names on success, a MDB2 error on failure - * @access public - */ - function listTables($dummy=null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL AND name!='sqlite_sequence' ORDER BY name"; - $table_names = $db->queryCol($query); - if (PEAR::isError($table_names)) { - return $table_names; - } - $result = array(); - foreach ($table_names as $table_name) { - if (!$this->_fixSequenceName($table_name, true)) { - $result[] = $table_name; - } - } - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - // }}} - // {{{ listTableFields() - - /** - * list all fields in a table in the current database - * - * @param string $table name of table that should be used in method - * @return mixed array of field names on success, a MDB2 error on failure - * @access public - */ - function listTableFields($table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $result = $db->loadModule('Reverse', null, true); - if (PEAR::isError($result)) { - return $result; - } - $query = "SELECT sql FROM sqlite_master WHERE type='table' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= 'name='.$db->quote($table, 'text'); - } - $sql = $db->queryOne($query); - if (PEAR::isError($sql)) { - return $sql; - } - $columns = $db->reverse->_getTableColumns($sql); - $fields = array(); - foreach ($columns as $column) { - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - if ($db->options['field_case'] == CASE_LOWER) { - $column['name'] = strtolower($column['name']); - } else { - $column['name'] = strtoupper($column['name']); - } - } else { - $column = array_change_key_case($column, $db->options['field_case']); - } - $fields[] = $column['name']; - } - return $fields; - } - - // }}} - // {{{ listTableTriggers() - - /** - * list all triggers in the database that reference a given table - * - * @param string table for which all referenced triggers should be found - * @return mixed array of trigger names on success, a MDB2 error on failure - * @access public - */ - function listTableTriggers($table = null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name FROM sqlite_master WHERE type='trigger' AND sql NOT NULL"; - if (!is_null($table)) { - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= ' AND LOWER(tbl_name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= ' AND tbl_name='.$db->quote($table, 'text'); - } - } - $result = $db->queryCol($query); - if (PEAR::isError($result)) { - return $result; - } - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - // }}} - // {{{ createIndex() - - /** - * Get the stucture of a field into an array - * - * @param string $table name of the table on which the index is to be created - * @param string $name name of the index to be created - * @param array $definition associative array that defines properties of the index to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the index fields as array - * indexes. Each entry of this array is set to another type of associative - * array that specifies properties of the index that are specific to - * each field. - * - * Currently, only the sorting property is supported. It should be used - * to define the sorting direction of the index. It may be set to either - * ascending or descending. - * - * Not all DBMS support index sorting direction configuration. The DBMS - * drivers of those that do not support it ignore this property. Use the - * function support() to determine whether the DBMS driver can manage indexes. - - * Example - * array( - * 'fields' => array( - * 'user_name' => array( - * 'sorting' => 'ascending' - * ), - * 'last_login' => array() - * ) - * ) - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createIndex($table, $name, $definition) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $table = $db->quoteIdentifier($table, true); - $name = $db->getIndexName($name); - $query = "CREATE INDEX $name ON $table"; - $fields = array(); - foreach ($definition['fields'] as $field_name => $field) { - $field_string = $field_name; - if (!empty($field['sorting'])) { - switch ($field['sorting']) { - case 'ascending': - $field_string.= ' ASC'; - break; - case 'descending': - $field_string.= ' DESC'; - break; - } - } - $fields[] = $field_string; - } - $query .= ' ('.implode(', ', $fields) . ')'; - return $db->exec($query); - } - - // }}} - // {{{ dropIndex() - - /** - * drop existing index - * - * @param string $table name of table that should be used in method - * @param string $name name of the index to be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropIndex($table, $name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $name = $db->getIndexName($name); - return $db->exec("DROP INDEX $name"); - } - - // }}} - // {{{ listTableIndexes() - - /** - * list all indexes in a table - * - * @param string $table name of table that should be used in method - * @return mixed array of index names on success, a MDB2 error on failure - * @access public - */ - function listTableIndexes($table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $table = $db->quote($table, 'text'); - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(tbl_name)='.strtolower($table); - } else { - $query.= "tbl_name=$table"; - } - $query.= " AND sql NOT NULL ORDER BY name"; - $indexes = $db->queryCol($query, 'text'); - if (PEAR::isError($indexes)) { - return $indexes; - } - - $result = array(); - foreach ($indexes as $sql) { - if (preg_match("/^create index ([^ ]+) on /i", $sql, $tmp)) { - $index = $this->_fixIndexName($tmp[1]); - if (!empty($index)) { - $result[$index] = true; - } - } - } - - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_change_key_case($result, $db->options['field_case']); - } - return array_keys($result); - } - - // }}} - // {{{ createConstraint() - - /** - * create a constraint on a table - * - * @param string $table name of the table on which the constraint is to be created - * @param string $name name of the constraint to be created - * @param array $definition associative array that defines properties of the constraint to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the constraint fields as array - * constraints. Each entry of this array is set to another type of associative - * array that specifies properties of the constraint that are specific to - * each field. - * - * Example - * array( - * 'fields' => array( - * 'user_name' => array(), - * 'last_login' => array() - * ) - * ) - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createConstraint($table, $name, $definition) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - if (!empty($definition['primary'])) { - return $db->manager->alterTable($table, array(), false, array('primary' => $definition['fields'])); - } - - if (!empty($definition['foreign'])) { - return $db->manager->alterTable($table, array(), false, array('foreign_keys' => array($name => $definition))); - } - - $table = $db->quoteIdentifier($table, true); - $name = $db->getIndexName($name); - $query = "CREATE UNIQUE INDEX $name ON $table"; - $fields = array(); - foreach ($definition['fields'] as $field_name => $field) { - $field_string = $field_name; - if (!empty($field['sorting'])) { - switch ($field['sorting']) { - case 'ascending': - $field_string.= ' ASC'; - break; - case 'descending': - $field_string.= ' DESC'; - break; - } - } - $fields[] = $field_string; - } - $query .= ' ('.implode(', ', $fields) . ')'; - return $db->exec($query); - } - - // }}} - // {{{ dropConstraint() - - /** - * drop existing constraint - * - * @param string $table name of table that should be used in method - * @param string $name name of the constraint to be dropped - * @param string $primary hint if the constraint is primary - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropConstraint($table, $name, $primary = false) - { - if ($primary || $name == 'PRIMARY') { - return $this->alterTable($table, array(), false, array('primary' => null)); - } - - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - //is it a FK constraint? If so, also delete the associated triggers - $db->loadModule('Reverse', null, true); - $definition = $db->reverse->getTableConstraintDefinition($table, $name); - if (!PEAR::isError($definition) && !empty($definition['foreign'])) { - //first drop the FK enforcing triggers - $result = $this->_dropFKTriggers($table, $name, $definition['references']['table']); - if (PEAR::isError($result)) { - return $result; - } - //then drop the constraint itself - return $this->alterTable($table, array(), false, array('foreign_keys' => array($name => null))); - } - - $name = $db->getIndexName($name); - return $db->exec("DROP INDEX $name"); - } - - // }}} - // {{{ _dropFKTriggers() - - /** - * Drop the triggers created to enforce the FOREIGN KEY constraint on the table - * - * @param string $table table name - * @param string $fkname FOREIGN KEY constraint name - * @param string $referenced_table referenced table name - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access private - */ - function _dropFKTriggers($table, $fkname, $referenced_table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $triggers = $this->listTableTriggers($table); - $triggers2 = $this->listTableTriggers($referenced_table); - if (!PEAR::isError($triggers2) && !PEAR::isError($triggers)) { - $triggers = array_merge($triggers, $triggers2); - $pattern = '/^'.$fkname.'(_pk)?_(insert|update|delete)_trg$/i'; - foreach ($triggers as $trigger) { - if (preg_match($pattern, $trigger)) { - $result = $db->exec('DROP TRIGGER '.$trigger); - if (PEAR::isError($result)) { - return $result; - } - } - } - } - return MDB2_OK; - } - - // }}} - // {{{ listTableConstraints() - - /** - * list all constraints in a table - * - * @param string $table name of table that should be used in method - * @return mixed array of constraint names on success, a MDB2 error on failure - * @access public - */ - function listTableConstraints($table) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $table = $db->quote($table, 'text'); - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(tbl_name)='.strtolower($table); - } else { - $query.= "tbl_name=$table"; - } - $query.= " AND sql NOT NULL ORDER BY name"; - $indexes = $db->queryCol($query, 'text'); - if (PEAR::isError($indexes)) { - return $indexes; - } - - $result = array(); - foreach ($indexes as $sql) { - if (preg_match("/^create unique index ([^ ]+) on /i", $sql, $tmp)) { - $index = $this->_fixIndexName($tmp[1]); - if (!empty($index)) { - $result[$index] = true; - } - } - } - - // also search in table definition for PRIMARY KEYs... - $query = "SELECT sql FROM sqlite_master WHERE type='table' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)='.strtolower($table); - } else { - $query.= "name=$table"; - } - $query.= " AND sql NOT NULL ORDER BY name"; - $table_def = $db->queryOne($query, 'text'); - if (PEAR::isError($table_def)) { - return $table_def; - } - if (preg_match("/\bPRIMARY\s+KEY\b/i", $table_def, $tmp)) { - $result['primary'] = true; - } - - // ...and for FOREIGN KEYs - if (preg_match_all("/\bCONSTRAINT\b\s+([^\s]+)\s+\bFOREIGN\s+KEY/imsx", $table_def, $tmp)) { - foreach ($tmp[1] as $fk) { - $result[$fk] = true; - } - } - - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_change_key_case($result, $db->options['field_case']); - } - return array_keys($result); - } - - // }}} - // {{{ createSequence() - - /** - * create sequence - * - * @param string $seq_name name of the sequence to be created - * @param string $start start value of the sequence; default is 1 - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function createSequence($seq_name, $start = 1) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true); - $seqcol_name = $db->quoteIdentifier($db->options['seqcol_name'], true); - $query = "CREATE TABLE $sequence_name ($seqcol_name INTEGER PRIMARY KEY DEFAULT 0 NOT NULL)"; - $res = $db->exec($query); - if (PEAR::isError($res)) { - return $res; - } - if ($start == 1) { - return MDB2_OK; - } - $res = $db->exec("INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')'); - if (!PEAR::isError($res)) { - return MDB2_OK; - } - // Handle error - $result = $db->exec("DROP TABLE $sequence_name"); - if (PEAR::isError($result)) { - return $db->raiseError($result, null, null, - 'could not drop inconsistent sequence table', __FUNCTION__); - } - return $db->raiseError($res, null, null, - 'could not create sequence table', __FUNCTION__); - } - - // }}} - // {{{ dropSequence() - - /** - * drop existing sequence - * - * @param string $seq_name name of the sequence to be dropped - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function dropSequence($seq_name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true); - return $db->exec("DROP TABLE $sequence_name"); - } - - // }}} - // {{{ listSequences() - - /** - * list all sequences in the current database - * - * @return mixed array of sequence names on success, a MDB2 error on failure - * @access public - */ - function listSequences($dummy=null) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name"; - $table_names = $db->queryCol($query); - if (PEAR::isError($table_names)) { - return $table_names; - } - $result = array(); - foreach ($table_names as $table_name) { - if ($sqn = $this->_fixSequenceName($table_name, true)) { - $result[] = $sqn; - } - } - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); - } - return $result; - } - - // }}} -} diff --git a/lib/MDB2/Driver/Native/sqlite3.php b/lib/MDB2/Driver/Native/sqlite3.php deleted file mode 100644 index 344d523bdf3..00000000000 --- a/lib/MDB2/Driver/Native/sqlite3.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Robin Appelman - * @copyright 2011 Robin Appelman icewind1991@gmail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ -require_once 'MDB2/Driver/Native/Common.php'; - -/** - * MDB2 SQLite driver for the native module - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - */ -class MDB2_Driver_Native_sqlite extends MDB2_Driver_Native_Common -{ -} diff --git a/lib/MDB2/Driver/Reverse/sqlite3.php b/lib/MDB2/Driver/Reverse/sqlite3.php deleted file mode 100644 index 97037809549..00000000000 --- a/lib/MDB2/Driver/Reverse/sqlite3.php +++ /dev/null @@ -1,586 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Robin Appelman - * @copyright 2011 Robin Appelman icewind1991@gmail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -require_once 'MDB2/Driver/Reverse/Common.php'; - -/** - * MDB2 SQlite driver for the schema reverse engineering module - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - */ -class MDB2_Driver_Reverse_sqlite3 extends MDB2_Driver_Reverse_Common -{ - /** - * Remove SQL comments from the field definition - * - * @access private - */ - function _removeComments($sql) { - $lines = explode("\n", $sql); - foreach ($lines as $k => $line) { - $pieces = explode('--', $line); - if (count($pieces) > 1 && (substr_count($pieces[0], '\'') % 2) == 0) { - $lines[$k] = substr($line, 0, strpos($line, '--')); - } - } - return implode("\n", $lines); - } - - /** - * - */ - function _getTableColumns($sql) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - $start_pos = strpos($sql, '('); - $end_pos = strrpos($sql, ')'); - $column_def = substr($sql, $start_pos+1, $end_pos-$start_pos-1); - // replace the decimal length-places-separator with a colon - $column_def = preg_replace('/(\d),(\d)/', '\1:\2', $column_def); - $column_def = $this->_removeComments($column_def); - $column_sql = explode(',', $column_def); - $columns = array(); - $count = count($column_sql); - if ($count == 0) { - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'unexpected empty table column definition list', __FUNCTION__); - } - $regexp = '/^\s*([^\s]+) +(CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|SMALLINT|INT|INTEGER|DECIMAL|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( ?\(([1-9][0-9]*)(:([1-9][0-9]*))?\))?( NULL| NOT NULL)?( UNSIGNED)?( NULL| NOT NULL)?( PRIMARY KEY)?( AUTOINCREMENT)?( DEFAULT (\'[^\']*\'|[^ ]+))?( NULL| NOT NULL)?( PRIMARY KEY)?(\s*\-\-.*)?$/i'; - $regexp2 = '/^\s*([^ ]+) +(PRIMARY|UNIQUE|CHECK)$/i'; - for ($i=0, $j=0; $i<$count; ++$i) { - if (!preg_match($regexp, trim($column_sql[$i]), $matches)) { - if (!preg_match($regexp2, trim($column_sql[$i]))) { - continue; - } - return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'unexpected table column SQL definition: "'.$column_sql[$i].'"', __FUNCTION__); - } - $columns[$j]['name'] = trim($matches[1], implode('', $db->identifier_quoting)); - $columns[$j]['type'] = strtolower($matches[2]); - if (isset($matches[4]) && strlen($matches[4])) { - $columns[$j]['length'] = $matches[4]; - } - if (isset($matches[6]) && strlen($matches[6])) { - $columns[$j]['decimal'] = $matches[6]; - } - if (isset($matches[8]) && strlen($matches[8])) { - $columns[$j]['unsigned'] = true; - } - if (isset($matches[10]) && strlen($matches[10])) { - $columns[$j]['autoincrement'] = true; - $columns[$j]['notnull']=true; - } - if (isset($matches[10]) && strlen($matches[10])) { - $columns[$j]['autoincrement'] = true; - $columns[$j]['notnull']=true; - } - if (isset($matches[13]) && strlen($matches[13])) { - $default = $matches[13]; - if (strlen($default) && $default[0]=="'") { - $default = str_replace("''", "'", substr($default, 1, strlen($default)-2)); - } - if ($default === 'NULL') { - $default = null; - } - $columns[$j]['default'] = $default; - } - if (isset($matches[7]) && strlen($matches[7])) { - $columns[$j]['notnull'] = ($matches[7] === ' NOT NULL'); - } else if (isset($matches[9]) && strlen($matches[9])) { - $columns[$j]['notnull'] = ($matches[9] === ' NOT NULL'); - } else if (isset($matches[14]) && strlen($matches[14])) { - $columns[$j]['notnull'] = ($matches[14] === ' NOT NULL'); - } - ++$j; - } - return $columns; - } - - // {{{ getTableFieldDefinition() - - /** - * Get the stucture of a field into an array - * - * @param string $table_name name of table that should be used in method - * @param string $field_name name of field that should be used in method - * @return mixed data array on success, a MDB2 error on failure. - * The returned array contains an array for each field definition, - * with (some of) these indices: - * [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type] - * @access public - */ - function getTableFieldDefinition($table_name, $field_name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - list($schema, $table) = $this->splitTableSchema($table_name); - - $result = $db->loadModule('Datatype', null, true); - if (PEAR::isError($result)) { - return $result; - } - $query = "SELECT sql FROM sqlite_master WHERE type='table' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= 'name='.$db->quote($table, 'text'); - } - $sql = $db->queryOne($query); - if (PEAR::isError($sql)) { - return $sql; - } - $columns = $this->_getTableColumns($sql); - foreach ($columns as $column) { - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - if ($db->options['field_case'] == CASE_LOWER) { - $column['name'] = strtolower($column['name']); - } else { - $column['name'] = strtoupper($column['name']); - } - } else { - $column = array_change_key_case($column, $db->options['field_case']); - } - if ($field_name == $column['name']) { - $mapped_datatype = $db->datatype->mapNativeDatatype($column); - if (PEAR::isError($mapped_datatype)) { - return $mapped_datatype; - } - list($types, $length, $unsigned, $fixed) = $mapped_datatype; - $notnull = false; - if (!empty($column['notnull'])) { - $notnull = $column['notnull']; - } - $default = false; - if (array_key_exists('default', $column)) { - $default = $column['default']; - if (is_null($default) && $notnull) { - $default = ''; - } - } - $autoincrement = false; - if (!empty($column['autoincrement'])) { - $autoincrement = true; - } - - $definition[0] = array( - 'notnull' => $notnull, - 'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type']) - ); - if (!is_null($length)) { - $definition[0]['length'] = $length; - } - if (!is_null($unsigned)) { - $definition[0]['unsigned'] = $unsigned; - } - if (!is_null($fixed)) { - $definition[0]['fixed'] = $fixed; - } - if ($default !== false) { - $definition[0]['default'] = $default; - } - if ($autoincrement !== false) { - $definition[0]['autoincrement'] = $autoincrement; - } - foreach ($types as $key => $type) { - $definition[$key] = $definition[0]; - if ($type == 'clob' || $type == 'blob') { - unset($definition[$key]['default']); - } - $definition[$key]['type'] = $type; - $definition[$key]['mdb2type'] = $type; - } - return $definition; - } - } - - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table column', __FUNCTION__); - } - - // }}} - // {{{ getTableIndexDefinition() - - /** - * Get the stucture of an index into an array - * - * @param string $table_name name of table that should be used in method - * @param string $index_name name of index that should be used in method - * @return mixed data array on success, a MDB2 error on failure - * @access public - */ - function getTableIndexDefinition($table_name, $index_name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - list($schema, $table) = $this->splitTableSchema($table_name); - - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text'); - } else { - $query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text'); - } - $query.= ' AND sql NOT NULL ORDER BY name'; - $index_name_mdb2 = $db->getIndexName($index_name); - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $qry = sprintf($query, $db->quote(strtolower($index_name_mdb2), 'text')); - } else { - $qry = sprintf($query, $db->quote($index_name_mdb2, 'text')); - } - $sql = $db->queryOne($qry, 'text'); - if (PEAR::isError($sql) || empty($sql)) { - // fallback to the given $index_name, without transformation - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $qry = sprintf($query, $db->quote(strtolower($index_name), 'text')); - } else { - $qry = sprintf($query, $db->quote($index_name, 'text')); - } - $sql = $db->queryOne($qry, 'text'); - } - if (PEAR::isError($sql)) { - return $sql; - } - if (!$sql) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table index', __FUNCTION__); - } - - $sql = strtolower($sql); - $start_pos = strpos($sql, '('); - $end_pos = strrpos($sql, ')'); - $column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1); - $column_names = explode(',', $column_names); - - if (preg_match("/^create unique/", $sql)) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table index', __FUNCTION__); - } - - $definition = array(); - $count = count($column_names); - for ($i=0; $i<$count; ++$i) { - $column_name = strtok($column_names[$i], ' '); - $collation = strtok(' '); - $definition['fields'][$column_name] = array( - 'position' => $i+1 - ); - if (!empty($collation)) { - $definition['fields'][$column_name]['sorting'] = - ($collation=='ASC' ? 'ascending' : 'descending'); - } - } - - if (empty($definition['fields'])) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table index', __FUNCTION__); - } - return $definition; - } - - // }}} - // {{{ getTableConstraintDefinition() - - /** - * Get the stucture of a constraint into an array - * - * @param string $table_name name of table that should be used in method - * @param string $constraint_name name of constraint that should be used in method - * @return mixed data array on success, a MDB2 error on failure - * @access public - */ - function getTableConstraintDefinition($table_name, $constraint_name) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - list($schema, $table) = $this->splitTableSchema($table_name); - - $query = "SELECT sql FROM sqlite_master WHERE type='index' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text'); - } else { - $query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text'); - } - $query.= ' AND sql NOT NULL ORDER BY name'; - $constraint_name_mdb2 = $db->getIndexName($constraint_name); - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $qry = sprintf($query, $db->quote(strtolower($constraint_name_mdb2), 'text')); - } else { - $qry = sprintf($query, $db->quote($constraint_name_mdb2, 'text')); - } - $sql = $db->queryOne($qry, 'text'); - if (PEAR::isError($sql) || empty($sql)) { - // fallback to the given $index_name, without transformation - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $qry = sprintf($query, $db->quote(strtolower($constraint_name), 'text')); - } else { - $qry = sprintf($query, $db->quote($constraint_name, 'text')); - } - $sql = $db->queryOne($qry, 'text'); - } - if (PEAR::isError($sql)) { - return $sql; - } - //default values, eventually overridden - $definition = array( - 'primary' => false, - 'unique' => false, - 'foreign' => false, - 'check' => false, - 'fields' => array(), - 'references' => array( - 'table' => '', - 'fields' => array(), - ), - 'onupdate' => '', - 'ondelete' => '', - 'match' => '', - 'deferrable' => false, - 'initiallydeferred' => false, - ); - if (!$sql) { - $query = "SELECT sql FROM sqlite_master WHERE type='table' AND "; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text'); - } else { - $query.= 'name='.$db->quote($table, 'text'); - } - $query.= " AND sql NOT NULL ORDER BY name"; - $sql = $db->queryOne($query, 'text'); - if (PEAR::isError($sql)) { - return $sql; - } - if ($constraint_name == 'primary') { - // search in table definition for PRIMARY KEYs - if (preg_match("/\bPRIMARY\s+KEY\b\s*\(([^)]+)/i", $sql, $tmp)) { - $definition['primary'] = true; - $definition['fields'] = array(); - $column_names = explode(',', $tmp[1]); - $colpos = 1; - foreach ($column_names as $column_name) { - $definition['fields'][trim($column_name)] = array( - 'position' => $colpos++ - ); - } - return $definition; - } - if (preg_match("/\"([^\"]+)\"[^\,\"]+\bPRIMARY\s+KEY\b[^\,\)]*/i", $sql, $tmp)) { - $definition['primary'] = true; - $definition['fields'] = array(); - $column_names = explode(',', $tmp[1]); - $colpos = 1; - foreach ($column_names as $column_name) { - $definition['fields'][trim($column_name)] = array( - 'position' => $colpos++ - ); - } - return $definition; - } - } else { - // search in table definition for FOREIGN KEYs - $pattern = "/\bCONSTRAINT\b\s+%s\s+ - \bFOREIGN\s+KEY\b\s*\(([^\)]+)\)\s* - \bREFERENCES\b\s+([^\s]+)\s*\(([^\)]+)\)\s* - (?:\bMATCH\s*([^\s]+))?\s* - (?:\bON\s+UPDATE\s+([^\s,\)]+))?\s* - (?:\bON\s+DELETE\s+([^\s,\)]+))?\s* - /imsx"; - $found_fk = false; - if (preg_match(sprintf($pattern, $constraint_name_mdb2), $sql, $tmp)) { - $found_fk = true; - } elseif (preg_match(sprintf($pattern, $constraint_name), $sql, $tmp)) { - $found_fk = true; - } - if ($found_fk) { - $definition['foreign'] = true; - $definition['match'] = 'SIMPLE'; - $definition['onupdate'] = 'NO ACTION'; - $definition['ondelete'] = 'NO ACTION'; - $definition['references']['table'] = $tmp[2]; - $column_names = explode(',', $tmp[1]); - $colpos = 1; - foreach ($column_names as $column_name) { - $definition['fields'][trim($column_name)] = array( - 'position' => $colpos++ - ); - } - $referenced_cols = explode(',', $tmp[3]); - $colpos = 1; - foreach ($referenced_cols as $column_name) { - $definition['references']['fields'][trim($column_name)] = array( - 'position' => $colpos++ - ); - } - if (isset($tmp[4])) { - $definition['match'] = $tmp[4]; - } - if (isset($tmp[5])) { - $definition['onupdate'] = $tmp[5]; - } - if (isset($tmp[6])) { - $definition['ondelete'] = $tmp[6]; - } - return $definition; - } - } - $sql = false; - } - if (!$sql) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - $constraint_name . ' is not an existing table constraint', __FUNCTION__); - } - - $sql = strtolower($sql); - $start_pos = strpos($sql, '('); - $end_pos = strrpos($sql, ')'); - $column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1); - $column_names = explode(',', $column_names); - - if (!preg_match("/^create unique/", $sql)) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - $constraint_name . ' is not an existing table constraint', __FUNCTION__); - } - - $definition['unique'] = true; - $count = count($column_names); - for ($i=0; $i<$count; ++$i) { - $column_name = strtok($column_names[$i], " "); - $collation = strtok(" "); - $definition['fields'][$column_name] = array( - 'position' => $i+1 - ); - if (!empty($collation)) { - $definition['fields'][$column_name]['sorting'] = - ($collation=='ASC' ? 'ascending' : 'descending'); - } - } - - if (empty($definition['fields'])) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - $constraint_name . ' is not an existing table constraint', __FUNCTION__); - } - return $definition; - } - - // }}} - // {{{ getTriggerDefinition() - - /** - * Get the structure of a trigger into an array - * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change the returned value - * at any time until labelled as non-experimental - * - * @param string $trigger name of trigger that should be used in method - * @return mixed data array on success, a MDB2 error on failure - * @access public - */ - function getTriggerDefinition($trigger) - { - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - $query = "SELECT name as trigger_name, - tbl_name AS table_name, - sql AS trigger_body, - NULL AS trigger_type, - NULL AS trigger_event, - NULL AS trigger_comment, - 1 AS trigger_enabled - FROM sqlite_master - WHERE type='trigger'"; - if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $query.= ' AND LOWER(name)='.$db->quote(strtolower($trigger), 'text'); - } else { - $query.= ' AND name='.$db->quote($trigger, 'text'); - } - $types = array( - 'trigger_name' => 'text', - 'table_name' => 'text', - 'trigger_body' => 'text', - 'trigger_type' => 'text', - 'trigger_event' => 'text', - 'trigger_comment' => 'text', - 'trigger_enabled' => 'boolean', - ); - $def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC); - if (PEAR::isError($def)) { - return $def; - } - if (empty($def)) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing trigger', __FUNCTION__); - } - if (preg_match("/^create\s+(?:temp|temporary)?trigger\s+(?:if\s+not\s+exists\s+)?.*(before|after)?\s+(insert|update|delete)/Uims", $def['trigger_body'], $tmp)) { - $def['trigger_type'] = strtoupper($tmp[1]); - $def['trigger_event'] = strtoupper($tmp[2]); - } - return $def; - } - - // }}} - // {{{ tableInfo() - - /** - * Returns information about a table - * - * @param string $result a string containing the name of a table - * @param int $mode a valid tableInfo mode - * - * @return array an associative array with the information requested. - * A MDB2_Error object on failure. - * - * @see MDB2_Driver_Common::tableInfo() - * @since Method available since Release 1.7.0 - */ - function tableInfo($result, $mode = null) - { - if (is_string($result)) { - return parent::tableInfo($result, $mode); - } - - $db =$this->getDBInstance(); - if (PEAR::isError($db)) { - return $db; - } - - return $db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null, - 'This DBMS can not obtain tableInfo from result sets', __FUNCTION__); - } -} diff --git a/lib/MDB2/Driver/sqlite3.php b/lib/MDB2/Driver/sqlite3.php deleted file mode 100644 index 693ceffa01c..00000000000 --- a/lib/MDB2/Driver/sqlite3.php +++ /dev/null @@ -1,1332 +0,0 @@ -<?php -/** - * ownCloud - * - * @author Robin Appelman - * @copyright 2011 Robin Appelman icewind1991@gmail.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * MDB2 SQLite3 driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - */ -class MDB2_Driver_sqlite3 extends MDB2_Driver_Common -{ - // {{{ properties - public $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false); - - public $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"'); - - public $_lasterror = ''; - - public $fix_assoc_fields_names = false; - - // }}} - // {{{ constructor - - /** - * Constructor - */ - function __construct() - { - parent::__construct(); - - $this->phptype = 'sqlite3'; - $this->dbsyntax = 'sqlite'; - - $this->supported['sequences'] = 'emulated'; - $this->supported['indexes'] = true; - $this->supported['affected_rows'] = true; - $this->supported['summary_functions'] = true; - $this->supported['order_by_text'] = true; - $this->supported['current_id'] = 'emulated'; - $this->supported['limit_queries'] = true; - $this->supported['LOBs'] = true; - $this->supported['replace'] = true; - $this->supported['transactions'] = false; - $this->supported['savepoints'] = false; - $this->supported['sub_selects'] = true; - $this->supported['triggers'] = true; - $this->supported['auto_increment'] = true; - $this->supported['primary_key'] = false; // requires alter table implementation - $this->supported['result_introspection'] = false; // not implemented - $this->supported['prepared_statements'] = true; - $this->supported['identifier_quoting'] = true; - $this->supported['pattern_escaping'] = false; - $this->supported['new_link'] = false; - - $this->options['DBA_username'] = false; - $this->options['DBA_password'] = false; - $this->options['base_transaction_name'] = '___php_MDB2_sqlite_auto_commit_off'; - $this->options['fixed_float'] = 0; - $this->options['database_path'] = ''; - $this->options['database_extension'] = ''; - $this->options['server_version'] = ''; - $this->options['max_identifiers_length'] = 128; //no real limit - } - - // }}} - // {{{ errorInfo() - - /** - * This method is used to collect information about an error - * - * @param integer $error - * @return array - * @access public - */ - function errorInfo($error = null) - { - $native_code = null; - if ($this->connection) { - $native_code = $this->connection->lastErrorCode(); - } - $native_msg = html_entity_decode($this->_lasterror); - - // PHP 5.2+ prepends the function name to $php_errormsg, so we need - // this hack to work around it, per bug #9599. - $native_msg = preg_replace('/^sqlite[a-z_]+\(\)[^:]*: /', '', $native_msg); - - if (is_null($error)) { - static $error_regexps; - if (empty($error_regexps)) { - $error_regexps = array( - '/^no such table:/' => MDB2_ERROR_NOSUCHTABLE, - '/^no such index:/' => MDB2_ERROR_NOT_FOUND, - '/^(table|index) .* already exists$/' => MDB2_ERROR_ALREADY_EXISTS, - '/PRIMARY KEY must be unique/i' => MDB2_ERROR_CONSTRAINT, - '/is not unique/' => MDB2_ERROR_CONSTRAINT, - '/columns .* are not unique/i' => MDB2_ERROR_CONSTRAINT, - '/uniqueness constraint failed/' => MDB2_ERROR_CONSTRAINT, - '/may not be NULL/' => MDB2_ERROR_CONSTRAINT_NOT_NULL, - '/^no such column:/' => MDB2_ERROR_NOSUCHFIELD, - '/no column named/' => MDB2_ERROR_NOSUCHFIELD, - '/column not present in both tables/i' => MDB2_ERROR_NOSUCHFIELD, - '/^near ".*": syntax error$/' => MDB2_ERROR_SYNTAX, - '/[0-9]+ values for [0-9]+ columns/i' => MDB2_ERROR_VALUE_COUNT_ON_ROW, - ); - } - foreach ($error_regexps as $regexp => $code) { - if (preg_match($regexp, $native_msg)) { - $error = $code; - break; - } - } - } - return array($error, $native_code, $native_msg); - } - - // }}} - // {{{ escape() - - /** - * Quotes a string so it can be safely used in a query. It will quote - * the text so it can safely be used within a query. - * - * @param string the input string to quote - * @param bool escape wildcards - * - * @return string quoted string - * - * @access public - */ - public function escape($text, $escape_wildcards = false) - { - if($this->connection) { - return $this->connection->escapeString($text); - }else{ - return str_replace("'", "''", $text);//TODO; more - } - } - - // }}} - // {{{ beginTransaction() - - /** - * Start a transaction or set a savepoint. - * - * @param string name of a savepoint to set - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function beginTransaction($savepoint = null) - { - $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); - if (!is_null($savepoint)) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'savepoints are not supported', __FUNCTION__); - } elseif ($this->in_transaction) { - return MDB2_OK; //nothing to do - } - if (!$this->destructor_registered && $this->opened_persistent) { - $this->destructor_registered = true; - register_shutdown_function('MDB2_closeOpenTransactions'); - } - $query = 'BEGIN TRANSACTION '.$this->options['base_transaction_name']; - $result =$this->_doQuery($query, true); - if (PEAR::isError($result)) { - return $result; - } - $this->in_transaction = true; - return MDB2_OK; - } - - // }}} - // {{{ commit() - - /** - * Commit the database changes done during a transaction that is in - * progress or release a savepoint. This function may only be called when - * auto-committing is disabled, otherwise it will fail. Therefore, a new - * transaction is implicitly started after committing the pending changes. - * - * @param string name of a savepoint to release - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function commit($savepoint = null) - { - $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); - if (!$this->in_transaction) { - return $this->raiseError(MDB2_ERROR_INVALID, null, null, - 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__); - } - if (!is_null($savepoint)) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'savepoints are not supported', __FUNCTION__); - } - - $query = 'COMMIT TRANSACTION '.$this->options['base_transaction_name']; - $result =$this->_doQuery($query, true); - if (PEAR::isError($result)) { - return $result; - } - $this->in_transaction = false; - return MDB2_OK; - } - - // }}} - // {{{ - - /** - * Cancel any database changes done during a transaction or since a specific - * savepoint that is in progress. This function may only be called when - * auto-committing is disabled, otherwise it will fail. Therefore, a new - * transaction is implicitly started after canceling the pending changes. - * - * @param string name of a savepoint to rollback to - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function rollback($savepoint = null) - { - $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); - if (!$this->in_transaction) { - return $this->raiseError(MDB2_ERROR_INVALID, null, null, - 'rollback cannot be done changes are auto committed', __FUNCTION__); - } - if (!is_null($savepoint)) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'savepoints are not supported', __FUNCTION__); - } - - $query = 'ROLLBACK TRANSACTION '.$this->options['base_transaction_name']; - $result =$this->_doQuery($query, true); - if (PEAR::isError($result)) { - return $result; - } - $this->in_transaction = false; - return MDB2_OK; - } - - // }}} - // {{{ function setTransactionIsolation() - - /** - * Set the transacton isolation level. - * - * @param string standard isolation level - * READ UNCOMMITTED (allows dirty reads) - * READ COMMITTED (prevents dirty reads) - * REPEATABLE READ (prevents nonrepeatable reads) - * SERIALIZABLE (prevents phantom reads) - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - * @since 2.1.1 - */ - function setTransactionIsolation($isolation, $options=array()) - { - $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true)); - switch ($isolation) { - case 'READ UNCOMMITTED': - $isolation = 0; - break; - case 'READ COMMITTED': - case 'REPEATABLE READ': - case 'SERIALIZABLE': - $isolation = 1; - break; - default: - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'isolation level is not supported: '.$isolation, __FUNCTION__); - } - - $query = "PRAGMA read_uncommitted=$isolation"; - return $this->_doQuery($query, true); - } - - // }}} - // {{{ getDatabaseFile() - - /** - * Builds the string with path+dbname+extension - * - * @return string full database path+file - * @access protected - */ - function _getDatabaseFile($database_name) - { - if ($database_name === '' || $database_name === ':memory:') { - return $database_name; - } - return $this->options['database_path'].$database_name.$this->options['database_extension']; - } - - // }}} - // {{{ connect() - - /** - * Connect to the database - * - * @return true on success, MDB2 Error Object on failure - **/ - function connect() - { - if($this->connection instanceof SQLite3) { - return MDB2_OK; - } - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - $database_file = $this->_getDatabaseFile($this->database_name); - if (is_resource($this->connection)) { - //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0 - if (MDB2::areEquals($this->connected_dsn, $this->dsn) - && $this->connected_database_name == $database_file - && $this->opened_persistent == $this->options['persistent'] - ) { - return MDB2_OK; - } - $this->disconnect(false); - } - - if (!PEAR::loadExtension($this->phptype)) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__); - } - - if (empty($this->database_name)) { - return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, - 'unable to establish a connection', __FUNCTION__); - } - - if ($database_file !== ':memory:') { - if(!strpos($database_file, '.db')) { - $database_file="$datadir/$database_file.db"; - } - if (!file_exists($database_file)) { - if (!touch($database_file)) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'Could not create database file', __FUNCTION__); - } - if (!isset($this->dsn['mode']) - || !is_numeric($this->dsn['mode']) - ) { - $mode = 0644; - } else { - $mode = octdec($this->dsn['mode']); - } - if (!chmod($database_file, $mode)) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'Could not be chmodded database file', __FUNCTION__); - } - if (!file_exists($database_file)) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'Could not be found database file', __FUNCTION__); - } - } - if (!is_file($database_file)) { - return $this->raiseError(MDB2_ERROR_INVALID, null, null, - 'Database is a directory name', __FUNCTION__); - } - if (!is_readable($database_file)) { - return $this->raiseError(MDB2_ERROR_ACCESS_VIOLATION, null, null, - 'Could not read database file', __FUNCTION__); - } - } - - $php_errormsg = ''; - $this->connection = new SQLite3($database_file); - if(is_callable(array($this->connection, 'busyTimeout'))) {//busy timout is only available in php>=5.3 - $this->connection->busyTimeout(60000); - } - $this->_lasterror = $this->connection->lastErrorMsg(); - if (!$this->connection) { - return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, - 'unable to establish a connection', __FUNCTION__); - } - - if ($this->fix_assoc_fields_names || - $this->options['portability'] & MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) { - $this->connection->exec("PRAGMA short_column_names = 1"); - $this->fix_assoc_fields_names = true; - } - - $this->connected_dsn = $this->dsn; - $this->connected_database_name = $database_file; - $this->opened_persistent = $this->getoption('persistent'); - $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype; - - return MDB2_OK; - } - - // }}} - // {{{ databaseExists() - - /** - * check if given database name is exists? - * - * @param string $name name of the database that should be checked - * - * @return mixed true/false on success, a MDB2 error on failure - * @access public - */ - function databaseExists($name) - { - $database_file = $this->_getDatabaseFile($name); - $result = file_exists($database_file); - return $result; - } - - // }}} - // {{{ disconnect() - - /** - * Log out and disconnect from the database. - * - * @param boolean $force if the disconnect should be forced even if the - * connection is opened persistently - * @return mixed true on success, false if not connected and error - * object on error - * @access public - */ - function disconnect($force = true) - { - if ($this->connection instanceof SQLite3) { - if ($this->in_transaction) { - $dsn = $this->dsn; - $database_name = $this->database_name; - $persistent = $this->options['persistent']; - $this->dsn = $this->connected_dsn; - $this->database_name = $this->connected_database_name; - $this->options['persistent'] = $this->opened_persistent; - $this->rollback(); - $this->dsn = $dsn; - $this->database_name = $database_name; - $this->options['persistent'] = $persistent; - } - - if (!$this->opened_persistent || $force) { - $this->connection->close(); - } - } else { - return false; - } - return parent::disconnect($force); - } - - // }}} - // {{{ _doQuery() - - /** - * Execute a query - * @param string $query query - * @param boolean $is_manip if the query is a manipulation query - * @param resource $connection - * @param string $database_name - * @return result or error object - * @access protected - */ - function _doQuery($query, $is_manip = false, $connection = null, $database_name = null) - { - $this->last_query = $query; - $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre')); - if ($result) { - if (PEAR::isError($result)) { - return $result; - } - $query = $result; - } - if ($this->options['disable_query']) { - $result = $is_manip ? 0 : null; - return $result; - } - $result=$this->connection->query($query.';'); - $this->_lasterror = $this->connection->lastErrorMsg(); - - if (!$result) { - $err =$this->raiseError(null, null, null, - 'Could not execute statement', __FUNCTION__); - return $err; - } - - $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result)); - return $result; - } - - // }}} - // {{{ _affectedRows() - - /** - * Returns the number of rows affected - * - * @param resource $result - * @param resource $connection - * @return mixed MDB2 Error Object or the number of rows affected - * @access private - */ - function _affectedRows($connection, $result = null) - { - return $this->connection->changes(); - } - - // }}} - // {{{ _modifyQuery() - - /** - * Changes a query string for various DBMS specific reasons - * - * @param string $query query to modify - * @param boolean $is_manip if it is a DML query - * @param integer $limit limit the number of rows - * @param integer $offset start reading from given offset - * @return string modified query - * @access protected - */ - function _modifyQuery($query, $is_manip, $limit, $offset) - { - if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) { - if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { - $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', - 'DELETE FROM \1 WHERE 1=1', $query); - } - } - if ($limit > 0 - && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query) - ) { - $query = rtrim($query); - if (substr($query, -1) == ';') { - $query = substr($query, 0, -1); - } - if ($is_manip) { - $query.= " LIMIT $limit"; - } else { - $query.= " LIMIT $offset,$limit"; - } - } - return $query; - } - - // }}} - // {{{ getServerVersion() - - /** - * return version information about the server - * - * @param bool $native determines if the raw version string should be returned - * @return mixed array/string with version information or MDB2 error object - * @access public - */ - function getServerVersion($native = false) - { - $server_info = false; - if ($this->connected_server_info) { - $server_info = $this->connected_server_info; - } elseif ($this->options['server_version']) { - $server_info = $this->options['server_version']; - } elseif (function_exists('sqlite_libversion')) { - $server_info = @sqlite_libversion(); - } - if (!$server_info) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, - 'Requires either the "server_version" option or the sqlite_libversion() function', __FUNCTION__); - } - // cache server_info - $this->connected_server_info = $server_info; - if (!$native) { - $tmp = explode('.', $server_info, 3); - $server_info = array( - 'major' => isset($tmp[0]) ? $tmp[0] : null, - 'minor' => isset($tmp[1]) ? $tmp[1] : null, - 'patch' => isset($tmp[2]) ? $tmp[2] : null, - 'extra' => null, - 'native' => $server_info, - ); - } - return $server_info; - } - - // }}} - // {{{ replace() - - /** - * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT - * query, except that if there is already a row in the table with the same - * key field values, the old row is deleted before the new row is inserted. - * - * The REPLACE type of query does not make part of the SQL standards. Since - * practically only SQLite implements it natively, this type of query is - * emulated through this method for other DBMS using standard types of - * queries inside a transaction to assure the atomicity of the operation. - * - * @access public - * - * @param string $table name of the table on which the REPLACE query will - * be executed. - * @param array $fields associative array that describes the fields and the - * values that will be inserted or updated in the specified table. The - * indexes of the array are the names of all the fields of the table. The - * values of the array are also associative arrays that describe the - * values and other properties of the table fields. - * - * Here follows a list of field properties that need to be specified: - * - * value: - * Value to be assigned to the specified field. This value may be - * of specified in database independent type format as this - * function can perform the necessary datatype conversions. - * - * Default: - * this property is required unless the Null property - * is set to 1. - * - * type - * Name of the type of the field. Currently, all types Metabase - * are supported except for clob and blob. - * - * Default: no type conversion - * - * null - * Boolean property that indicates that the value for this field - * should be set to null. - * - * The default value for fields missing in INSERT queries may be - * specified the definition of a table. Often, the default value - * is already null, but since the REPLACE may be emulated using - * an UPDATE query, make sure that all fields of the table are - * listed in this function argument array. - * - * Default: 0 - * - * key - * Boolean property that indicates that this field should be - * handled as a primary key or at least as part of the compound - * unique index of the table that will determine the row that will - * updated if it exists or inserted a new row otherwise. - * - * This function will fail if no key field is specified or if the - * value of a key field is set to null because fields that are - * part of unique index they may not be null. - * - * Default: 0 - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - */ - function replace($table, $fields) - { - $count = count($fields); - $query = $values = ''; - $keys = $colnum = 0; - for (reset($fields); $colnum < $count; next($fields), $colnum++) { - $name = key($fields); - if ($colnum > 0) { - $query .= ','; - $values.= ','; - } - $query.= $this->quoteIdentifier($name, true); - if (isset($fields[$name]['null']) && $fields[$name]['null']) { - $value = 'NULL'; - } else { - $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null; - $value = $this->quote($fields[$name]['value'], $type); - if (PEAR::isError($value)) { - return $value; - } - } - $values.= $value; - if (isset($fields[$name]['key']) && $fields[$name]['key']) { - if ($value === 'NULL') { - return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, - 'key value '.$name.' may not be NULL', __FUNCTION__); - } - $keys++; - } - } - if ($keys == 0) { - return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, - 'not specified which fields are keys', __FUNCTION__); - } - - $connection = $this->getConnection(); - if (PEAR::isError($connection)) { - return $connection; - } - - $table = $this->quoteIdentifier($table, true); - $query = "REPLACE INTO $table ($query) VALUES ($values)"; - $result =$this->_doQuery($query, true, $connection); - if (PEAR::isError($result)) { - return $result; - } - return $this->_affectedRows($connection, $result); - } - - // }}} - // {{{ nextID() - - /** - * Returns the next free id of a sequence - * - * @param string $seq_name name of the sequence - * @param boolean $ondemand when true the sequence is - * automatic created, if it - * not exists - * - * @return mixed MDB2 Error Object or id - * @access public - */ - function nextID($seq_name, $ondemand = true) - { - $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true); - $seqcol_name = $this->options['seqcol_name']; - $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)"; - $this->pushErrorHandling(PEAR_ERROR_RETURN); - $this->expectError(MDB2_ERROR_NOSUCHTABLE); - $result =$this->_doQuery($query, true); - $this->popExpect(); - $this->popErrorHandling(); - if (PEAR::isError($result)) { - if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) { - $this->loadModule('Manager', null, true); - $result = $this->manager->createSequence($seq_name); - if (PEAR::isError($result)) { - return $this->raiseError($result, null, null, - 'on demand sequence '.$seq_name.' could not be created', __FUNCTION__); - } else { - return $this->nextID($seq_name, false); - } - } - return $result; - } - $value = $this->lastInsertID(); - if (is_numeric($value)) { - $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value"; - $result =$this->_doQuery($query, true); - if (PEAR::isError($result)) { - $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name; - } - } - return $value; - } - - // }}} - // {{{ lastInsertID() - - /** - * Returns the autoincrement ID if supported or $id or fetches the current - * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field) - * - * @param string $table name of the table into which a new row was inserted - * @param string $field name of the field into which a new row was inserted - * @return mixed MDB2 Error Object or id - * @access public - */ - function lastInsertID($table = null, $field = null) - { - return $this->connection->lastInsertRowID(); - } - - // }}} - // {{{ currID() - - /** - * Returns the current id of a sequence - * - * @param string $seq_name name of the sequence - * @return mixed MDB2 Error Object or id - * @access public - */ - function currID($seq_name) - { - $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true); - $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true); - $query = "SELECT MAX($seqcol_name) FROM $sequence_name"; - return $this->queryOne($query, 'integer'); - } - - /** - * Prepares a query for multiple execution with execute(). - * With some database backends, this is emulated. - * prepare() requires a generic query as string like - * 'INSERT INTO numbers VALUES(?,?)' or - * 'INSERT INTO numbers VALUES(:foo,:bar)'. - * The ? and :name and are placeholders which can be set using - * bindParam() and the query can be sent off using the execute() method. - * The allowed format for :name can be set with the 'bindname_format' option. - * - * @param string $query the query to prepare - * @param mixed $types array that contains the types of the placeholders - * @param mixed $result_types array that contains the types of the columns in - * the result set or MDB2_PREPARE_RESULT, if set to - * MDB2_PREPARE_MANIP the query is handled as a manipulation query - * @param mixed $lobs key (field) value (parameter) pair for all lob placeholders - * @return mixed resource handle for the prepared query on success, a MDB2 - * error on failure - * @access public - * @see bindParam, execute - */ - function prepare($query, $types = null, $result_types = null, $lobs = array()) - { - if ($this->options['emulate_prepared'] - || $this->supported['prepared_statements'] !== true - ) { - $obj =& parent::prepare($query, $types, $result_types, $lobs); - return $obj; - } - $this->last_query = $query; - $is_manip = ($result_types === MDB2_PREPARE_MANIP); - $offset = $this->offset; - $limit = $this->limit; - $this->offset = $this->limit = 0; - $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); - $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre')); - if ($result) { - if (PEAR::isError($result)) { - return $result; - } - $query = $result; - } - $placeholder_type_guess = $placeholder_type = null; - $question = '?'; - $colon = ':'; - $positions = array(); - $position = 0; - while ($position < strlen($query)) { - $q_position = strpos($query, $question, $position); - $c_position = strpos($query, $colon, $position); - if ($q_position && $c_position) { - $p_position = min($q_position, $c_position); - } elseif ($q_position) { - $p_position = $q_position; - } elseif ($c_position) { - $p_position = $c_position; - } else { - break; - } - if (is_null($placeholder_type)) { - $placeholder_type_guess = $query[$p_position]; - } - - $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position); - if (PEAR::isError($new_pos)) { - return $new_pos; - } - if ($new_pos != $position) { - $position = $new_pos; - continue; //evaluate again starting from the new position - } - - - if ($query[$position] == $placeholder_type_guess) { - if (is_null($placeholder_type)) { - $placeholder_type = $query[$p_position]; - $question = $colon = $placeholder_type; - } - if ($placeholder_type == ':') { - $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s'; - $parameter = preg_replace($regexp, '\\1', $query); - if ($parameter === '') { - $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null, - 'named parameter name must match "bindname_format" option', __FUNCTION__); - return $err; - } - $positions[$p_position] = $parameter; - $query = substr_replace($query, '?', $position, strlen($parameter)+1); - } else { - $positions[$p_position] = count($positions); - } - $position = $p_position + 1; - } else { - $position = $p_position; - } - } - $connection = $this->getConnection(); - if (PEAR::isError($connection)) { - return $connection; - } - $statement =$this->connection->prepare($query); - if (!$statement) { - return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'unable to prepare statement: '.$query); - } - - $class_name = 'MDB2_Statement_'.$this->phptype; - $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); - $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj)); - return $obj; - } -} - -/** - * MDB2 SQLite result driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - */ -class MDB2_Result_sqlite3 extends MDB2_Result_Common -{ - // }}} - // {{{ fetchRow() - - /** - * Fetch a row and insert the data into an existing array. - * - * @param int $fetchmode how the array data should be indexed - * @param int $rownum number of the row where the data can be found - * @return int data array on success, a MDB2 error on failure - * @access public - */ - function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) - { - if (!is_null($rownum)) { - $seek = $this->seek($rownum); - if (PEAR::isError($seek)) { - return $seek; - } - } - if ($fetchmode == MDB2_FETCHMODE_DEFAULT) { - $fetchmode = $this->db->fetchmode; - } - if ($fetchmode & MDB2_FETCHMODE_ASSOC) { - //$row = @sqlite_fetch_array($this->result, SQLITE_ASSOC); - $row=$this->result->fetchArray(SQLITE3_ASSOC); - if (is_array($row) - && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE - ) { - $row = array_change_key_case($row, $this->db->options['field_case']); - } - } else { - $row=$this->result->fetchArray(SQLITE3_NUM); - } - if (!$row) { - if ($this->result === false) { - $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, - 'resultset has already been freed', __FUNCTION__); - return $err; - } - $null = null; - return $null; - } - $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL; - $rtrim = false; - if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) { - if (empty($this->types)) { - $mode += MDB2_PORTABILITY_RTRIM; - } else { - $rtrim = true; - } - } - if ($mode) { - $this->db->_fixResultArrayValues($row, $mode); - } - if (!empty($this->types)) { - $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim); - } - if (!empty($this->values)) { - $this->_assignBindColumns($row); - } - if ($fetchmode === MDB2_FETCHMODE_OBJECT) { - $object_class = $this->db->options['fetch_class']; - if ($object_class == 'stdClass') { - $row = (object) $row; - } else { - $row = new $object_class($row); - } - } - ++$this->rownum; - return $row; - } - - // }}} - // {{{ _getColumnNames() - - /** - * Retrieve the names of columns returned by the DBMS in a query result. - * - * @return mixed Array variable that holds the names of columns as keys - * or an MDB2 error on failure. - * Some DBMS may not return any columns when the result set - * does not contain any rows. - * @access private - */ - function _getColumnNames() - { - $columns = array(); - $numcols = $this->numCols(); - if (PEAR::isError($numcols)) { - return $numcols; - } - for ($column = 0; $column < $numcols; $column++) { - $column_name = $this->result->getColumnName($column); - $columns[$column_name] = $column; - } - if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { - $columns = array_change_key_case($columns, $this->db->options['field_case']); - } - return $columns; - } - - // }}} - // {{{ numCols() - - /** - * Count the number of columns returned by the DBMS in a query result. - * - * @access public - * @return mixed integer value with the number of columns, a MDB2 error - * on failure - */ - function numCols() - { - $this->result->numColumns(); - } -} - -/** - * MDB2 SQLite buffered result driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - */ -class MDB2_BufferedResult_sqlite3 extends MDB2_Result_sqlite3 -{ - // {{{ seek() - - /** - * Seek to a specific row in a result set - * - * @param int $rownum number of the row where the data can be found - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function seek($rownum = 0) - { - $this->result->reset(); - for($i=0;$i<$rownum;$i++) { - $this->result->fetchArray(); - } - $this->rownum = $rownum - 1; - return MDB2_OK; - } - - // }}} - // {{{ valid() - - /** - * Check if the end of the result set has been reached - * - * @return mixed true or false on sucess, a MDB2 error on failure - * @access public - */ - function valid() - { - $numrows = $this->numRows(); - if (PEAR::isError($numrows)) { - return $numrows; - } - return $this->rownum < ($numrows - 1); - } - - // }}} - // {{{ numRows() - - /** - * Returns the number of rows in a result object - * - * @return mixed MDB2 Error Object or the number of rows - * @access public - */ - function numRows() - { - $rows = 0; - $this->result->reset(); - while($this->result->fetchArray()) { - $rows++; - } - $this->result->reset(); - return $rows; - } -} - -/** - * MDB2 SQLite statement driver - * - * @package MDB2 - * @category Database - * @author Lukas Smith <smith@pooteeweet.org> - */ -class MDB2_Statement_sqlite3 extends MDB2_Statement_Common -{ - // }}} - // {{{ function bindValue($parameter, &$value, $type = null) - - private function getParamType($type) { - switch(strtolower($type)) { - case 'text': - return SQLITE3_TEXT; - case 'boolean': - case 'integer': - return SQLITE3_INTEGER; - case 'float': - return SQLITE3_FLOAT; - case 'blob': - return SQLITE3_BLOB; - } - } - /** - * Set the value of a parameter of a prepared query. - * - * @param int the order number of the parameter in the query - * statement. The order number of the first parameter is 1. - * @param mixed value that is meant to be assigned to specified - * parameter. The type of the value depends on the $type argument. - * @param string specifies the type of the field - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function bindValue($parameter, $value, $type = null) { - if($type) { - $type=$this->getParamType($type); - $this->statement->bindValue($parameter, $value, $type); - }else{ - $this->statement->bindValue($parameter, $value); - } - return MDB2_OK; - } - - /** - * Bind a variable to a parameter of a prepared query. - * - * @param int the order number of the parameter in the query - * statement. The order number of the first parameter is 1. - * @param mixed variable that is meant to be bound to specified - * parameter. The type of the value depends on the $type argument. - * @param string specifies the type of the field - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - */ - function bindParam($parameter, &$value, $type = null) { - if($type) { - $type=$this->getParamType($type); - $this->statement->bindParam($parameter, $value, $type); - }else{ - $this->statement->bindParam($parameter, $value); - } - return MDB2_OK; - } - - /** - * Release resources allocated for the specified prepared query. - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * @access public - */ - function free() - { - $this->statement->close(); - } - - /** - * Execute a prepared query statement helper method. - * - * @param mixed $result_class string which specifies which result class to use - * @param mixed $result_wrap_class string which specifies which class to wrap results in - * - * @return mixed MDB2_Result or integer (affected rows) on success, - * a MDB2 error on failure - * @access private - */ - function _execute($result_class = true, $result_wrap_class = false) { - if (is_null($this->statement)) { - $result =& parent::_execute($result_class, $result_wrap_class); - return $result; - } - $this->db->last_query = $this->query; - $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values)); - if ($this->db->getOption('disable_query')) { - $result = $this->is_manip ? 0 : null; - return $result; - } - - $connection = $this->db->getConnection(); - if (PEAR::isError($connection)) { - return $connection; - } - - $result = $this->statement->execute(); - if ($result==false) { - $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, - 'cant execute statement', __FUNCTION__); - } - - if ($this->is_manip) { - $affected_rows = $this->db->_affectedRows($connection, $result); - return $affected_rows; - } - - $result = $this->db->_wrapResult($result, $this->result_types, - $result_class, $result_wrap_class, $this->limit, $this->offset); - $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result)); - return $result; - } - - /** - * Set the values of multiple a parameter of a prepared query in bulk. - * - * @param array specifies all necessary information - * for bindValue() the array elements must use keys corresponding to - * the number of the position of the parameter. - * @param array specifies the types of the fields - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - * @see bindParam() - */ - function bindValueArray($values, $types = null) - { - $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); - $parameters = array_keys($values); - foreach ($parameters as $key => $parameter) { - $this->db->pushErrorHandling(PEAR_ERROR_RETURN); - $this->db->expectError(MDB2_ERROR_NOT_FOUND); - $err = $this->bindValue($parameter+1, $values[$parameter], $types[$key]); - $this->db->popExpect(); - $this->db->popErrorHandling(); - if (PEAR::isError($err)) { - if ($err->getCode() == MDB2_ERROR_NOT_FOUND) { - //ignore (extra value for missing placeholder) - continue; - } - return $err; - } - } - return MDB2_OK; - } - // }}} - // {{{ function bindParamArray(&$values, $types = null) - - /** - * Bind the variables of multiple a parameter of a prepared query in bulk. - * - * @param array specifies all necessary information - * for bindParam() the array elements must use keys corresponding to - * the number of the position of the parameter. - * @param array specifies the types of the fields - * - * @return mixed MDB2_OK on success, a MDB2 error on failure - * - * @access public - * @see bindParam() - */ - function bindParamArray(&$values, $types = null) - { - $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); - $parameters = array_keys($values); - foreach ($parameters as $key => $parameter) { - $err = $this->bindParam($parameter+1, $values[$parameter], $types[$key]); - if (PEAR::isError($err)) { - return $err; - } - } - return MDB2_OK; - } - - // }}} - // {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false) - - /** - * Execute a prepared query statement. - * - * @param array specifies all necessary information - * for bindParam() the array elements must use keys corresponding - * to the number of the position of the parameter. - * @param mixed specifies which result class to use - * @param mixed specifies which class to wrap results in - * - * @return mixed MDB2_Result or integer (affected rows) on success, - * a MDB2 error on failure - * @access public - */ - function execute($values = null, $result_class = true, $result_wrap_class = false) - { - if (is_null($this->positions)) { - return $this->db->raiseError(MDB2_ERROR, null, null, - 'Prepared statement has already been freed', __FUNCTION__); - } - $values = (array)$values; - if (!empty($values)) { - if(count($this->types)) { - $types=$this->types; - }else{ - $types=null; - } - $err = $this->bindValueArray($values, $types); - if (PEAR::isError($err)) { - return $this->db->raiseError(MDB2_ERROR, null, null, - 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__); - } - } - $result =$this->_execute($result_class, $result_wrap_class); - return $result; - } - - function __destruct() { - $this->free(); - } -} diff --git a/lib/app.php b/lib/app.php index baacf508d8e..2437896157a 100644 --- a/lib/app.php +++ b/lib/app.php @@ -839,9 +839,9 @@ class OC_App{ OC_Hook::emit('update', 'success', 'Updated '.$info['name'].' app'); } catch (Exception $e) { - echo 'Failed to upgrade "'.$app.'". Exception="'.$e->getMessage().'"'; OC_Hook::emit('update', 'failure', 'Failed to update '.$info['name'].' app: '.$e->getMessage()); - die; + $l = OC_L10N::get('lib'); + throw new RuntimeException($l->t('Failed to upgrade "%s".', array($app)), 0, $e); } OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app)); } diff --git a/lib/base.php b/lib/base.php index 53aa7b09fd5..eaee8424651 100644 --- a/lib/base.php +++ b/lib/base.php @@ -124,10 +124,9 @@ class OC { OC::$THIRDPARTYWEBROOT = rtrim(dirname(OC::$WEBROOT), '/'); OC::$THIRDPARTYROOT = rtrim(dirname(OC::$SERVERROOT), '/'); } else { - echo('3rdparty directory not found! Please put the ownCloud 3rdparty' + throw new Exception('3rdparty directory not found! Please put the ownCloud 3rdparty' .' folder in the ownCloud folder or the folder above.' .' You can also configure the location in the config.php file.'); - exit; } // search the apps folder $config_paths = OC_Config::getValue('apps_paths', array()); @@ -150,9 +149,8 @@ class OC { } if (empty(OC::$APPSROOTS)) { - echo('apps directory not found! Please put the ownCloud apps folder in the ownCloud folder' + throw new Exception('apps directory not found! Please put the ownCloud apps folder in the ownCloud folder' .' or the folder above. You can also configure the location in the config.php file.'); - exit; } $paths = array(); foreach (OC::$APPSROOTS as $path) { @@ -174,14 +172,11 @@ class OC { if (file_exists(OC::$SERVERROOT . "/config/config.php") and !is_writable(OC::$SERVERROOT . "/config/config.php")) { $defaults = new OC_Defaults(); - $tmpl = new OC_Template('', 'error', 'guest'); - $tmpl->assign('errors', array(1 => array( - 'error' => "Can't write into config directory 'config'", - 'hint' => 'This can usually be fixed by ' + OC_Template::printErrorPage( + "Can't write into config directory!", + 'This can usually be fixed by ' .'<a href="' . $defaults->getDocBaseUrl() . '/server/5.0/admin_manual/installation/installation_source.html#set-the-directory-permissions" target="_blank">giving the webserver write access to the config directory</a>.' - ))); - $tmpl->printPage(); - exit(); + ); } } @@ -223,10 +218,7 @@ class OC { header('Retry-After: 120'); // render error page - $tmpl = new OC_Template('', 'error', 'guest'); - $tmpl->assign('errors', array(1 => array('error' => 'ownCloud is in maintenance mode'))); - $tmpl->printPage(); - exit(); + OC_Template::printErrorPage('ownCloud is in maintenance mode'); } } @@ -305,11 +297,7 @@ class OC { $error = 'Session could not be initialized. Please contact your '; $error .= 'system administrator'; - $tmpl = new OC_Template('', 'error', 'guest'); - $tmpl->assign('errors', array(1 => array('error' => $error))); - $tmpl->printPage(); - - exit(); + OC_Template::printErrorPage($error); } $sessionLifeTime = self::getSessionLifeTime(); @@ -370,6 +358,7 @@ class OC { self::$loader->registerPrefix('Symfony\\Component\\Routing', 'symfony/routing'); self::$loader->registerPrefix('Sabre\\VObject', '3rdparty'); self::$loader->registerPrefix('Sabre_', '3rdparty'); + self::$loader->registerPrefix('Patchwork', '3rdparty'); spl_autoload_register(array(self::$loader, 'load')); // set some stuff @@ -433,9 +422,13 @@ class OC { } } - if (!defined('PHPUNIT_RUN') and !(defined('DEBUG') and DEBUG)) { - OC\Log\ErrorHandler::register(); - OC\Log\ErrorHandler::setLogger(OC_Log::$object); + if (!defined('PHPUNIT_RUN')) { + if (defined('DEBUG') and DEBUG) { + set_exception_handler(array('OC_Template', 'printExceptionErrorPage')); + } else { + OC\Log\ErrorHandler::register(); + OC\Log\ErrorHandler::setLogger(OC_Log::$object); + } } // register the stream wrappers @@ -833,3 +826,4 @@ if (!function_exists('get_temp_dir')) { } OC::init(); + diff --git a/lib/config.php b/lib/config.php index 00d9f5b4247..e773e6e2eb0 100644 --- a/lib/config.php +++ b/lib/config.php @@ -144,7 +144,8 @@ class Config { continue; } unset($CONFIG); - include $file; + // ignore errors on include, this can happen when doing a fresh install + @include $file; if (isset($CONFIG) && is_array($CONFIG)) { $this->cache = array_merge($this->cache, $CONFIG); } @@ -173,7 +174,7 @@ class Config { if (!$result) { $url = $defaults->getDocBaseUrl() . '/server/5.0/admin_manual/installation/installation_source.html#set-the-directory-permissions'; throw new HintException( - "Can't write into config directory 'config'", + "Can't write into config directory!", 'This can usually be fixed by ' .'<a href="' . $url . '" target="_blank">giving the webserver write access to the config directory</a>.'); } @@ -182,3 +183,4 @@ class Config { \OC_Util::clearOpcodeCache(); } } + diff --git a/lib/connector/sabre/auth.php b/lib/connector/sabre/auth.php index 6990d928cff..bf3a49593cb 100644 --- a/lib/connector/sabre/auth.php +++ b/lib/connector/sabre/auth.php @@ -60,4 +60,25 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic { } return $user; } + + /** + * Override function here. We want to cache authentication cookies + * in the syncing client to avoid HTTP-401 roundtrips. + * If the sync client supplies the cookies, then OC_User::isLoggedIn() + * will return true and we can see this WebDAV request as already authenticated, + * even if there are no HTTP Basic Auth headers. + * In other case, just fallback to the parent implementation. + * + * @return bool + */ + public function authenticate(Sabre_DAV_Server $server, $realm) { + if (OC_User::isLoggedIn()) { + $user = OC_User::getUser(); + OC_Util::setupFS($user); + $this->currentUser = $user; + return true; + } + + return parent::authenticate($server, $realm); + } } diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index 3d15a2a584d..ed8d085462d 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -222,7 +222,6 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa throw new \Sabre_DAV_Exception_Forbidden(); } if ($this->path != "/Shared") { - foreach($this->getChildren() as $child) $child->delete(); \OC\Files\Filesystem::rmdir($this->path); } diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php index 438d9871c22..06ab73e3e4d 100644 --- a/lib/connector/sabre/file.php +++ b/lib/connector/sabre/file.php @@ -115,8 +115,11 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D */ public function getSize() { $this->getFileinfoCache(); - return $this->fileinfo_cache['size']; - + if ($this->fileinfo_cache['size'] > -1) { + return $this->fileinfo_cache['size']; + } else { + return null; + } } /** diff --git a/lib/connector/sabre/objecttree.php b/lib/connector/sabre/objecttree.php new file mode 100644 index 00000000000..c4ddcbecbb8 --- /dev/null +++ b/lib/connector/sabre/objecttree.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Connector\Sabre; + +use OC\Files\Filesystem; + +class ObjectTree extends \Sabre_DAV_ObjectTree { + /** + * Returns the INode object for the requested path + * + * @param string $path + * @throws \Sabre_DAV_Exception_NotFound + * @return \Sabre_DAV_INode + */ + public function getNodeForPath($path) { + + $path = trim($path, '/'); + if (isset($this->cache[$path])) return $this->cache[$path]; + + // Is it the root node? + if (!strlen($path)) { + return $this->rootNode; + } + + $info = Filesystem::getFileInfo($path); + + if (!$info) { + throw new \Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located'); + } + + if ($info['mimetype'] === 'httpd/unix-directory') { + $node = new \OC_Connector_Sabre_Directory($path); + } else { + $node = new \OC_Connector_Sabre_File($path); + } + + $node->setFileinfoCache($info); + + $this->cache[$path] = $node; + return $node; + + } + + /** + * Moves a file from one location to another + * + * @param string $sourcePath The path to the file which should be moved + * @param string $destinationPath The full destination path, so not just the destination parent node + * @throws \Sabre_DAV_Exception_Forbidden + * @return int + */ + public function move($sourcePath, $destinationPath) { + + $sourceNode = $this->getNodeForPath($sourcePath); + if ($sourceNode instanceof \Sabre_DAV_ICollection and $this->nodeExists($destinationPath)) { + throw new \Sabre_DAV_Exception_Forbidden('Could not copy directory ' . $sourceNode . ', target exists'); + } + list($sourceDir,) = \Sabre_DAV_URLUtil::splitPath($sourcePath); + list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destinationPath); + + Filesystem::rename($sourcePath, $destinationPath); + + $this->markDirty($sourceDir); + $this->markDirty($destinationDir); + + } + + /** + * Copies a file or directory. + * + * This method must work recursively and delete the destination + * if it exists + * + * @param string $source + * @param string $destination + * @return void + */ + public function copy($source, $destination) { + + if (Filesystem::is_file($source)) { + Filesystem::copy($source, $destination); + } else { + Filesystem::mkdir($destination); + $dh = Filesystem::opendir($source); + while ($subnode = readdir($dh)) { + + if ($subnode == '.' || $subnode == '..') continue; + $this->copy($source . '/' . $subnode, $destination . '/' . $subnode); + + } + } + + list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destination); + $this->markDirty($destinationDir); + } +} diff --git a/lib/connector/sabre/principal.php b/lib/connector/sabre/principal.php index 04be410ac85..16c88b96ea6 100644 --- a/lib/connector/sabre/principal.php +++ b/lib/connector/sabre/principal.php @@ -121,6 +121,6 @@ class OC_Connector_Sabre_Principal implements Sabre_DAVACL_IPrincipalBackend { } function searchPrincipals($prefixPath, array $searchProperties) { - return 0; + return array(); } } diff --git a/lib/db.php b/lib/db.php index 6fec60e53ce..e70d66fc2ba 100644 --- a/lib/db.php +++ b/lib/db.php @@ -20,7 +20,9 @@ * */ -class DatabaseException extends Exception{ +define('MDB2_SCHEMA_DUMP_STRUCTURE', '1'); + +class DatabaseException extends Exception { private $query; //FIXME getQuery seems to be unused, maybe use parent constructor with $message, $code and $previous @@ -29,61 +31,41 @@ class DatabaseException extends Exception{ $this->query = $query; } - public function getQuery(){ + public function getQuery() { return $this->query; } } /** * This class manages the access to the database. It basically is a wrapper for - * MDB2 with some adaptions. + * Doctrine with some adaptions. */ class OC_DB { - const BACKEND_PDO=0; - const BACKEND_MDB2=1; + const BACKEND_DOCTRINE=2; static private $preparedQueries = array(); static private $cachingEnabled = true; /** - * @var MDB2_Driver_Common + * @var \Doctrine\DBAL\Connection */ - static private $connection; //the prefered connection to use, either PDO or MDB2 + static private $connection; //the preferred connection to use, only Doctrine static private $backend=null; /** - * @var MDB2_Driver_Common - */ - static private $MDB2=null; - /** - * @var PDO + * @var \Doctrine\DBAL\Connection */ - static private $PDO=null; - /** - * @var MDB2_Schema - */ - static private $schema=null; + static private $DOCTRINE=null; + static private $inTransaction=false; static private $prefix=null; static private $type=null; /** * check which backend we should use - * @return int BACKEND_MDB2 or BACKEND_PDO + * @return int BACKEND_DOCTRINE */ private static function getDBBackend() { - //check if we can use PDO, else use MDB2 (installation always needs to be done my mdb2) - if(class_exists('PDO') && OC_Config::getValue('installed', false)) { - $type = OC_Config::getValue( "dbtype", "sqlite" ); - if($type=='oci') { //oracle also always needs mdb2 - return self::BACKEND_MDB2; - } - if($type=='sqlite3') $type='sqlite'; - $drivers=PDO::getAvailableDrivers(); - if(array_search($type, $drivers)!==false) { - return self::BACKEND_PDO; - } - } - return self::BACKEND_MDB2; + return self::BACKEND_DOCTRINE; } /** @@ -100,28 +82,24 @@ class OC_DB { if(is_null($backend)) { $backend=self::getDBBackend(); } - if($backend==self::BACKEND_PDO) { - $success = self::connectPDO(); - self::$connection=self::$PDO; - self::$backend=self::BACKEND_PDO; - }else{ - $success = self::connectMDB2(); - self::$connection=self::$MDB2; - self::$backend=self::BACKEND_MDB2; + if($backend==self::BACKEND_DOCTRINE) { + $success = self::connectDoctrine(); + self::$connection=self::$DOCTRINE; + self::$backend=self::BACKEND_DOCTRINE; } return $success; } /** - * connect to the database using pdo + * connect to the database using doctrine * * @return bool */ - public static function connectPDO() { + public static function connectDoctrine() { if(self::$connection) { - if(self::$backend==self::BACKEND_MDB2) { + if(self::$backend!=self::BACKEND_DOCTRINE) { self::disconnect(); - }else{ + } else { return true; } } @@ -134,169 +112,85 @@ class OC_DB { $type = OC_Config::getValue( "dbtype", "sqlite" ); if(strpos($host, ':')) { list($host, $port)=explode(':', $host, 2); - }else{ + } else { $port=false; } - $opts = array(); - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); // do nothing if the connection already has been established - if(!self::$PDO) { - // Add the dsn according to the database type + if(!self::$DOCTRINE) { + $config = new \Doctrine\DBAL\Configuration(); switch($type) { case 'sqlite': - $dsn='sqlite2:'.$datadir.'/'.$name.'.db'; - break; case 'sqlite3': - $dsn='sqlite:'.$datadir.'/'.$name.'.db'; - break; - case 'mysql': - if($port) { - $dsn='mysql:dbname='.$name.';host='.$host.';port='.$port; - }else{ - $dsn='mysql:dbname='.$name.';host='.$host; - } - $opts[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES 'UTF8'"; - break; - case 'pgsql': - if($port) { - $dsn='pgsql:dbname='.$name.';host='.$host.';port='.$port; - }else{ - $dsn='pgsql:dbname='.$name.';host='.$host; - } - /** - * Ugly fix for pg connections pbm when password use spaces - */ - $e_user = addslashes($user); - $e_password = addslashes($pass); - $pass = $user = null; - $dsn .= ";user='$e_user';password='$e_password'"; - /** END OF FIX***/ - break; - case 'oci': // Oracle with PDO is unsupported - if ($port) { - $dsn = 'oci:dbname=//' . $host . ':' . $port . '/' . $name; - } else { - $dsn = 'oci:dbname=//' . $host . '/' . $name; - } - break; - case 'mssql': - if ($port) { - $dsn='sqlsrv:Server='.$host.','.$port.';Database='.$name; - } else { - $dsn='sqlsrv:Server='.$host.';Database='.$name; - } - break; - default: - return false; - } - self::$PDO=new PDO($dsn, $user, $pass, $opts); - - // We always, really always want associative arrays - self::$PDO->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); - self::$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } - return true; - } - - /** - * connect to the database using mdb2 - */ - public static function connectMDB2() { - if(self::$connection) { - if(self::$backend==self::BACKEND_PDO) { - self::disconnect(); - }else{ - return true; - } - } - self::$preparedQueries = array(); - // The global data we need - $name = OC_Config::getValue( "dbname", "owncloud" ); - $host = OC_Config::getValue( "dbhost", "" ); - $user = OC_Config::getValue( "dbuser", "" ); - $pass = OC_Config::getValue( "dbpassword", "" ); - $type = OC_Config::getValue( "dbtype", "sqlite" ); - $SERVERROOT=OC::$SERVERROOT; - $datadir=OC_Config::getValue( "datadirectory", "$SERVERROOT/data" ); - - // do nothing if the connection already has been established - if(!self::$MDB2) { - // Require MDB2.php (not required in the head of the file so we only load it when needed) - require_once 'MDB2.php'; - - // Prepare options array - $options = array( - 'portability' => MDB2_PORTABILITY_ALL - MDB2_PORTABILITY_FIX_CASE, - 'log_line_break' => '<br>', - 'idxname_format' => '%s', - 'debug' => true, - 'quote_identifier' => true - ); - - // Add the dsn according to the database type - switch($type) { - case 'sqlite': - case 'sqlite3': - $dsn = array( - 'phptype' => $type, - 'database' => "$datadir/$name.db", - 'mode' => '0644' + $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'path' => $datadir.'/'.$name.'.db', + 'driver' => 'pdo_sqlite', ); break; case 'mysql': - $dsn = array( - 'phptype' => 'mysql', - 'username' => $user, - 'password' => $pass, - 'hostspec' => $host, - 'database' => $name + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'port' => $port, + 'dbname' => $name, + 'charset' => 'UTF8', + 'driver' => 'pdo_mysql', ); break; case 'pgsql': - $dsn = array( - 'phptype' => 'pgsql', - 'username' => $user, - 'password' => $pass, - 'hostspec' => $host, - 'database' => $name + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'port' => $port, + 'dbname' => $name, + 'driver' => 'pdo_pgsql', ); break; case 'oci': - $dsn = array( - 'phptype' => 'oci8', - 'username' => $user, - 'password' => $pass, - 'service' => $name, - 'hostspec' => $host, - 'charset' => 'AL32UTF8', + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'dbname' => $name, + 'charset' => 'AL32UTF8', + 'driver' => 'oci8', ); + if (!empty($port)) { + $connectionParams['port'] = $port; + } break; case 'mssql': - $dsn = array( - 'phptype' => 'sqlsrv', - 'username' => $user, - 'password' => $pass, - 'hostspec' => $host, - 'database' => $name, - 'charset' => 'UTF-8' + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'port' => $port, + 'dbname' => $name, + 'charset' => 'UTF8', + 'driver' => 'pdo_sqlsrv', ); - $options['portability'] = $options['portability'] - MDB2_PORTABILITY_EMPTY_TO_NULL; break; default: return false; } + try { + self::$DOCTRINE = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config); + } catch(\Doctrine\DBAL\DBALException $e) { + OC_Log::write('core', $e->getMessage(), OC_Log::FATAL); + OC_User::setUserId(null); - // Try to establish connection - self::$MDB2 = MDB2::factory( $dsn, $options ); - - self::raiseExceptionOnError( self::$MDB2 ); - - // We always, really always want associative arrays - self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC); + // send http status 503 + header('HTTP/1.1 503 Service Temporarily Unavailable'); + header('Status: 503 Service Temporarily Unavailable'); + OC_Template::printErrorPage('Failed to connect to database'); + die(); + } } - - // we are done. great! return true; } @@ -306,34 +200,19 @@ class OC_DB { * @param int $limit * @param int $offset * @param bool $isManipulation - * @return MDB2_Statement_Common prepared SQL query + * @throws DatabaseException + * @return \Doctrine\DBAL\Statement prepared SQL query * - * SQL query via MDB2 prepare(), needs to be execute()'d! + * SQL query via Doctrine prepare(), needs to be execute()'d! */ static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) { if (!is_null($limit) && $limit != -1) { - if (self::$backend == self::BACKEND_MDB2) { - //MDB2 uses or emulates limits & offset internally - self::$MDB2->setLimit($limit, $offset); - } else { - //PDO does not handle limit and offset. - //FIXME: check limit notation for other dbs - //the following sql thus might needs to take into account db ways of representing it - //(oracle has no LIMIT / OFFSET) - $limit = (int)$limit; - $limitsql = ' LIMIT ' . $limit; - if (!is_null($offset)) { - $offset = (int)$offset; - $limitsql .= ' OFFSET ' . $offset; - } - //insert limitsql - if (substr($query, -1) == ';') { //if query ends with ; - $query = substr($query, 0, -1) . $limitsql . ';'; - } else { - $query.=$limitsql; - } + if ($limit === -1) { + $limit = null; } + $platform = self::$connection->getDatabasePlatform(); + $query = $platform->modifyLimitQuery($query, $limit, $offset); } else { if (isset(self::$preparedQueries[$query]) and self::$cachingEnabled) { return self::$preparedQueries[$query]; @@ -354,26 +233,14 @@ class OC_DB { } // return the result - if(self::$backend==self::BACKEND_MDB2) { - // differentiate between query and manipulation - if ($isManipulation) { - $result = self::$connection->prepare( $query, null, MDB2_PREPARE_MANIP ); - } else { - $result = self::$connection->prepare( $query, null, MDB2_PREPARE_RESULT ); - } - - // Die if we have an error (error means: bad query, not 0 results!) - if( self::isError($result)) { - throw new DatabaseException($result->getMessage(), $query); - } - }else{ - try{ + if (self::$backend == self::BACKEND_DOCTRINE) { + try { $result=self::$connection->prepare($query); - }catch(PDOException $e) { - throw new DatabaseException($e->getMessage(), $query); + } catch(\Doctrine\DBAL\DBALException $e) { + throw new \DatabaseException($e->getMessage(), $query); } // differentiate between query and manipulation - $result = new PDOStatementWrapper($result, $isManipulation); + $result=new OC_DB_StatementWrapper($result, $isManipulation); } if ((is_null($limit) || $limit == -1) and self::$cachingEnabled ) { $type = OC_Config::getValue( "dbtype", "sqlite" ); @@ -412,7 +279,7 @@ class OC_DB { /** * @brief execute a prepared statement, on error write log and throw exception - * @param mixed $stmt PDOStatementWrapper | MDB2_Statement_Common , + * @param mixed $stmt OC_DB_StatementWrapper, * an array with 'sql' and optionally 'limit' and 'offset' keys * .. or a simple sql query string * @param array $parameters @@ -445,7 +312,7 @@ class OC_DB { $stmt = self::prepare($stmt['sql'], $stmt['limit'], $stmt['offset']); } self::raiseExceptionOnError($stmt, 'Could not prepare statement'); - if ($stmt instanceof PDOStatementWrapper || $stmt instanceof MDB2_Statement_Common) { + if ($stmt instanceof OC_DB_StatementWrapper) { $result = $stmt->execute($parameters); self::raiseExceptionOnError($result, 'Could not execute statement'); } else { @@ -465,7 +332,7 @@ class OC_DB { * @return int id * @throws DatabaseException * - * MDB2 lastInsertID() + * \Doctrine\DBAL\Connection lastInsertId * * Call this method right after the insert command or other functions may * cause trouble! @@ -478,12 +345,20 @@ class OC_DB { $row = $result->fetchRow(); self::raiseExceptionOnError($row, 'fetching row for insertid failed'); return $row['id']; - } else if( $type === 'mssql' || $type === 'oci') { + } else if( $type === 'mssql') { if($table !== null) { $prefix = OC_Config::getValue( "dbtableprefix", "oc_" ); $table = str_replace( '*PREFIX*', $prefix, $table ); } - $result = self::$connection->lastInsertId($table); + return self::$connection->lastInsertId($table); + } + if( $type === 'oci' ) { + if($table !== null) { + $prefix = OC_Config::getValue( "dbtableprefix", "oc_" ); + $suffix = '_SEQ'; + $table = '"'.str_replace( '*PREFIX*', $prefix, $table ).$suffix.'"'; + } + return self::$connection->lastInsertId($table); } else { if($table !== null) { $prefix = OC_Config::getValue( "dbtableprefix", "oc_" ); @@ -505,18 +380,14 @@ class OC_DB { public static function disconnect() { // Cut connection if required if(self::$connection) { - if(self::$backend==self::BACKEND_MDB2) { - self::$connection->disconnect(); - } self::$connection=false; - self::$MDB2=false; - self::$PDO=false; + self::$DOCTRINE=false; } return true; } - /** + /** else { * @brief saves database scheme to xml file * @param string $file name of file * @param int $mode @@ -525,18 +396,8 @@ class OC_DB { * TODO: write more documentation */ public static function getDbStructure( $file, $mode=MDB2_SCHEMA_DUMP_STRUCTURE) { - self::connectScheme(); - - // write the scheme - $definition = self::$schema->getDefinitionFromDatabase(); - $dump_options = array( - 'output_mode' => 'file', - 'output' => $file, - 'end_of_line' => "\n" - ); - self::$schema->dumpDatabase( $definition, $dump_options, $mode ); - - return true; + self::connectDoctrine(); + return OC_DB_Schema::getDbStructure(self::$DOCTRINE, $file); } /** @@ -547,134 +408,25 @@ class OC_DB { * TODO: write more documentation */ public static function createDbFromStructure( $file ) { - $CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" ); - $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" ); - $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" ); - - // cleanup the cached queries - self::$preparedQueries = array(); - - self::connectScheme(); - - // read file - $content = file_get_contents( $file ); - - // Make changes and save them to an in-memory file - $file2 = 'static://db_scheme'; - $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content ); - $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); - /* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1] - * as a fallback we could use <default>0000-01-01 00:00:00</default> everywhere - * [1] http://bugs.mysql.com/bug.php?id=27645 - * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html - * http://www.postgresql.org/docs/8.1/static/functions-datetime.html - * http://www.sqlite.org/lang_createtable.html - * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm - */ - if( $CONFIG_DBTYPE == 'pgsql' ) { //mysql support it too but sqlite doesn't - $content = str_replace( '<default>0000-00-00 00:00:00</default>', - '<default>CURRENT_TIMESTAMP</default>', $content ); - } - - file_put_contents( $file2, $content ); - - // Try to create tables - $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - - //clean up memory - unlink( $file2 ); - - self::raiseExceptionOnError($definition,'Failed to parse the database definition'); - - if(OC_Config::getValue('dbtype', 'sqlite')==='oci') { - unset($definition['charset']); //or MDB2 tries SHUTDOWN IMMEDIATE - $oldname = $definition['name']; - $definition['name']=OC_Config::getValue( "dbuser", $oldname ); - } - - // we should never drop a database - $definition['overwrite'] = false; - - $ret=self::$schema->createDatabase( $definition ); - - self::raiseExceptionOnError($ret,'Failed to create the database structure'); - - return true; + self::connectDoctrine(); + return OC_DB_Schema::createDbFromStructure(self::$DOCTRINE, $file); } /** * @brief update the database scheme * @param string $file file to read structure from + * @throws Exception * @return bool */ public static function updateDbFromStructure($file) { - $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" ); - $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" ); - - self::connectScheme(); - - if(OC_Config::getValue('dbtype', 'sqlite')==='oci') { - //set dbname, it is unset because oci uses 'service' to connect - self::$schema->db->database_name=self::$schema->db->dsn['username']; - } - - // read file - $content = file_get_contents( $file ); - - $previousSchema = self::$schema->getDefinitionFromDatabase(); - self::raiseExceptionOnError($previousSchema,'Failed to get existing database structure for updating'); - - // Make changes and save them to an in-memory file - $file2 = 'static://db_scheme'; - $content = str_replace( '*dbname*', $previousSchema['name'], $content ); - $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); - /* FIXME: use CURRENT_TIMESTAMP for all databases. mysql supports it as a default for DATETIME since 5.6.5 [1] - * as a fallback we could use <default>0000-01-01 00:00:00</default> everywhere - * [1] http://bugs.mysql.com/bug.php?id=27645 - * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html - * http://www.postgresql.org/docs/8.1/static/functions-datetime.html - * http://www.sqlite.org/lang_createtable.html - * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm - */ - if( $CONFIG_DBTYPE == 'pgsql' ) { //mysql support it too but sqlite doesn't - $content = str_replace( '<default>0000-00-00 00:00:00</default>', - '<default>CURRENT_TIMESTAMP</default>', $content ); - } - if(OC_Config::getValue('dbtype', 'sqlite')==='oci') { - unset($previousSchema['charset']); //or MDB2 tries SHUTDOWN IMMEDIATE - $oldname = $previousSchema['name']; - $previousSchema['name']=OC_Config::getValue( "dbuser", $oldname ); - //TODO check identifiers are at most 30 chars long - } - file_put_contents( $file2, $content ); - $op = self::$schema->updateDatabase($file2, $previousSchema, array(), false); - - //clean up memory - unlink( $file2 ); - - self::raiseExceptionOnError($op,'Failed to update database structure'); - return true; - } - - /** - * @brief connects to a MDB2 database scheme - * @returns bool - * - * Connects to a MDB2 database scheme - */ - private static function connectScheme() { - // We need a mdb2 database connection - self::connectMDB2(); - self::$MDB2->loadModule('Manager'); - self::$MDB2->loadModule('Reverse'); - - // Connect if this did not happen before - if(!self::$schema) { - require_once 'MDB2/Schema.php'; - self::$schema=MDB2_Schema::factory(self::$MDB2); + self::connectDoctrine(); + try { + $result = OC_DB_Schema::updateDbFromStructure(self::$DOCTRINE, $file); + } catch (Exception $e) { + OC_Log::write('core', 'Failed to update database structure ('.$e.')', OC_Log::FATAL); + throw $e; } - - return true; + return $result; } /** @@ -733,7 +485,7 @@ class OC_DB { try { $result = self::executeAudited($query, $inserts); - } catch(PDOException $e) { + } catch(\Doctrine\DBAL\DBALException $e) { OC_Template::printExceptionErrorPage( $e ); } @@ -765,20 +517,20 @@ class OC_DB { $query = str_replace( '`', '"', $query ); $query = str_ireplace( 'NOW()', 'datetime(\'now\')', $query ); $query = str_ireplace( 'UNIX_TIMESTAMP()', 'strftime(\'%s\',\'now\')', $query ); - }elseif( $type == 'pgsql' ) { + } elseif( $type == 'pgsql' ) { $query = str_replace( '`', '"', $query ); $query = str_ireplace( 'UNIX_TIMESTAMP()', 'cast(extract(epoch from current_timestamp) as integer)', $query ); - }elseif( $type == 'oci' ) { + } elseif( $type == 'oci' ) { $query = str_replace( '`', '"', $query ); $query = str_ireplace( 'NOW()', 'CURRENT_TIMESTAMP', $query ); - $query = str_ireplace( 'UNIX_TIMESTAMP()', '((CAST(SYS_EXTRACT_UTC(systimestamp) AS DATE))-TO_DATE(\'1970101000000\',\'YYYYMMDDHH24MiSS\'))*24*3600', $query ); + $query = str_ireplace( 'UNIX_TIMESTAMP()', "(cast(sys_extract_utc(systimestamp) as date) - date'1970-01-01') * 86400", $query ); }elseif( $type == 'mssql' ) { $query = preg_replace( "/\`(.*?)`/", "[$1]", $query ); - $query = str_replace( 'NOW()', 'CURRENT_TIMESTAMP', $query ); - $query = str_replace( 'now()', 'CURRENT_TIMESTAMP', $query ); + $query = str_ireplace( 'NOW()', 'CURRENT_TIMESTAMP', $query ); $query = str_replace( 'LENGTH(', 'LEN(', $query ); $query = str_replace( 'SUBSTR(', 'SUBSTRING(', $query ); + $query = str_ireplace( 'UNIX_TIMESTAMP()', 'DATEDIFF(second,{d \'1970-01-01\'},GETDATE())', $query ); $query = self::fixLimitClauseForMSSQL($query); } @@ -848,9 +600,8 @@ class OC_DB { * @param string $tableName the table to drop */ public static function dropTable($tableName) { - self::connectMDB2(); - self::$MDB2->loadModule('Manager'); - self::$MDB2->dropTable($tableName); + self::connectDoctrine(); + OC_DB_Schema::dropTable(self::$DOCTRINE, $tableName); } /** @@ -858,50 +609,17 @@ class OC_DB { * @param string $file the xml file describing the tables */ public static function removeDBStructure($file) { - $CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" ); - $CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" ); - self::connectScheme(); - - // read file - $content = file_get_contents( $file ); - - // Make changes and save them to a temporary file - $file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' ); - $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content ); - $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); - file_put_contents( $file2, $content ); - - // get the tables - $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - - // Delete our temporary file - unlink( $file2 ); - $tables=array_keys($definition['tables']); - foreach($tables as $table) { - self::dropTable($table); - } + self::connectDoctrine(); + OC_DB_Schema::removeDBStructure(self::$DOCTRINE, $file); } /** - * @brief replaces the owncloud tables with a new set + * @brief replaces the ownCloud tables with a new set * @param $file string path to the MDB2 xml db export file */ public static function replaceDB( $file ) { - $apps = OC_App::getAllApps(); - self::beginTransaction(); - // Delete the old tables - self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' ); - - foreach($apps as $app) { - $path = OC_App::getAppPath($app).'/appinfo/database.xml'; - if(file_exists($path)) { - self::removeDBStructure( $path ); - } - } - - // Create new tables - self::createDBFromStructure( $file ); - self::commit(); + self::connectDoctrine(); + OC_DB_Schema::replaceDB(self::$DOCTRINE, $file); } /** @@ -910,9 +628,6 @@ class OC_DB { */ public static function beginTransaction() { self::connect(); - if (self::$backend==self::BACKEND_MDB2 && !self::$connection->supports('transactions')) { - return false; - } self::$connection->beginTransaction(); self::$inTransaction=true; return true; @@ -933,24 +648,16 @@ class OC_DB { } /** - * check if a result is an error, works with MDB2 and PDOException + * check if a result is an error, works with Doctrine * @param mixed $result * @return bool */ public static function isError($result) { - //MDB2 returns an MDB2_Error object - if (class_exists('PEAR') === true && PEAR::isError($result)) { - return true; - } - //PDO returns false on error (and throws an exception) - if (self::$backend===self::BACKEND_PDO and $result === false) { - return true; - } - - return false; + //Doctrine returns false on error (and throws an exception) + return $result === false; } /** - * check if a result is an error and throws an exception, works with MDB2 and PDOException + * check if a result is an error and throws an exception, works with \Doctrine\DBAL\DBALException * @param mixed $result * @param string $message * @return void @@ -968,32 +675,19 @@ class OC_DB { } public static function getErrorCode($error) { - if ( class_exists('PEAR') === true && PEAR::isError($error) ) { - /** @var $error PEAR_Error */ - return $error->getCode(); - } - if ( self::$backend==self::BACKEND_PDO and self::$PDO ) { - return self::$PDO->errorCode(); - } - - return -1; + $code = self::$connection->errorCode(); + return $code; } /** * returns the error code and message as a string for logging - * works with MDB2 and PDOException + * works with DoctrineException * @param mixed $error * @return string */ public static function getErrorMessage($error) { - if ( class_exists('PEAR') === true && PEAR::isError($error) ) { - $msg = $error->getCode() . ': ' . $error->getMessage(); - $msg .= ' (' . $error->getDebugInfo() . ')'; - - return $msg; - } - if (self::$backend==self::BACKEND_PDO and self::$PDO) { - $msg = self::$PDO->errorCode() . ': '; - $errorInfo = self::$PDO->errorInfo(); + if (self::$backend==self::BACKEND_DOCTRINE and self::$DOCTRINE) { + $msg = self::$DOCTRINE->errorCode() . ': '; + $errorInfo = self::$DOCTRINE->errorInfo(); if (is_array($errorInfo)) { $msg .= 'SQLSTATE = '.$errorInfo[0] . ', '; $msg .= 'Driver Code = '.$errorInfo[1] . ', '; @@ -1015,180 +709,3 @@ class OC_DB { self::$cachingEnabled = $enabled; } } - -/** - * small wrapper around PDOStatement to make it behave ,more like an MDB2 Statement - */ -class PDOStatementWrapper{ - /** - * @var PDOStatement - */ - private $statement = null; - private $isManipulation = false; - private $lastArguments = array(); - - public function __construct($statement, $isManipulation = false) { - $this->statement = $statement; - $this->isManipulation = $isManipulation; - } - - /** - * make execute return the result or updated row count instead of a bool - */ - public function execute($input=array()) { - if(OC_Config::getValue( "log_query", false)) { - $params_str = str_replace("\n"," ",var_export($input,true)); - OC_Log::write('core', 'DB execute with arguments : '.$params_str, OC_Log::DEBUG); - } - $this->lastArguments = $input; - if (count($input) > 0) { - - if (!isset($type)) { - $type = OC_Config::getValue( "dbtype", "sqlite" ); - } - - if ($type == 'mssql') { - $input = $this->tryFixSubstringLastArgumentDataForMSSQL($input); - } - - $result = $this->statement->execute($input); - } else { - $result = $this->statement->execute(); - } - - if ($result === false) { - return false; - } - if ($this->isManipulation) { - return $this->statement->rowCount(); - } else { - return $this; - } - } - - private function tryFixSubstringLastArgumentDataForMSSQL($input) { - $query = $this->statement->queryString; - $pos = stripos ($query, 'SUBSTRING'); - - if ( $pos === false) { - return; - } - - try { - $newQuery = ''; - - $cArg = 0; - - $inSubstring = false; - - // Create new query - for ($i = 0; $i < strlen ($query); $i++) { - if ($inSubstring == false) { - // Defines when we should start inserting values - if (substr ($query, $i, 9) == 'SUBSTRING') { - $inSubstring = true; - } - } else { - // Defines when we should stop inserting values - if (substr ($query, $i, 1) == ')') { - $inSubstring = false; - } - } - - if (substr ($query, $i, 1) == '?') { - // We found a question mark - if ($inSubstring) { - $newQuery .= $input[$cArg]; - - // - // Remove from input array - // - array_splice ($input, $cArg, 1); - } else { - $newQuery .= substr ($query, $i, 1); - $cArg++; - } - } else { - $newQuery .= substr ($query, $i, 1); - } - } - - // The global data we need - $name = OC_Config::getValue( "dbname", "owncloud" ); - $host = OC_Config::getValue( "dbhost", "" ); - $user = OC_Config::getValue( "dbuser", "" ); - $pass = OC_Config::getValue( "dbpassword", "" ); - if (strpos($host,':')) { - list($host, $port) = explode(':', $host, 2); - } else { - $port = false; - } - $opts = array(); - - if ($port) { - $dsn = 'sqlsrv:Server='.$host.','.$port.';Database='.$name; - } else { - $dsn = 'sqlsrv:Server='.$host.';Database='.$name; - } - - $PDO = new PDO($dsn, $user, $pass, $opts); - $PDO->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); - $PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - - $this->statement = $PDO->prepare($newQuery); - - $this->lastArguments = $input; - - return $input; - } catch (PDOException $e){ - $entry = 'PDO DB Error: "'.$e->getMessage().'"<br />'; - $entry .= 'Offending command was: '.$this->statement->queryString .'<br />'; - $entry .= 'Input parameters: ' .print_r($input, true).'<br />'; - $entry .= 'Stack trace: ' .$e->getTraceAsString().'<br />'; - OC_Log::write('core', $entry, OC_Log::FATAL); - OC_User::setUserId(null); - - // send http status 503 - header('HTTP/1.1 503 Service Temporarily Unavailable'); - header('Status: 503 Service Temporarily Unavailable'); - OC_Template::printErrorPage('Failed to connect to database'); - die ($entry); - } - } - - /** - * provide numRows - */ - public function numRows() { - $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i'; - if (preg_match($regex, $this->statement->queryString, $output) > 0) { - $query = OC_DB::prepare("SELECT COUNT(*) FROM {$output[1]}"); - return $query->execute($this->lastArguments)->fetchColumn(); - }else{ - return $this->statement->rowCount(); - } - } - - /** - * provide an alias for fetch - */ - public function fetchRow() { - return $this->statement->fetch(); - } - - /** - * pass all other function directly to the PDOStatement - */ - public function __call($name, $arguments) { - return call_user_func_array(array($this->statement, $name), $arguments); - } - - /** - * Provide a simple fetchOne. - * fetch single column from the next row - * @param int $colnum the column number to fetch - */ - public function fetchOne($colnum = 0) { - return $this->statement->fetchColumn($colnum); - } -} diff --git a/lib/db/mdb2schemareader.php b/lib/db/mdb2schemareader.php new file mode 100644 index 00000000000..0ead9528c93 --- /dev/null +++ b/lib/db/mdb2schemareader.php @@ -0,0 +1,241 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_DB_MDB2SchemaReader { + static protected $DBNAME; + static protected $DBTABLEPREFIX; + static protected $platform; + + /** + * @param $file + * @param $platform + * @return \Doctrine\DBAL\Schema\Schema + * @throws DomainException + */ + public static function loadSchemaFromFile($file, $platform) { + self::$DBNAME = OC_Config::getValue( "dbname", "owncloud" ); + self::$DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" ); + self::$platform = $platform; + $schema = new \Doctrine\DBAL\Schema\Schema(); + $xml = simplexml_load_file($file); + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'name': + case 'create': + case 'overwrite': + case 'charset': + break; + case 'table': + self::loadTable($schema, $child); + break; + default: + throw new DomainException('Unknown element: '.$child->getName()); + + } + } + return $schema; + } + + /** + * @param\Doctrine\DBAL\Schema\Schema $schema + * @param $xml + * @throws DomainException + */ + private static function loadTable($schema, $xml) { + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'name': + $name = (string)$child; + $name = str_replace( '*dbprefix*', self::$DBTABLEPREFIX, $name ); + $name = self::$platform->quoteIdentifier($name); + $table = $schema->createTable($name); + break; + case 'create': + case 'overwrite': + case 'charset': + break; + case 'declaration': + self::loadDeclaration($table, $child); + break; + default: + throw new DomainException('Unknown element: '.$child->getName()); + + } + } + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param $xml + * @throws DomainException + */ + private static function loadDeclaration($table, $xml) { + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'field': + self::loadField($table, $child); + break; + case 'index': + self::loadIndex($table, $child); + break; + default: + throw new DomainException('Unknown element: '.$child->getName()); + + } + } + } + + private static function loadField($table, $xml) { + $options = array(); + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'name': + $name = (string)$child; + $name = self::$platform->quoteIdentifier($name); + break; + case 'type': + $type = (string)$child; + switch($type) { + case 'text': + $type = 'string'; + break; + case 'clob': + $type = 'text'; + break; + case 'timestamp': + $type = 'datetime'; + break; + // TODO + return; + } + break; + case 'length': + $length = (string)$child; + $options['length'] = $length; + break; + case 'unsigned': + $unsigned = self::asBool($child); + $options['unsigned'] = $unsigned; + break; + case 'notnull': + $notnull = self::asBool($child); + $options['notnull'] = $notnull; + break; + case 'autoincrement': + $autoincrement = self::asBool($child); + $options['autoincrement'] = $autoincrement; + break; + case 'default': + $default = (string)$child; + $options['default'] = $default; + break; + case 'comments': + $comment = (string)$child; + $options['comment'] = $comment; + break; + default: + throw new DomainException('Unknown element: '.$child->getName()); + + } + } + if (isset($name) && isset($type)) { + if (empty($options['default'])) { + if (empty($options['notnull']) || !$options['notnull']) { + unset($options['default']); + $options['notnull'] = false; + } else { + $options['default'] = ''; + } + if ($type == 'integer') { + $options['default'] = 0; + } + if (!empty($options['autoincrement']) && $options['autoincrement']) { + unset($options['default']); + } + } + if ($type == 'integer' && isset($options['length'])) { + $length = $options['length']; + if ($length < 4) { + $type = 'smallint'; + } + else if ($length > 4) { + $type = 'bigint'; + } + } + if (!empty($options['autoincrement']) + && !empty($options['notnull'])) { + $options['primary'] = true; + } + $table->addColumn($name, $type, $options); + if (!empty($options['primary']) && $options['primary']) { + $table->setPrimaryKey(array($name)); + } + } + } + + private static function loadIndex($table, $xml) { + $name = null; + $fields = array(); + foreach($xml->children() as $child) { + switch($child->getName()) { + case 'name': + $name = (string)$child; + break; + case 'primary': + $primary = self::asBool($child); + break; + case 'unique': + $unique = self::asBool($child); + break; + case 'field': + foreach($child->children() as $field) { + switch($field->getName()) { + case 'name': + $field_name = (string)$field; + $field_name = self::$platform->quoteIdentifier($field_name); + $fields[] = $field_name; + break; + case 'sorting': + break; + default: + throw new DomainException('Unknown element: '.$field->getName()); + + } + } + break; + default: + throw new DomainException('Unknown element: '.$child->getName()); + + } + } + if (!empty($fields)) { + if (isset($primary) && $primary) { + $table->setPrimaryKey($fields, $name); + } else + if (isset($unique) && $unique) { + $table->addUniqueIndex($fields, $name); + } else { + $table->addIndex($fields, $name); + } + } else { + throw new DomainException('Empty index definition: '.$name.' options:'. print_r($fields, true)); + } + } + + private static function asBool($xml) { + $result = (string)$xml; + if ($result == 'true') { + $result = true; + } else + if ($result == 'false') { + $result = false; + } + return (bool)$result; + } + +} diff --git a/lib/db/mdb2schemawriter.php b/lib/db/mdb2schemawriter.php new file mode 100644 index 00000000000..21b43cbfe80 --- /dev/null +++ b/lib/db/mdb2schemawriter.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_DB_MDB2SchemaWriter { + + /** + * @param $file + * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $sm + * @return bool + */ + static public function saveSchemaToFile($file, $sm) { + $xml = new SimpleXMLElement('<database/>'); + $xml->addChild('name', OC_Config::getValue( "dbname", "owncloud" )); + $xml->addChild('create', 'true'); + $xml->addChild('overwrite', 'false'); + $xml->addChild('charset', 'utf8'); + foreach ($sm->listTables() as $table) { + self::saveTable($table, $xml->addChild('table')); + } + file_put_contents($file, $xml->asXML()); + return true; + } + + private static function saveTable($table, $xml) { + $xml->addChild('name', $table->getName()); + $declaration = $xml->addChild('declaration'); + foreach($table->getColumns() as $column) { + self::saveColumn($column, $declaration->addChild('field')); + } + foreach($table->getIndexes() as $index) { + if ($index->getName() == 'PRIMARY') { + $autoincrement = false; + foreach($index->getColumns() as $column) { + if ($table->getColumn($column)->getAutoincrement()) { + $autoincrement = true; + } + } + if ($autoincrement) { + continue; + } + } + self::saveIndex($index, $declaration->addChild('index')); + } + } + + private static function saveColumn($column, $xml) { + $xml->addChild('name', $column->getName()); + switch($column->getType()) { + case 'SmallInt': + case 'Integer': + case 'BigInt': + $xml->addChild('type', 'integer'); + $default = $column->getDefault(); + if (is_null($default) && $column->getAutoincrement()) { + $default = '0'; + } + $xml->addChild('default', $default); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + if ($column->getAutoincrement()) { + $xml->addChild('autoincrement', '1'); + } + if ($column->getUnsigned()) { + $xml->addChild('unsigned', 'true'); + } + $length = '4'; + if ($column->getType() == 'SmallInt') { + $length = '2'; + } + elseif ($column->getType() == 'BigInt') { + $length = '8'; + } + $xml->addChild('length', $length); + break; + case 'String': + $xml->addChild('type', 'text'); + $default = trim($column->getDefault()); + if ($default === '') { + $default = false; + } + $xml->addChild('default', $default); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + $xml->addChild('length', $column->getLength()); + break; + case 'Text': + $xml->addChild('type', 'clob'); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + break; + case 'Decimal': + $xml->addChild('type', 'decimal'); + $xml->addChild('default', $column->getDefault()); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + $xml->addChild('length', '15'); + break; + case 'Boolean': + $xml->addChild('type', 'integer'); + $xml->addChild('default', $column->getDefault()); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + $xml->addChild('length', '1'); + break; + case 'DateTime': + $xml->addChild('type', 'timestamp'); + $xml->addChild('default', $column->getDefault()); + $xml->addChild('notnull', self::toBool($column->getNotnull())); + break; + + } + } + + private static function saveIndex($index, $xml) { + $xml->addChild('name', $index->getName()); + if ($index->isPrimary()) { + $xml->addChild('primary', 'true'); + } + elseif ($index->isUnique()) { + $xml->addChild('unique', 'true'); + } + foreach($index->getColumns() as $column) { + $field = $xml->addChild('field'); + $field->addChild('name', $column); + $field->addChild('sorting', 'ascending'); + + } + } + + private static function toBool($bool) { + return $bool ? 'true' : 'false'; + } +} diff --git a/lib/db/schema.php b/lib/db/schema.php new file mode 100644 index 00000000000..fa053c64ef0 --- /dev/null +++ b/lib/db/schema.php @@ -0,0 +1,136 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_DB_Schema { + /** + * @brief saves database scheme to xml file + * @param \Doctrine\DBAL\Connection $conn + * @param string $file name of file + * @param int|string $mode + * @return bool + * + * TODO: write more documentation + */ + public static function getDbStructure( $conn, $file, $mode=MDB2_SCHEMA_DUMP_STRUCTURE) { + $sm = $conn->getSchemaManager(); + + return OC_DB_MDB2SchemaWriter::saveSchemaToFile($file, $sm); + } + + /** + * @brief Creates tables from XML file + * @param string $file file to read structure from + * @return bool + * + * TODO: write more documentation + */ + public static function createDbFromStructure( $conn, $file ) { + $toSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file, $conn->getDatabasePlatform()); + return self::executeSchemaChange($conn, $toSchema); + } + + /** + * @brief update the database scheme + * @param string $file file to read structure from + * @return bool + */ + public static function updateDbFromStructure($conn, $file) { + $sm = $conn->getSchemaManager(); + $fromSchema = $sm->createSchema(); + + $toSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file, $conn->getDatabasePlatform()); + + // remove tables we don't know about + foreach($fromSchema->getTables() as $table) { + if (!$toSchema->hasTable($table->getName())) { + $fromSchema->dropTable($table->getName()); + } + } + // remove sequences we don't know about + foreach($fromSchema->getSequences() as $table) { + if (!$toSchema->hasSequence($table->getName())) { + $fromSchema->dropSequence($table->getName()); + } + } + + $comparator = new \Doctrine\DBAL\Schema\Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + $platform = $conn->getDatabasePlatform(); + $tables = $schemaDiff->newTables + $schemaDiff->changedTables + $schemaDiff->removedTables; + foreach($tables as $tableDiff) { + $tableDiff->name = $platform->quoteIdentifier($tableDiff->name); + } + + + //$from = $fromSchema->toSql($conn->getDatabasePlatform()); + //$to = $toSchema->toSql($conn->getDatabasePlatform()); + //echo($from[9]); + //echo '<br>'; + //echo($to[9]); + //var_dump($from, $to); + return self::executeSchemaChange($conn, $schemaDiff); + } + + /** + * @brief drop a table + * @param string $tableName the table to drop + */ + public static function dropTable($conn, $tableName) { + $sm = $conn->getSchemaManager(); + $fromSchema = $sm->createSchema(); + $toSchema = clone $fromSchema; + $toSchema->dropTable($tableName); + $sql = $fromSchema->getMigrateToSql($toSchema, $conn->getDatabasePlatform()); + $conn->execute($sql); + } + + /** + * remove all tables defined in a database structure xml file + * @param string $file the xml file describing the tables + */ + public static function removeDBStructure($conn, $file) { + $fromSchema = OC_DB_MDB2SchemaReader::loadSchemaFromFile($file, $conn->getDatabasePlatform()); + $toSchema = clone $fromSchema; + foreach($toSchema->getTables() as $table) { + $toSchema->dropTable($table->getName()); + } + $comparator = new \Doctrine\DBAL\Schema\Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + self::executeSchemaChange($conn, $schemaDiff); + } + + /** + * @brief replaces the ownCloud tables with a new set + * @param $file string path to the MDB2 xml db export file + */ + public static function replaceDB( $conn, $file ) { + $apps = OC_App::getAllApps(); + self::beginTransaction(); + // Delete the old tables + self::removeDBStructure( $conn, OC::$SERVERROOT . '/db_structure.xml' ); + + foreach($apps as $app) { + $path = OC_App::getAppPath($app).'/appinfo/database.xml'; + if(file_exists($path)) { + self::removeDBStructure( $conn, $path ); + } + } + + // Create new tables + self::commit(); + } + + private static function executeSchemaChange($conn, $schema) { + $conn->beginTransaction(); + foreach($schema->toSql($conn->getDatabasePlatform()) as $sql) { + $conn->query($sql); + } + $conn->commit(); + } +} diff --git a/lib/db/statementwrapper.php b/lib/db/statementwrapper.php new file mode 100644 index 00000000000..f7bc45e068f --- /dev/null +++ b/lib/db/statementwrapper.php @@ -0,0 +1,191 @@ +<?php +/** + * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +/** + * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement + */ +class OC_DB_StatementWrapper { + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $statement = null; + private $isManipulation = false; + private $lastArguments = array(); + + public function __construct($statement, $isManipulation) { + $this->statement = $statement; + $this->isManipulation = $isManipulation; + } + + /** + * pass all other function directly to the \Doctrine\DBAL\Driver\Statement + */ + public function __call($name,$arguments) { + return call_user_func_array(array($this->statement,$name), $arguments); + } + + /** + * provide numRows + */ + public function numRows() { + $type = OC_Config::getValue( "dbtype", "sqlite" ); + if ($type == 'oci') { + // OCI doesn't have a queryString, just do a rowCount for now + return $this->statement->rowCount(); + } + $regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i'; + $queryString = $this->statement->getWrappedStatement()->queryString; + if (preg_match($regex, $queryString, $output) > 0) { + $query = OC_DB::prepare("SELECT COUNT(*) FROM {$output[1]}"); + return $query->execute($this->lastArguments)->fetchColumn(); + }else{ + return $this->statement->rowCount(); + } + } + + /** + * make execute return the result instead of a bool + */ + public function execute($input=array()) { + if(OC_Config::getValue( "log_query", false)) { + $params_str = str_replace("\n"," ",var_export($input,true)); + OC_Log::write('core', 'DB execute with arguments : '.$params_str, OC_Log::DEBUG); + } + $this->lastArguments = $input; + if (count($input) > 0) { + + if (!isset($type)) { + $type = OC_Config::getValue( "dbtype", "sqlite" ); + } + + if ($type == 'mssql') { + $input = $this->tryFixSubstringLastArgumentDataForMSSQL($input); + } + + $result = $this->statement->execute($input); + } else { + $result = $this->statement->execute(); + } + + if ($result === false) { + return false; + } + if ($this->isManipulation) { + return $this->statement->rowCount(); + } else { + return $this; + } + } + + private function tryFixSubstringLastArgumentDataForMSSQL($input) { + $query = $this->statement->getWrappedStatement()->queryString; + $pos = stripos ($query, 'SUBSTRING'); + + if ( $pos === false) { + return $input; + } + + try { + $newQuery = ''; + + $cArg = 0; + + $inSubstring = false; + + // Create new query + for ($i = 0; $i < strlen ($query); $i++) { + if ($inSubstring == false) { + // Defines when we should start inserting values + if (substr ($query, $i, 9) == 'SUBSTRING') { + $inSubstring = true; + } + } else { + // Defines when we should stop inserting values + if (substr ($query, $i, 1) == ')') { + $inSubstring = false; + } + } + + if (substr ($query, $i, 1) == '?') { + // We found a question mark + if ($inSubstring) { + $newQuery .= $input[$cArg]; + + // + // Remove from input array + // + array_splice ($input, $cArg, 1); + } else { + $newQuery .= substr ($query, $i, 1); + $cArg++; + } + } else { + $newQuery .= substr ($query, $i, 1); + } + } + + // The global data we need + $name = OC_Config::getValue( "dbname", "owncloud" ); + $host = OC_Config::getValue( "dbhost", "" ); + $user = OC_Config::getValue( "dbuser", "" ); + $pass = OC_Config::getValue( "dbpassword", "" ); + if (strpos($host,':')) { + list($host, $port) = explode(':', $host, 2); + } else { + $port = false; + } + $opts = array(); + + if ($port) { + $dsn = 'sqlsrv:Server='.$host.','.$port.';Database='.$name; + } else { + $dsn = 'sqlsrv:Server='.$host.';Database='.$name; + } + + $PDO = new PDO($dsn, $user, $pass, $opts); + $PDO->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + $PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $this->statement = $PDO->prepare($newQuery); + + $this->lastArguments = $input; + + return $input; + } catch (PDOException $e){ + $entry = 'PDO DB Error: "'.$e->getMessage().'"<br />'; + $entry .= 'Offending command was: '.$this->statement->queryString .'<br />'; + $entry .= 'Input parameters: ' .print_r($input, true).'<br />'; + $entry .= 'Stack trace: ' .$e->getTraceAsString().'<br />'; + OC_Log::write('core', $entry, OC_Log::FATAL); + OC_User::setUserId(null); + + // send http status 503 + header('HTTP/1.1 503 Service Temporarily Unavailable'); + header('Status: 503 Service Temporarily Unavailable'); + OC_Template::printErrorPage('Failed to connect to database'); + die ($entry); + } + } + + /** + * provide an alias for fetch + */ + public function fetchRow() { + return $this->statement->fetch(); + } + + /** + * Provide a simple fetchOne. + * fetch single column from the next row + * @param int $colnum the column number to fetch + * @return string + */ + public function fetchOne($colnum = 0) { + return $this->statement->fetchColumn($colnum); + } +} diff --git a/lib/defaults.php b/lib/defaults.php index 196bb5cf14d..10813a3e8d8 100644 --- a/lib/defaults.php +++ b/lib/defaults.php @@ -16,6 +16,7 @@ class OC_Defaults { private $defaultEntity; private $defaultName; + private $defaultTitle; private $defaultBaseUrl; private $defaultSyncClientUrl; private $defaultDocBaseUrl; @@ -25,8 +26,9 @@ class OC_Defaults { function __construct() { $l = OC_L10N::get('core'); - $this->defaultEntity = "ownCloud"; - $this->defaultName = "ownCloud"; + $this->defaultEntity = "ownCloud"; /* e.g. company name, used for footers and copyright notices */ + $this->defaultName = "ownCloud"; /* short name, used when referring to the software */ + $this->defaultTitle = "ownCloud"; /* can be a longer name, for titles */ $this->defaultBaseUrl = "http://owncloud.org"; $this->defaultSyncClientUrl = " http://owncloud.org/sync-clients/"; $this->defaultDocBaseUrl = "http://doc.owncloud.org"; @@ -69,6 +71,14 @@ class OC_Defaults { } } + public function getTitle() { + if ($this->themeExist('getTitle')) { + return $this->theme->getTitle(); + } else { + return $this->defaultTitle; + } + } + public function getName() { if ($this->themeExist('getName')) { return $this->theme->getName(); diff --git a/lib/eventsource.php b/lib/eventsource.php index 31d6edc1874..a83084d9251 100644 --- a/lib/eventsource.php +++ b/lib/eventsource.php @@ -53,7 +53,7 @@ class OC_EventSource{ /** * send a message to the client * @param string $type - * @param object $data + * @param mixed $data * * if only one parameter is given, a typeless message will be send with that parameter as data */ diff --git a/lib/files.php b/lib/files.php index f5dffd970d2..c705d2adb1a 100644 --- a/lib/files.php +++ b/lib/files.php @@ -62,7 +62,8 @@ class OC_Files { $zip = new ZipArchive(); $filename = OC_Helper::tmpFile('.zip'); if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { - exit("cannot open <$filename>\n"); + $l = OC_L10N::get('lib'); + throw new Exception($l->t('cannot open "%s"', array($filename))); } foreach ($files as $file) { $file = $dir . '/' . $file; @@ -93,7 +94,8 @@ class OC_Files { $zip = new ZipArchive(); $filename = OC_Helper::tmpFile('.zip'); if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { - exit("cannot open <$filename>\n"); + $l = OC_L10N::get('lib'); + throw new Exception($l->t('cannot open "%s"', array($filename))); } $file = $dir . '/' . $files; self::zipAddDir($file, $zip); @@ -220,16 +222,11 @@ class OC_Files { if (!OC_Config::getValue('allowZipDownload', true)) { $l = OC_L10N::get('lib'); header("HTTP/1.0 409 Conflict"); - $tmpl = new OC_Template('', 'error', 'user'); - $errors = array( - array( - 'error' => $l->t('ZIP download is turned off.'), - 'hint' => $l->t('Files need to be downloaded one by one.') - . '<br/><a href="javascript:history.back()">' . $l->t('Back to Files') . '</a>', - ) + OC_Template::printErrorPage( + $l->t('ZIP download is turned off.'), + $l->t('Files need to be downloaded one by one.') + . '<br/><a href="javascript:history.back()">' . $l->t('Back to Files') . '</a>' ); - $tmpl->assign('errors', $errors); - $tmpl->printPage(); exit; } @@ -252,17 +249,12 @@ class OC_Files { if ($totalsize > $zipLimit) { $l = OC_L10N::get('lib'); header("HTTP/1.0 409 Conflict"); - $tmpl = new OC_Template('', 'error', 'user'); - $errors = array( - array( - 'error' => $l->t('Selected files too large to generate zip file.'), - 'hint' => 'Download the files in smaller chunks, seperately' - .' or kindly ask your administrator.<br/><a href="javascript:history.back()">' - . $l->t('Back to Files') . '</a>', - ) + OC_Template::printErrorPage( + $l->t('Selected files too large to generate zip file.'), + $l->t('Download the files in smaller chunks, seperately or kindly ask your administrator.') + .'<br/><a href="javascript:history.back()">' + . $l->t('Back to Files') . '</a>' ); - $tmpl->assign('errors', $errors); - $tmpl->printPage(); exit; } } diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 9b94a24f481..dd212d84cc5 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -9,8 +9,18 @@ namespace OC\Files\Cache; use OC\Files\Filesystem; +use OC\Hooks\BasicEmitter; -class Scanner { +/** + * Class Scanner + * + * Hooks available in scope \OC\Files\Cache\Scanner: + * - scanFile(string $path, string $storageId) + * - scanFolder(string $path, string $storageId) + * + * @package OC\Files\Cache + */ +class Scanner extends BasicEmitter { /** * @var \OC\Files\Storage\Storage $storage */ @@ -65,16 +75,18 @@ class Scanner { * * @param string $file * @param int $reuseExisting + * @param bool $parentExistsInCache * @return array with metadata of the scanned file */ - public function scanFile($file, $reuseExisting = 0) { + public function scanFile($file, $reuseExisting = 0, $parentExistsInCache = false) { if (!self::isPartialFile($file) and !Filesystem::isFileBlacklisted($file) ) { + $this->emit('\OC\Files\Cache\Scanner', 'scanFile', array($file, $this->storageId)); \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); $data = $this->getData($file); if ($data) { - if ($file) { + if ($file and !$parentExistsInCache) { $parent = dirname($file); if ($parent === '.' or $parent === '/') { $parent = ''; @@ -97,9 +109,9 @@ class Scanner { // Only update metadata that has changed $newData = array_diff($data, $cacheData); } - } - if (!empty($newData)) { - $this->cache->put($file, $newData); + if (!empty($newData)) { + $this->cache->put($file, $newData); + } } return $data; } @@ -134,7 +146,7 @@ class Scanner { if ($reuse === -1) { $reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : 0; } - \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_folder', array('path' => $path, 'storage' => $this->storageId)); + $this->emit('\OC\Files\Cache\Scanner', 'scanFolder', array($path, $this->storageId)); $size = 0; $childQueue = array(); $existingChildren = array(); @@ -151,7 +163,7 @@ class Scanner { $child = ($path) ? $path . '/' . $file : $file; if (!Filesystem::isIgnoredDir($file)) { $newChildren[] = $file; - $data = $this->scanFile($child, $reuse); + $data = $this->scanFile($child, $reuse, true); if ($data) { if ($data['size'] === -1) { if ($recursive === self::SCAN_RECURSIVE) { diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 1bf7270c7f1..d6ebe7d629a 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -148,13 +148,20 @@ class Filesystem { */ private static $loader; - public static function getLoader(){ + public static function getLoader() { if (!self::$loader) { self::$loader = new Loader(); } return self::$loader; } + public static function getMountManager() { + if (!self::$mounts) { + \OC_Util::setupFS(); + } + return self::$mounts; + } + /** * get the mountpoint of the storage object for a path * ( note: because a storage is not always mounted inside the fakeroot, the diff --git a/lib/files/utils/scanner.php b/lib/files/utils/scanner.php new file mode 100644 index 00000000000..800bb649934 --- /dev/null +++ b/lib/files/utils/scanner.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Utils; + +use OC\Hooks\BasicEmitter; +use OC\Files\Filesystem; + +/** + * Class Scanner + * + * Hooks available in scope \OC\Utils\Scanner + * - scanFile(string $absolutePath) + * - scanFolder(string $absolutePath) + * + * @package OC\Files\Utils + */ +class Scanner extends BasicEmitter { + /** + * @var string $user + */ + private $user; + + /** + * @param string $user + */ + public function __construct($user) { + $this->user = $user; + } + + /** + * get all storages for $dir + * + * @param string $dir + * @return \OC\Files\Mount\Mount[] + */ + protected function getMounts($dir) { + //TODO: move to the node based fileapi once that's done + \OC_Util::tearDownFS(); + \OC_Util::setupFS($this->user); + $absolutePath = Filesystem::getView()->getAbsolutePath($dir); + + $mountManager = Filesystem::getMountManager(); + $mounts = $mountManager->findIn($absolutePath); + $mounts[] = $mountManager->find($absolutePath); + $mounts = array_reverse($mounts); //start with the mount of $dir + + return $mounts; + } + + /** + * attach listeners to the scanner + * + * @param \OC\Files\Mount\Mount $mount + */ + protected function attachListener($mount) { + $scanner = $mount->getStorage()->getScanner(); + $scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function ($path) use ($mount) { + $this->emit('\OC\Files\Utils\Scanner', 'scanFile', array($mount->getMountPoint() . $path)); + }); + $scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function ($path) use ($mount) { + $this->emit('\OC\Files\Utils\Scanner', 'scanFolder', array($mount->getMountPoint() . $path)); + }); + } + + public function backgroundScan($dir) { + $mounts = $this->getMounts($dir); + foreach ($mounts as $mount) { + $scanner = $mount->getStorage()->getScanner(); + $this->attachListener($mount); + $scanner->backgroundScan(); + } + } + + public function scan($dir) { + $mounts = $this->getMounts($dir); + foreach ($mounts as $mount) { + $scanner = $mount->getStorage()->getScanner(); + $this->attachListener($mount); + $scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG); + } + } +} + diff --git a/lib/group.php b/lib/group.php index d1a830730b7..8fbf5f86418 100644 --- a/lib/group.php +++ b/lib/group.php @@ -34,28 +34,43 @@ * post_removeFromGroup(uid, gid) */ class OC_Group { - // The backend used for group management /** - * @var OC_Group_Interface[] + * @var \OC\Group\Manager $manager */ - private static $_usedBackends = array(); + private static $manager; + + /** + * @var \OC\User\Manager + */ + private static $userManager; + + /** + * @return \OC\Group\Manager + */ + public static function getManager() { + if (self::$manager) { + return self::$manager; + } + self::$userManager = \OC_User::getManager(); + self::$manager = new \OC\Group\Manager(self::$userManager); + return self::$manager; + } /** * @brief set the group backend - * @param string $backend The backend to use for user managment + * @param \OC_Group_Backend $backend The backend to use for user managment * @return bool */ - public static function useBackend( $backend ) { - if($backend instanceof OC_Group_Interface) { - self::$_usedBackends[]=$backend; - } + public static function useBackend($backend) { + self::getManager()->addBackend($backend); + return true; } /** * remove all used backends */ public static function clearBackends() { - self::$_usedBackends=array(); + self::getManager()->clearBackends(); } /** @@ -66,32 +81,13 @@ class OC_Group { * Tries to create a new group. If the group name already exists, false will * be returned. Basic checking of Group name */ - public static function createGroup( $gid ) { - // No empty group names! - if( !$gid ) { - return false; - } - // No duplicate group names - if( in_array( $gid, self::getGroups())) { - return false; - } - - $run = true; - OC_Hook::emit( "OC_Group", "pre_createGroup", array( "run" => &$run, "gid" => $gid )); - - if($run) { - //create the group in the first backend that supports creating groups - foreach(self::$_usedBackends as $backend) { - if(!$backend->implementsActions(OC_GROUP_BACKEND_CREATE_GROUP)) - continue; + public static function createGroup($gid) { + OC_Hook::emit("OC_Group", "pre_createGroup", array("run" => true, "gid" => $gid)); - $backend->createGroup($gid); - OC_Hook::emit( "OC_User", "post_createGroup", array( "gid" => $gid )); - - return true; - } - return false; - }else{ + if (self::getManager()->createGroup($gid)) { + OC_Hook::emit("OC_User", "post_createGroup", array("gid" => $gid)); + return true; + } else { return false; } } @@ -103,30 +99,22 @@ class OC_Group { * * Deletes a group and removes it from the group_user-table */ - public static function deleteGroup( $gid ) { + public static function deleteGroup($gid) { // Prevent users from deleting group admin - if( $gid == "admin" ) { + if ($gid == "admin") { return false; } - $run = true; - OC_Hook::emit( "OC_Group", "pre_deleteGroup", array( "run" => &$run, "gid" => $gid )); - - if($run) { - //delete the group from all backends - foreach(self::$_usedBackends as $backend) { - if(!$backend->implementsActions(OC_GROUP_BACKEND_DELETE_GROUP)) - continue; - - $backend->deleteGroup($gid); - OC_Hook::emit( "OC_User", "post_deleteGroup", array( "gid" => $gid )); + OC_Hook::emit("OC_Group", "pre_deleteGroup", array("run" => true, "gid" => $gid)); + $group = self::getManager()->get($gid); + if ($group) { + if ($group->delete()) { + OC_Hook::emit("OC_User", "post_deleteGroup", array("gid" => $gid)); return true; } - return false; - }else{ - return false; } + return false; } /** @@ -137,11 +125,11 @@ class OC_Group { * * Checks whether the user is member of a group or not. */ - public static function inGroup( $uid, $gid ) { - foreach(self::$_usedBackends as $backend) { - if($backend->inGroup($uid, $gid)) { - return true; - } + public static function inGroup($uid, $gid) { + $group = self::getManager()->get($gid); + $user = self::$userManager->get($uid); + if ($group and $user) { + return $group->inGroup($user); } return false; } @@ -154,33 +142,15 @@ class OC_Group { * * Adds a user to a group. */ - public static function addToGroup( $uid, $gid ) { - // Does the group exist? - if( !OC_Group::groupExists($gid)) { - return false; - } - - // Go go go - $run = true; - OC_Hook::emit( "OC_Group", "pre_addToGroup", array( "run" => &$run, "uid" => $uid, "gid" => $gid )); - - if($run) { - $success=false; - - //add the user to the all backends that have the group - foreach(self::$_usedBackends as $backend) { - if(!$backend->implementsActions(OC_GROUP_BACKEND_ADD_TO_GROUP)) - continue; - - if($backend->groupExists($gid)) { - $success|=$backend->addToGroup($uid, $gid); - } - } - if($success) { - OC_Hook::emit( "OC_User", "post_addToGroup", array( "uid" => $uid, "gid" => $gid )); - } - return $success; - }else{ + public static function addToGroup($uid, $gid) { + $group = self::getManager()->get($gid); + $user = self::$userManager->get($uid); + if ($group and $user) { + OC_Hook::emit("OC_Group", "pre_addToGroup", array("run" => true, "uid" => $uid, "gid" => $gid)); + $group->addUser($user); + OC_Hook::emit("OC_User", "post_addToGroup", array("uid" => $uid, "gid" => $gid)); + return true; + } else { return false; } } @@ -193,21 +163,15 @@ class OC_Group { * * removes the user from a group. */ - public static function removeFromGroup( $uid, $gid ) { - $run = true; - OC_Hook::emit( "OC_Group", "pre_removeFromGroup", array( "run" => &$run, "uid" => $uid, "gid" => $gid )); - - if($run) { - //remove the user from the all backends that have the group - foreach(self::$_usedBackends as $backend) { - if(!$backend->implementsActions(OC_GROUP_BACKEND_REMOVE_FROM_GOUP)) - continue; - - $backend->removeFromGroup($uid, $gid); - OC_Hook::emit( "OC_User", "post_removeFromGroup", array( "uid" => $uid, "gid" => $gid )); - } + public static function removeFromGroup($uid, $gid) { + $group = self::getManager()->get($gid); + $user = self::$userManager->get($uid); + if ($group and $user) { + OC_Hook::emit("OC_Group", "pre_removeFromGroup", array("run" => true, "uid" => $uid, "gid" => $gid)); + $group->removeUser($user); + OC_Hook::emit("OC_User", "post_removeFromGroup", array("uid" => $uid, "gid" => $gid)); return true; - }else{ + } else { return false; } } @@ -220,13 +184,18 @@ class OC_Group { * This function fetches all groups a user belongs to. It does not check * if the user exists at all. */ - public static function getUserGroups( $uid ) { - $groups=array(); - foreach(self::$_usedBackends as $backend) { - $groups=array_merge($backend->getUserGroups($uid), $groups); + public static function getUserGroups($uid) { + $user = self::$userManager->get($uid); + if ($user) { + $groups = self::getManager()->getUserGroups($user); + $groupIds = array(); + foreach ($groups as $group) { + $groupIds[] = $group->getGID(); + } + return $groupIds; + } else { + return array(); } - asort($groups); - return $groups; } /** @@ -235,27 +204,23 @@ class OC_Group { * * Returns a list with all groups */ - public static function getGroups($search = '', $limit = -1, $offset = 0) { - $groups = array(); - foreach (self::$_usedBackends as $backend) { - $groups = array_merge($backend->getGroups($search, $limit, $offset), $groups); + public static function getGroups($search = '', $limit = null, $offset = null) { + $groups = self::getManager()->search($search, $limit, $offset); + $groupIds = array(); + foreach ($groups as $group) { + $groupIds[] = $group->getGID(); } - asort($groups); - return $groups; + return $groupIds; } /** * check if a group exists + * * @param string $gid * @return bool */ public static function groupExists($gid) { - foreach(self::$_usedBackends as $backend) { - if ($backend->groupExists($gid)) { - return true; - } - } - return false; + return self::getManager()->groupExists($gid); } /** @@ -263,11 +228,17 @@ class OC_Group { * @returns array with user ids */ public static function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { - $users=array(); - foreach(self::$_usedBackends as $backend) { - $users = array_merge($backend->usersInGroup($gid, $search, $limit, $offset), $users); + $group = self::getManager()->get($gid); + if ($group) { + $users = $group->searchUsers($search . $limit, $offset); + $userIds = array(); + foreach ($users as $user) { + $userIds[] = $user->getUID(); + } + return $userIds; + } else { + return array(); } - return $users; } /** @@ -292,17 +263,17 @@ class OC_Group { * @returns array with display names (value) and user ids(key) */ public static function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) { - $displayNames=array(); - foreach(self::$_usedBackends as $backend) { - if($backend->implementsActions(OC_GROUP_BACKEND_GET_DISPLAYNAME)) { - $displayNames = array_merge($backend->displayNamesInGroup($gid, $search, $limit, $offset), $displayNames); - } else { - $users = $backend->usersInGroup($gid, $search, $limit, $offset); - $names = array_combine($users, $users); - $displayNames = array_merge($names, $displayNames); + $group = self::getManager()->get($gid); + if ($group) { + $users = $group->searchDisplayName($search . $limit, $offset); + $displayNames = array(); + foreach ($users as $user) { + $displayNames[] = $user->getDisplayName(); } + return $displayNames; + } else { + return array(); } - return $displayNames; } /** diff --git a/lib/group/group.php b/lib/group/group.php new file mode 100644 index 00000000000..a752c4311c1 --- /dev/null +++ b/lib/group/group.php @@ -0,0 +1,238 @@ +<?php + +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Group; + +class Group { + /** + * @var string $id + */ + private $gid; + + /** + * @var \OC\User\User[] $users + */ + private $users; + + /** + * @var \OC_Group_Backend[] | \OC_Group_Database[] $backend + */ + private $backends; + + /** + * @var \OC\Hooks\PublicEmitter $emitter; + */ + private $emitter; + + /** + * @var \OC\User\Manager $userManager + */ + private $userManager; + + /** + * @param string $gid + * @param \OC_Group_Backend[] $backends + * @param \OC\User\Manager $userManager + * @param \OC\Hooks\PublicEmitter $emitter + */ + public function __construct($gid, $backends, $userManager, $emitter = null) { + $this->gid = $gid; + $this->backends = $backends; + $this->userManager = $userManager; + $this->emitter = $emitter; + } + + public function getGID() { + return $this->gid; + } + + /** + * get all users in the group + * + * @return \OC\User\User[] + */ + public function getUsers() { + if ($this->users) { + return $this->users; + } + + $users = array(); + $userIds = array(); + foreach ($this->backends as $backend) { + $diff = array_diff( + $backend->usersInGroup($this->gid), + $userIds + ); + if ($diff) { + $userIds = array_merge($userIds, $diff); + } + } + + foreach ($userIds as $userId) { + $users[] = $this->userManager->get($userId); + } + $this->users = $users; + return $users; + } + + /** + * check if a user is in the group + * + * @param \OC\User\User $user + * @return bool + */ + public function inGroup($user) { + foreach ($this->backends as $backend) { + if ($backend->inGroup($user->getUID(), $this->gid)) { + return true; + } + } + return false; + } + + /** + * add a user to the group + * + * @param \OC\User\User $user + */ + public function addUser($user) { + if ($this->inGroup($user)) { + return; + } + + if ($this->emitter) { + $this->emitter->emit('\OC\Group', 'preAddUser', array($this, $user)); + } + foreach ($this->backends as $backend) { + if ($backend->implementsActions(OC_GROUP_BACKEND_ADD_TO_GROUP)) { + $backend->addToGroup($user->getUID(), $this->gid); + if ($this->users) { + $this->users[] = $user; + } + if ($this->emitter) { + $this->emitter->emit('\OC\Group', 'postAddUser', array($this, $user)); + } + return; + } + } + } + + /** + * remove a user from the group + * + * @param \OC\User\User $user + */ + public function removeUser($user) { + $result = false; + if ($this->emitter) { + $this->emitter->emit('\OC\Group', 'preRemoveUser', array($this, $user)); + } + foreach ($this->backends as $backend) { + if ($backend->implementsActions(OC_GROUP_BACKEND_REMOVE_FROM_GOUP) and $backend->inGroup($user->getUID(), $this->gid)) { + $backend->removeFromGroup($user->getUID(), $this->gid); + $result = true; + } + } + if ($result) { + if ($this->emitter) { + $this->emitter->emit('\OC\Group', 'postRemoveUser', array($this, $user)); + } + if ($this->users) { + foreach ($this->users as $index => $groupUser) { + if ($groupUser->getUID() === $user->getUID()) { + unset($this->users[$index]); + return; + } + } + } + } + } + + /** + * search for users in the group by userid + * + * @param string $search + * @param int $limit + * @param int $offset + * @return \OC\User\User[] + */ + public function searchUsers($search, $limit = null, $offset = null) { + $users = array(); + foreach ($this->backends as $backend) { + $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); + if (!is_null($limit)) { + $limit -= count($userIds); + } + if (!is_null($offset)) { + $offset -= count($userIds); + } + foreach ($userIds as $userId) { + $users[$userId] = $this->userManager->get($userId); + } + if (!is_null($limit) and $limit <= 0) { + return array_values($users); + } + } + return array_values($users); + } + + /** + * search for users in the group by displayname + * + * @param string $search + * @param int $limit + * @param int $offset + * @return \OC\User\User[] + */ + public function searchDisplayName($search, $limit = null, $offset = null) { + $users = array(); + foreach ($this->backends as $backend) { + if ($backend->implementsActions(OC_GROUP_BACKEND_GET_DISPLAYNAME)) { + $userIds = array_keys($backend->displayNamesInGroup($this->gid, $search, $limit, $offset)); + } else { + $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); + } + if (!is_null($limit)) { + $limit -= count($userIds); + } + if (!is_null($offset)) { + $offset -= count($userIds); + } + foreach ($userIds as $userId) { + $users[$userId] = $this->userManager->get($userId); + } + if (!is_null($limit) and $limit <= 0) { + return array_values($users); + } + } + return array_values($users); + } + + /** + * delete the group + * + * @return bool + */ + public function delete() { + $result = false; + if ($this->emitter) { + $this->emitter->emit('\OC\Group', 'preDelete', array($this)); + } + foreach ($this->backends as $backend) { + if ($backend->implementsActions(OC_GROUP_BACKEND_DELETE_GROUP)) { + $result = true; + $backend->deleteGroup($this->gid); + } + } + if ($result and $this->emitter) { + $this->emitter->emit('\OC\Group', 'postDelete', array($this)); + } + return $result; + } +} diff --git a/lib/group/manager.php b/lib/group/manager.php new file mode 100644 index 00000000000..bf469d51d12 --- /dev/null +++ b/lib/group/manager.php @@ -0,0 +1,169 @@ +<?php + +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Group; + +use OC\Hooks\PublicEmitter; + +/** + * Class Manager + * + * Hooks available in scope \OC\Group: + * - preAddUser(\OC\Group\Group $group, \OC\User\User $user) + * - postAddUser(\OC\Group\Group $group, \OC\User\User $user) + * - preRemoveUser(\OC\Group\Group $group, \OC\User\User $user) + * - postRemoveUser(\OC\Group\Group $group, \OC\User\User $user) + * - preDelete(\OC\Group\Group $group) + * - postDelete(\OC\Group\Group $group) + * - preCreate(string $groupId) + * - postCreate(\OC\Group\Group $group) + * + * @package OC\Group + */ +class Manager extends PublicEmitter { + /** + * @var \OC_Group_Backend[] | \OC_Group_Database[] $backends + */ + private $backends = array(); + + /** + * @var \OC\User\Manager $userManager + */ + private $userManager; + + /** + * @var \OC\Group\Group[] + */ + private $cachedGroups; + + /** + * @param \OC\User\Manager $userManager + */ + public function __construct($userManager) { + $this->userManager = $userManager; + $cache = & $this->cachedGroups; + $this->listen('\OC\Group', 'postDelete', function ($group) use (&$cache) { + /** + * @var \OC\Group\Group $group + */ + unset($cache[$group->getGID()]); + }); + } + + /** + * @param \OC_Group_Backend $backend + */ + public function addBackend($backend) { + $this->backends[] = $backend; + } + + public function clearBackends() { + $this->backends = array(); + $this->cachedGroups = array(); + } + + /** + * @param string $gid + * @return \OC\Group\Group + */ + public function get($gid) { + if (isset($this->cachedGroups[$gid])) { + return $this->cachedGroups[$gid]; + } + foreach ($this->backends as $backend) { + if ($backend->groupExists($gid)) { + return $this->getGroupObject($gid); + } + } + return null; + } + + protected function getGroupObject($gid) { + $backends = array(); + foreach ($this->backends as $backend) { + if ($backend->groupExists($gid)) { + $backends[] = $backend; + } + } + $this->cachedGroups[$gid] = new Group($gid, $backends, $this->userManager, $this); + return $this->cachedGroups[$gid]; + } + + /** + * @param string $gid + * @return bool + */ + public function groupExists($gid) { + return !is_null($this->get($gid)); + } + + /** + * @param string $gid + * @return \OC\Group\Group + */ + public function createGroup($gid) { + if (!$gid) { + return false; + } else if ($this->groupExists($gid)) { + return $this->get($gid); + } else { + $this->emit('\OC\Group', 'preCreate', array($gid)); + foreach ($this->backends as $backend) { + if ($backend->implementsActions(OC_GROUP_BACKEND_CREATE_GROUP)) { + $backend->createGroup($gid); + $group = $this->getGroupObject($gid); + $this->emit('\OC\Group', 'postCreate', array($group)); + return $group; + } + } + return null; + } + } + + /** + * @param string $search + * @param int $limit + * @param int $offset + * @return \OC\Group\Group[] + */ + public function search($search, $limit = null, $offset = null) { + $groups = array(); + foreach ($this->backends as $backend) { + $groupIds = $backend->getGroups($search, $limit, $offset); + if (!is_null($limit)) { + $limit -= count($groupIds); + } + if (!is_null($offset)) { + $offset -= count($groupIds); + } + foreach ($groupIds as $groupId) { + $groups[$groupId] = $this->getGroupObject($groupId); + } + if (!is_null($limit) and $limit <= 0) { + return array_values($groups); + } + } + return array_values($groups); + } + + /** + * @param \OC\User\User $user + * @return \OC\Group\Group[] + */ + public function getUserGroups($user) { + $groups = array(); + foreach ($this->backends as $backend) { + $groupIds = $backend->getUserGroups($user->getUID()); + foreach ($groupIds as $groupId) { + $groups[$groupId] = $this->getGroupObject($groupId); + } + } + return array_values($groups); + } +} diff --git a/lib/helper.php b/lib/helper.php index df0d120976d..ca508e1d933 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -176,8 +176,7 @@ class OC_Helper { }elseif( file_exists( OC::$SERVERROOT."/core/img/$image" )) { return OC::$WEBROOT."/core/img/$image"; }else{ - echo('image not found: image:'.$image.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); - die(); + throw new RuntimeException('image not found: image:'.$image.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); } } diff --git a/lib/installer.php b/lib/installer.php index 49ba4492632..dcd29f9e1ad 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -436,10 +436,30 @@ class OC_Installer{ $blacklist=array( 'exec(', - 'eval(' + 'eval(', // more evil pattern will go here later - // will will also check if an app is using private api once the public api is in place + // classes replaced by the public api + 'OC_API::', + 'OC_App::', + 'OC_AppConfig::', + 'OC_BackgroundJob::', + 'OC_Config::', + 'OC_DB::', + 'OC_Files::', + 'OC_Helper::', + 'OC_Hook::', + 'OC_Image::', + 'OC_JSON::', + 'OC_L10N::', + 'OC_Log::', + 'OC_Mail::', + 'OC_Preferences::', + 'OC_Request::', + 'OC_Response::', + 'OC_Template::', + 'OC_User::', + 'OC_Util::', ); // is the code checker enabled? diff --git a/lib/l10n/ca.php b/lib/l10n/ca.php index 93f7fa5f7bc..14f9edfe34c 100644 --- a/lib/l10n/ca.php +++ b/lib/l10n/ca.php @@ -5,11 +5,14 @@ "Users" => "Usuaris", "Apps" => "Aplicacions", "Admin" => "Administració", +"Failed to upgrade \"%s\"." => "Ha fallat l'actualització \"%s\".", "web services under your control" => "controleu els vostres serveis web", +"cannot open \"%s\"" => "no es pot obrir \"%s\"", "ZIP download is turned off." => "La baixada en ZIP està desactivada.", "Files need to be downloaded one by one." => "Els fitxers s'han de baixar d'un en un.", "Back to Files" => "Torna a Fitxers", "Selected files too large to generate zip file." => "Els fitxers seleccionats son massa grans per generar un fitxer zip.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Baixeu els fitxers en trossos petits, de forma separada, o pregunteu a l'administrador.", "couldn't be determined" => "no s'ha pogut determinar", "Application is not enabled" => "L'aplicació no està habilitada", "Authentication error" => "Error d'autenticació", @@ -49,5 +52,6 @@ "%d months ago" => "fa %d mesos", "last year" => "l'any passat", "years ago" => "anys enrere", +"Caused by:" => "Provocat per:", "Could not find category \"%s\"" => "No s'ha trobat la categoria \"%s\"" ); diff --git a/lib/l10n/da.php b/lib/l10n/da.php index 5f11453bcdd..789328a3100 100644 --- a/lib/l10n/da.php +++ b/lib/l10n/da.php @@ -5,11 +5,14 @@ "Users" => "Brugere", "Apps" => "Apps", "Admin" => "Admin", +"Failed to upgrade \"%s\"." => "Upgradering af \"%s\" fejlede", "web services under your control" => "Webtjenester under din kontrol", +"cannot open \"%s\"" => "Kan ikke åbne \"%s\"", "ZIP download is turned off." => "ZIP-download er slået fra.", "Files need to be downloaded one by one." => "Filer skal downloades en for en.", "Back to Files" => "Tilbage til Filer", "Selected files too large to generate zip file." => "De markerede filer er for store til at generere en ZIP-fil.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Download filerne i små bider, seperat, eller kontakt venligst din administrator.", "couldn't be determined" => "kunne ikke fastslås", "Application is not enabled" => "Programmet er ikke aktiveret", "Authentication error" => "Adgangsfejl", @@ -49,5 +52,6 @@ "%d months ago" => "%d måneder siden", "last year" => "sidste år", "years ago" => "år siden", +"Caused by:" => "Forårsaget af:", "Could not find category \"%s\"" => "Kunne ikke finde kategorien \"%s\"" ); diff --git a/lib/l10n/de.php b/lib/l10n/de.php index 4ef02402b9b..f1c4a1171e2 100644 --- a/lib/l10n/de.php +++ b/lib/l10n/de.php @@ -5,11 +5,14 @@ "Users" => "Benutzer", "Apps" => "Apps", "Admin" => "Administration", +"Failed to upgrade \"%s\"." => "Konnte \"%s\" nicht aktualisieren.", "web services under your control" => "Web-Services unter Deiner Kontrolle", +"cannot open \"%s\"" => "Öffnen von \"%s\" fehlgeschlagen", "ZIP download is turned off." => "Der ZIP-Download ist deaktiviert.", "Files need to be downloaded one by one." => "Die Dateien müssen einzeln heruntergeladen werden.", "Back to Files" => "Zurück zu \"Dateien\"", "Selected files too large to generate zip file." => "Die gewählten Dateien sind zu groß, um eine ZIP-Datei zu erstellen.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Lade die Dateien in kleineren, separaten, Stücken herunter oder bitte deinen Administrator.", "couldn't be determined" => "konnte nicht festgestellt werden", "Application is not enabled" => "Die Anwendung ist nicht aktiviert", "Authentication error" => "Fehler bei der Anmeldung", @@ -49,5 +52,6 @@ "%d months ago" => "Vor %d Monaten", "last year" => "Letztes Jahr", "years ago" => "Vor Jahren", +"Caused by:" => "Verursacht durch:", "Could not find category \"%s\"" => "Die Kategorie \"%s\" konnte nicht gefunden werden." ); diff --git a/lib/l10n/de_DE.php b/lib/l10n/de_DE.php index 823d423abcd..276e526e2ed 100644 --- a/lib/l10n/de_DE.php +++ b/lib/l10n/de_DE.php @@ -5,11 +5,14 @@ "Users" => "Benutzer", "Apps" => "Apps", "Admin" => "Administrator", +"Failed to upgrade \"%s\"." => "Konnte \"%s\" nicht aktualisieren.", "web services under your control" => "Web-Services unter Ihrer Kontrolle", +"cannot open \"%s\"" => "Öffnen von \"%s\" fehlgeschlagen", "ZIP download is turned off." => "Der ZIP-Download ist deaktiviert.", "Files need to be downloaded one by one." => "Die Dateien müssen einzeln heruntergeladen werden.", "Back to Files" => "Zurück zu \"Dateien\"", "Selected files too large to generate zip file." => "Die gewählten Dateien sind zu groß, um eine ZIP-Datei zu erstellen.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Laden Sie die Dateien in kleineren, separaten, Stücken herunter oder bitten Sie Ihren Administrator.", "couldn't be determined" => "konnte nicht ermittelt werden", "Application is not enabled" => "Die Anwendung ist nicht aktiviert", "Authentication error" => "Authentifizierungs-Fehler", @@ -49,5 +52,6 @@ "%d months ago" => "Vor %d Monaten", "last year" => "Letztes Jahr", "years ago" => "Vor Jahren", +"Caused by:" => "Verursacht durch:", "Could not find category \"%s\"" => "Die Kategorie \"%s\" konnte nicht gefunden werden." ); diff --git a/lib/l10n/es.php b/lib/l10n/es.php index 1f243a224e4..66011e61a0d 100644 --- a/lib/l10n/es.php +++ b/lib/l10n/es.php @@ -5,11 +5,14 @@ "Users" => "Usuarios", "Apps" => "Aplicaciones", "Admin" => "Administración", +"Failed to upgrade \"%s\"." => "Falló la actualización \"%s\".", "web services under your control" => "Servicios web bajo su control", +"cannot open \"%s\"" => "No se puede abrir \"%s\"", "ZIP download is turned off." => "La descarga en ZIP está desactivada.", "Files need to be downloaded one by one." => "Los archivos deben ser descargados uno por uno.", "Back to Files" => "Volver a Archivos", "Selected files too large to generate zip file." => "Los archivos seleccionados son demasiado grandes para generar el archivo zip.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Descargue los archivos en trozos más pequeños, por separado o solicítelos amablemente su administrador.", "couldn't be determined" => "no pudo ser determinado", "Application is not enabled" => "La aplicación no está habilitada", "Authentication error" => "Error de autenticación", @@ -49,5 +52,6 @@ "%d months ago" => "Hace %d meses", "last year" => "año pasado", "years ago" => "hace años", +"Caused by:" => "Causado por:", "Could not find category \"%s\"" => "No puede encontrar la categoria \"%s\"" ); diff --git a/lib/l10n/es_AR.php b/lib/l10n/es_AR.php index e66771f7e74..da0f6fae84a 100644 --- a/lib/l10n/es_AR.php +++ b/lib/l10n/es_AR.php @@ -3,51 +3,55 @@ "Personal" => "Personal", "Settings" => "Configuración", "Users" => "Usuarios", -"Apps" => "Aplicaciones", +"Apps" => "Apps", "Admin" => "Administración", -"web services under your control" => "servicios web que controlás", +"Failed to upgrade \"%s\"." => "No se pudo actualizar \"%s\".", +"web services under your control" => "servicios web sobre los que tenés control", +"cannot open \"%s\"" => "no se puede abrir \"%s\"", "ZIP download is turned off." => "La descarga en ZIP está desactivada.", "Files need to be downloaded one by one." => "Los archivos deben ser descargados de a uno.", -"Back to Files" => "Volver a archivos", +"Back to Files" => "Volver a Archivos", "Selected files too large to generate zip file." => "Los archivos seleccionados son demasiado grandes para generar el archivo zip.", -"couldn't be determined" => "no pudo ser determinado", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Descargá los archivos en partes más chicas, de forma separada, o pedíselos al administrador", +"couldn't be determined" => "no se pudo determinar", "Application is not enabled" => "La aplicación no está habilitada", "Authentication error" => "Error al autenticar", "Token expired. Please reload page." => "Token expirado. Por favor, recargá la página.", "Files" => "Archivos", "Text" => "Texto", "Images" => "Imágenes", -"%s enter the database username." => "%s Entre el Usuario de la Base de Datos", -"%s enter the database name." => "%s Entre el Nombre de la Base de Datos", -"%s you may not use dots in the database name" => "%s no puede usar puntos en el nombre de la Base de Datos", +"%s enter the database username." => "%s Entrá el usuario de la base de datos", +"%s enter the database name." => "%s Entrá el nombre de la base de datos.", +"%s you may not use dots in the database name" => "%s no podés usar puntos en el nombre de la base de datos", "MS SQL username and/or password not valid: %s" => "Nombre de usuario y contraseña de MS SQL no son válidas: %s", -"You need to enter either an existing account or the administrator." => "Debe ingresar una cuenta existente o el administrador", +"You need to enter either an existing account or the administrator." => "Tenés que ingresar una cuenta existente o el administrador.", "MySQL username and/or password not valid" => "Usuario y/o contraseña MySQL no válido", "DB Error: \"%s\"" => "Error DB: \"%s\"", "Offending command was: \"%s\"" => "El comando no comprendido es: \"%s\"", -"MySQL user '%s'@'localhost' exists already." => "Usuario MySQL '%s'@'localhost' ya existente", +"MySQL user '%s'@'localhost' exists already." => "Usuario MySQL '%s'@'localhost' ya existe.", "Drop this user from MySQL" => "Borrar este usuario de MySQL", -"MySQL user '%s'@'%%' already exists" => "Usuario MySQL '%s'@'%%' ya existente", +"MySQL user '%s'@'%%' already exists" => "Usuario MySQL '%s'@'%%' ya existe", "Drop this user from MySQL." => "Borrar este usuario de MySQL", "Oracle connection could not be established" => "No fue posible establecer la conexión a Oracle", -"Oracle username and/or password not valid" => "El nombre de usuario y contraseña no son válidos", +"Oracle username and/or password not valid" => "El nombre de usuario y/o contraseña no son válidos", "Offending command was: \"%s\", name: %s, password: %s" => "El comando no comprendido es: \"%s\", nombre: \"%s\", contraseña: \"%s\"", -"PostgreSQL username and/or password not valid" => "Nombre de usuario o contraseña de PostgradeSQL no válido.", -"Set an admin username." => "Configurar un nombre de administrador", -"Set an admin password." => "Configurar una palabra clave de administrador", +"PostgreSQL username and/or password not valid" => "Nombre de usuario o contraseña PostgradeSQL inválido.", +"Set an admin username." => "Configurar un nombre de administrador.", +"Set an admin password." => "Configurar una contraseña de administrador.", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "Tu servidor web no está configurado todavía para permitir sincronización de archivos porque la interfaz WebDAV parece no funcionar.", "Please double check the <a href='%s'>installation guides</a>." => "Por favor, comprobá nuevamente la <a href='%s'>guía de instalación</a>.", "seconds ago" => "segundos atrás", "1 minute ago" => "hace 1 minuto", "%d minutes ago" => "hace %d minutos", -"1 hour ago" => "1 hora atrás", -"%d hours ago" => "%d horas atrás", +"1 hour ago" => "hace 1 hora", +"%d hours ago" => "hace %d horas", "today" => "hoy", "yesterday" => "ayer", "%d days ago" => "hace %d días", "last month" => "el mes pasado", -"%d months ago" => "%d meses atrás", +"%d months ago" => "hace %d meses", "last year" => "el año pasado", "years ago" => "años atrás", +"Caused by:" => "Provocado por:", "Could not find category \"%s\"" => "No fue posible encontrar la categoría \"%s\"" ); diff --git a/lib/l10n/et_EE.php b/lib/l10n/et_EE.php index 4da2c36d6ac..c2cd8ff0389 100644 --- a/lib/l10n/et_EE.php +++ b/lib/l10n/et_EE.php @@ -5,11 +5,14 @@ "Users" => "Kasutajad", "Apps" => "Rakendused", "Admin" => "Admin", +"Failed to upgrade \"%s\"." => "Ebaõnnestunud uuendus \"%s\".", "web services under your control" => "veebitenused sinu kontrolli all", +"cannot open \"%s\"" => "ei suuda avada \"%s\"", "ZIP download is turned off." => "ZIP-ina allalaadimine on välja lülitatud.", "Files need to be downloaded one by one." => "Failid tuleb alla laadida ükshaaval.", "Back to Files" => "Tagasi failide juurde", "Selected files too large to generate zip file." => "Valitud failid on ZIP-faili loomiseks liiga suured.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Laadi failid alla eraldi väiksemate osadena või küsi nõu oma süsteemiadminstraatorilt.", "couldn't be determined" => "ei suudetud tuvastada", "Application is not enabled" => "Rakendus pole sisse lülitatud", "Authentication error" => "Autentimise viga", @@ -49,5 +52,6 @@ "%d months ago" => "%d kuud tagasi", "last year" => "viimasel aastal", "years ago" => "aastat tagasi", +"Caused by:" => "Põhjustaja:", "Could not find category \"%s\"" => "Ei leia kategooriat \"%s\"" ); diff --git a/lib/l10n/eu.php b/lib/l10n/eu.php index 028ad0a631e..94b4cfb902e 100644 --- a/lib/l10n/eu.php +++ b/lib/l10n/eu.php @@ -5,11 +5,14 @@ "Users" => "Erabiltzaileak", "Apps" => "Aplikazioak", "Admin" => "Admin", +"Failed to upgrade \"%s\"." => "Ezin izan da \"%s\" eguneratu.", "web services under your control" => "web zerbitzuak zure kontrolpean", +"cannot open \"%s\"" => "ezin da \"%s\" ireki", "ZIP download is turned off." => "ZIP deskarga ez dago gaituta.", "Files need to be downloaded one by one." => "Fitxategiak banan-banan deskargatu behar dira.", "Back to Files" => "Itzuli fitxategietara", "Selected files too large to generate zip file." => "Hautatuko fitxategiak oso handiak dira zip fitxategia sortzeko.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Deskargatu fitzategiak zati txikiagoetan, banan-banan edo eskatu mesedez zure administradoreari", "couldn't be determined" => "ezin izan da zehaztu", "Application is not enabled" => "Aplikazioa ez dago gaituta", "Authentication error" => "Autentifikazio errorea", @@ -29,6 +32,7 @@ "Drop this user from MySQL" => "Ezabatu erabiltzaile hau MySQLtik", "MySQL user '%s'@'%%' already exists" => "MySQL '%s'@'%%' erabiltzailea dagoeneko existitzen da", "Drop this user from MySQL." => "Ezabatu erabiltzaile hau MySQLtik.", +"Oracle connection could not be established" => "Ezin da Oracle konexioa sortu", "Oracle username and/or password not valid" => "Oracle erabiltzaile edota pasahitza ez dira egokiak.", "Offending command was: \"%s\", name: %s, password: %s" => "Errorea komando honek sortu du: \"%s\", izena: %s, pasahitza: %s", "PostgreSQL username and/or password not valid" => "PostgreSQL erabiltzaile edota pasahitza ez dira egokiak.", @@ -48,5 +52,6 @@ "%d months ago" => "orain dela %d hilabete", "last year" => "joan den urtean", "years ago" => "urte", +"Caused by:" => "Honek eraginda:", "Could not find category \"%s\"" => "Ezin da \"%s\" kategoria aurkitu" ); diff --git a/lib/l10n/gl.php b/lib/l10n/gl.php index 351f18c7155..f5309d95081 100644 --- a/lib/l10n/gl.php +++ b/lib/l10n/gl.php @@ -5,11 +5,14 @@ "Users" => "Usuarios", "Apps" => "Aplicativos", "Admin" => "Administración", +"Failed to upgrade \"%s\"." => "Non foi posíbel anovar «%s».", "web services under your control" => "servizos web baixo o seu control", +"cannot open \"%s\"" => "non foi posíbel abrir «%s»", "ZIP download is turned off." => "As descargas ZIP están desactivadas.", "Files need to be downloaded one by one." => "Os ficheiros necesitan seren descargados dun en un.", "Back to Files" => "Volver aos ficheiros", "Selected files too large to generate zip file." => "Os ficheiros seleccionados son demasiado grandes como para xerar un ficheiro zip.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Descargue os ficheiros en cachos máis pequenos e por separado, ou pídallos amabelmente ao seu administrador.", "couldn't be determined" => "non foi posíbel determinalo", "Application is not enabled" => "O aplicativo non está activado", "Authentication error" => "Produciuse un erro de autenticación", @@ -49,5 +52,6 @@ "%d months ago" => "Vai %d meses", "last year" => "último ano", "years ago" => "anos atrás", +"Caused by:" => "Causado por:", "Could not find category \"%s\"" => "Non foi posíbel atopar a categoría «%s»" ); diff --git a/lib/l10n/hu_HU.php b/lib/l10n/hu_HU.php index 3aa04274fa3..7738dcc05c8 100644 --- a/lib/l10n/hu_HU.php +++ b/lib/l10n/hu_HU.php @@ -5,11 +5,14 @@ "Users" => "Felhasználók", "Apps" => "Alkalmazások", "Admin" => "Adminsztráció", +"Failed to upgrade \"%s\"." => "Sikertelen Frissítés \"%s\".", "web services under your control" => "webszolgáltatások saját kézben", +"cannot open \"%s\"" => "nem sikerült megnyitni \"%s\"", "ZIP download is turned off." => "A ZIP-letöltés nincs engedélyezve.", "Files need to be downloaded one by one." => "A fájlokat egyenként kell letölteni.", "Back to Files" => "Vissza a Fájlokhoz", "Selected files too large to generate zip file." => "A kiválasztott fájlok túl nagyok a zip tömörítéshez.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Tölts le a fileokat kisebb chunkokban, kölün vagy kérj segitséget a rendszergazdádtól.", "couldn't be determined" => "nem határozható meg", "Application is not enabled" => "Az alkalmazás nincs engedélyezve", "Authentication error" => "Azonosítási hiba", @@ -49,5 +52,6 @@ "%d months ago" => "%d hónapja", "last year" => "tavaly", "years ago" => "több éve", +"Caused by:" => "Okozta:", "Could not find category \"%s\"" => "Ez a kategória nem található: \"%s\"" ); diff --git a/lib/l10n/it.php b/lib/l10n/it.php index 74483315ca0..693f86b6b80 100644 --- a/lib/l10n/it.php +++ b/lib/l10n/it.php @@ -5,11 +5,14 @@ "Users" => "Utenti", "Apps" => "Applicazioni", "Admin" => "Admin", +"Failed to upgrade \"%s\"." => "Aggiornamento non riuscito \"%s\".", "web services under your control" => "servizi web nelle tue mani", +"cannot open \"%s\"" => "impossibile aprire \"%s\"", "ZIP download is turned off." => "Lo scaricamento in formato ZIP è stato disabilitato.", "Files need to be downloaded one by one." => "I file devono essere scaricati uno alla volta.", "Back to Files" => "Torna ai file", "Selected files too large to generate zip file." => "I file selezionati sono troppo grandi per generare un file zip.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Scarica i file in blocchi più piccoli, separatamente o chiedi al tuo amministratore.", "couldn't be determined" => "non può essere determinato", "Application is not enabled" => "L'applicazione non è abilitata", "Authentication error" => "Errore di autenticazione", @@ -49,5 +52,6 @@ "%d months ago" => "%d mesi fa", "last year" => "anno scorso", "years ago" => "anni fa", +"Caused by:" => "Causato da:", "Could not find category \"%s\"" => "Impossibile trovare la categoria \"%s\"" ); diff --git a/lib/l10n/ko.php b/lib/l10n/ko.php index 31245ea96f2..1f32ebe402a 100644 --- a/lib/l10n/ko.php +++ b/lib/l10n/ko.php @@ -17,6 +17,13 @@ "Files" => "파일", "Text" => "텍스트", "Images" => "그림", +"%s enter the database username." => "데이터베이스 사용자 명을 %s 에 입력해주십시오", +"%s enter the database name." => "데이터베이스 명을 %s 에 입력해주십시오", +"%s you may not use dots in the database name" => "%s 에 적으신 데이터베이스 이름에는 점을 사용할수 없습니다", +"DB Error: \"%s\"" => "DB 오류: \"%s\"", +"PostgreSQL username and/or password not valid" => "PostgreSQL의 사용자 명 혹은 비밀번호가 잘못되었습니다", +"Set an admin username." => "관리자 이름 설정", +"Set an admin password." => "관리자 비밀번호 설정", "Your web server is not yet properly setup to allow files synchronization because the WebDAV interface seems to be broken." => "WebDAV 인터페이스가 제대로 작동하지 않습니다. 웹 서버에서 파일 동기화를 사용할 수 있도록 설정이 제대로 되지 않은 것 같습니다.", "Please double check the <a href='%s'>installation guides</a>." => "<a href='%s'>설치 가이드</a>를 다시 한 번 확인하십시오.", "seconds ago" => "초 전", diff --git a/lib/l10n/nl.php b/lib/l10n/nl.php index de80d1b5d56..0a221b11f00 100644 --- a/lib/l10n/nl.php +++ b/lib/l10n/nl.php @@ -5,11 +5,14 @@ "Users" => "Gebruikers", "Apps" => "Apps", "Admin" => "Beheerder", +"Failed to upgrade \"%s\"." => "Upgrade \"%s\" mislukt.", "web services under your control" => "Webdiensten in eigen beheer", +"cannot open \"%s\"" => "Kon \"%s\" niet openen", "ZIP download is turned off." => "ZIP download is uitgeschakeld.", "Files need to be downloaded one by one." => "Bestanden moeten één voor één worden gedownload.", "Back to Files" => "Terug naar bestanden", "Selected files too large to generate zip file." => "De geselecteerde bestanden zijn te groot om een zip bestand te maken.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Download de bestanden in kleinere brokken, appart of vraag uw administrator.", "couldn't be determined" => "kon niet worden vastgesteld", "Application is not enabled" => "De applicatie is niet actief", "Authentication error" => "Authenticatie fout", @@ -49,5 +52,6 @@ "%d months ago" => "%d maanden geleden", "last year" => "vorig jaar", "years ago" => "jaar geleden", +"Caused by:" => "Gekomen door:", "Could not find category \"%s\"" => "Kon categorie \"%s\" niet vinden" ); diff --git a/lib/l10n/pt_BR.php b/lib/l10n/pt_BR.php index 029331fdec8..23385e8c7d1 100644 --- a/lib/l10n/pt_BR.php +++ b/lib/l10n/pt_BR.php @@ -5,11 +5,14 @@ "Users" => "Usuários", "Apps" => "Aplicações", "Admin" => "Admin", +"Failed to upgrade \"%s\"." => "Falha na atualização de \"%s\".", "web services under your control" => "serviços web sob seu controle", +"cannot open \"%s\"" => "não pode abrir \"%s\"", "ZIP download is turned off." => "Download ZIP está desligado.", "Files need to be downloaded one by one." => "Arquivos precisam ser baixados um de cada vez.", "Back to Files" => "Voltar para Arquivos", "Selected files too large to generate zip file." => "Arquivos selecionados são muito grandes para gerar arquivo zip.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Baixe os arquivos em pedaços menores, separadamente ou solicite educadamente ao seu administrador.", "couldn't be determined" => "não pôde ser determinado", "Application is not enabled" => "Aplicação não está habilitada", "Authentication error" => "Erro de autenticação", @@ -49,5 +52,6 @@ "%d months ago" => "%d meses atrás", "last year" => "último ano", "years ago" => "anos atrás", +"Caused by:" => "Causados por:", "Could not find category \"%s\"" => "Impossível localizar categoria \"%s\"" ); diff --git a/lib/l10n/pt_PT.php b/lib/l10n/pt_PT.php index 7480026e920..96cb5f65f56 100644 --- a/lib/l10n/pt_PT.php +++ b/lib/l10n/pt_PT.php @@ -5,11 +5,14 @@ "Users" => "Utilizadores", "Apps" => "Aplicações", "Admin" => "Admin", +"Failed to upgrade \"%s\"." => "A actualização \"%s\" falhou.", "web services under your control" => "serviços web sob o seu controlo", +"cannot open \"%s\"" => "Não foi possível abrir \"%s\"", "ZIP download is turned off." => "Descarregamento em ZIP está desligado.", "Files need to be downloaded one by one." => "Os ficheiros precisam de ser descarregados um por um.", "Back to Files" => "Voltar a Ficheiros", "Selected files too large to generate zip file." => "Os ficheiros seleccionados são grandes demais para gerar um ficheiro zip.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Descarregue os ficheiros em partes menores, separados ou peça gentilmente ao seu administrador.", "couldn't be determined" => "Não foi possível determinar", "Application is not enabled" => "A aplicação não está activada", "Authentication error" => "Erro na autenticação", @@ -49,5 +52,6 @@ "%d months ago" => "Há %d meses atrás", "last year" => "ano passado", "years ago" => "anos atrás", +"Caused by:" => "Causado por:", "Could not find category \"%s\"" => "Não foi encontrado a categoria \"%s\"" ); diff --git a/lib/l10n/sv.php b/lib/l10n/sv.php index 56776e574aa..0e2e806b4f6 100644 --- a/lib/l10n/sv.php +++ b/lib/l10n/sv.php @@ -5,11 +5,14 @@ "Users" => "Användare", "Apps" => "Program", "Admin" => "Admin", +"Failed to upgrade \"%s\"." => "Misslyckades med att uppgradera \"%s\".", "web services under your control" => "webbtjänster under din kontroll", +"cannot open \"%s\"" => "Kan inte öppna \"%s\"", "ZIP download is turned off." => "Nerladdning av ZIP är avstängd.", "Files need to be downloaded one by one." => "Filer laddas ner en åt gången.", "Back to Files" => "Tillbaka till Filer", "Selected files too large to generate zip file." => "Valda filer är för stora för att skapa zip-fil.", +"Download the files in smaller chunks, seperately or kindly ask your administrator." => "Ladda ner filerna i mindre bitar, separat eller fråga din administratör.", "couldn't be determined" => "kunde inte bestämmas", "Application is not enabled" => "Applikationen är inte aktiverad", "Authentication error" => "Fel vid autentisering", @@ -49,5 +52,6 @@ "%d months ago" => "%d månader sedan", "last year" => "förra året", "years ago" => "år sedan", +"Caused by:" => "Orsakad av:", "Could not find category \"%s\"" => "Kunde inte hitta kategorin \"%s\"" ); diff --git a/lib/memcache/apcu.php b/lib/memcache/apcu.php new file mode 100644 index 00000000000..ccc1aa6e562 --- /dev/null +++ b/lib/memcache/apcu.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Memcache; + +class APCu extends APC { + public function clear($prefix = '') { + $ns = $this->getNamespace() . $prefix; + $ns = preg_quote($ns, '/'); + $iter = new \APCIterator('/^'.$ns.'/'); + return apc_delete($iter); + } + + static public function isAvailable() { + if (!extension_loaded('apcu')) { + return false; + } elseif (!ini_get('apc.enable_cli') && \OC::$CLI) { + return false; + } else { + return true; + } + } +} diff --git a/lib/memcache/factory.php b/lib/memcache/factory.php index b1b49971031..4c1b1ab207f 100644 --- a/lib/memcache/factory.php +++ b/lib/memcache/factory.php @@ -18,6 +18,8 @@ class Factory { function create($prefix = '') { if (XCache::isAvailable()) { return new XCache($prefix); + } elseif (APCu::isAvailable()) { + return new APCu($prefix); } elseif (APC::isAvailable()) { return new APC($prefix); } elseif (Memcached::isAvailable()) { @@ -33,6 +35,6 @@ class Factory { * @return bool */ public function isAvailable() { - return XCache::isAvailable() || APC::isAvailable() || Memcached::isAvailable(); + return XCache::isAvailable() || APCu::isAvailable() || APC::isAvailable() || Memcached::isAvailable(); } } diff --git a/lib/public/files.php b/lib/public/files.php index 4975bbb7dfa..852b041eefb 100644 --- a/lib/public/files.php +++ b/lib/public/files.php @@ -61,7 +61,7 @@ class Files { * @param string $query * @return array */ - public function searchByMime($mimetype) { + static public function searchByMime($mimetype) { return(\OC\Files\Filesystem::searchByMime( $mimetype )); } diff --git a/lib/public/image.php b/lib/public/image.php new file mode 100644 index 00000000000..c6dd9a113ab --- /dev/null +++ b/lib/public/image.php @@ -0,0 +1,29 @@ +<?php +/** +* ownCloud +* +* @author Bart Visscher +* @copyright 2013 Bart Visscher <bartv@thisnet.nl> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +namespace OCP; + +/** + * This class provides functions to handle images + */ +class Image extends \OC_Image { +} diff --git a/lib/public/template.php b/lib/public/template.php index ccf19cf052c..ab1089c332d 100644 --- a/lib/public/template.php +++ b/lib/public/template.php @@ -77,12 +77,13 @@ function relative_modified_date($timestamp) { /** - * @brief Return a human readable outout for a file size. + * @brief DEPRECATED Return a human readable outout for a file size. + * @deprecated human_file_size() instead * @param $byte size of a file in byte * @returns human readable interpretation of a file size */ function simple_file_size($bytes) { - return(\simple_file_size($bytes)); + return(\human_file_size($bytes)); } diff --git a/lib/public/user.php b/lib/public/user.php index 9edebe0e7cf..23ff991642d 100644 --- a/lib/public/user.php +++ b/lib/public/user.php @@ -40,7 +40,7 @@ class User { * @return string uid or false */ public static function getUser() { - return \OC_USER::getUser(); + return \OC_User::getUser(); } /** @@ -50,7 +50,7 @@ class User { * Get a list of all users. */ public static function getUsers($search = '', $limit = null, $offset = null) { - return \OC_USER::getUsers(); + return \OC_User::getUsers($search, $limit, $offset); } /** @@ -58,7 +58,7 @@ class User { * @return string display name */ public static function getDisplayName($user=null) { - return \OC_USER::getDisplayName($user); + return \OC_User::getDisplayName($user); } /** @@ -68,7 +68,7 @@ class User { * Get a list of all display names and user ids. */ public static function getDisplayNames($search = '', $limit = null, $offset = null) { - return \OC_USER::getDisplayNames($search, $limit, $offset); + return \OC_User::getDisplayNames($search, $limit, $offset); } /** @@ -78,7 +78,7 @@ class User { * Checks if the user is logged in */ public static function isLoggedIn() { - return \OC_USER::isLoggedIn(); + return \OC_User::isLoggedIn(); } /** @@ -88,14 +88,14 @@ class User { * @return boolean */ public static function userExists( $uid, $excludingBackend = null ) { - return \OC_USER::userExists( $uid, $excludingBackend ); + return \OC_User::userExists( $uid, $excludingBackend ); } /** * @brief Loggs the user out including all the session data * Logout, destroys session */ public static function logout() { - \OC_USER::logout(); + \OC_User::logout(); } /** @@ -107,7 +107,7 @@ class User { * Check if the password is correct without logging in the user */ public static function checkPassword( $uid, $password ) { - return \OC_USER::checkPassword( $uid, $password ); + return \OC_User::checkPassword( $uid, $password ); } /** diff --git a/lib/public/util.php b/lib/public/util.php index d69602f4507..693805946ea 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -78,6 +78,15 @@ class Util { } /** + * @brief get l10n object + * @param string $app + * @return OC_L10N + */ + public static function getL10N( $application ) { + return \OC_L10N::get( $application ); + } + + /** * @brief add a css file * @param string $url */ diff --git a/lib/setup.php b/lib/setup.php index 59f4cab75de..05a49890976 100644 --- a/lib/setup.php +++ b/lib/setup.php @@ -185,9 +185,7 @@ class OC_Setup { $hint = $l->t('Please double check the <a href=\'%s\'>installation guides</a>.', 'http://doc.owncloud.org/server/5.0/admin_manual/installation.html'); - $tmpl = new OC_Template('', 'error', 'guest'); - $tmpl->assign('errors', array(1 => array('error' => $error, 'hint' => $hint))); - $tmpl->printPage(); + OC_Template::printErrorPage($error, $hint); exit(); } } diff --git a/lib/template.php b/lib/template.php index ae9ea187445..9b2c1211e61 100644 --- a/lib/template.php +++ b/lib/template.php @@ -21,152 +21,20 @@ * */ -/** - * Prints an XSS escaped string - * @param string $string the string which will be escaped and printed - */ -function p($string) { - print(OC_Util::sanitizeHTML($string)); -} - -/** - * Prints an unescaped string - * @param string $string the string which will be printed as it is - */ -function print_unescaped($string) { - print($string); -} - -/** - * @brief make OC_Helper::linkTo available as a simple function - * @param string $app app - * @param string $file file - * @param array $args array with param=>value, will be appended to the returned url - * @return string link to the file - * - * For further information have a look at OC_Helper::linkTo - */ -function link_to( $app, $file, $args = array() ) { - return OC_Helper::linkTo( $app, $file, $args ); -} +require_once __DIR__.'/template/functions.php'; /** - * @brief make OC_Helper::imagePath available as a simple function - * @param string $app app - * @param string $image image - * @return string link to the image - * - * For further information have a look at OC_Helper::imagePath + * This class provides the templates for ownCloud. */ -function image_path( $app, $image ) { - return OC_Helper::imagePath( $app, $image ); -} - -/** - * @brief make OC_Helper::mimetypeIcon available as a simple function - * @param string $mimetype mimetype - * @return string link to the image - * - * For further information have a look at OC_Helper::mimetypeIcon - */ -function mimetype_icon( $mimetype ) { - return OC_Helper::mimetypeIcon( $mimetype ); -} - -/** - * @brief make OC_Helper::humanFileSize available as a simple function - * @param int $bytes size in bytes - * @return string size as string - * - * For further information have a look at OC_Helper::humanFileSize - */ -function human_file_size( $bytes ) { - return OC_Helper::humanFileSize( $bytes ); -} - -function simple_file_size($bytes) { - if ($bytes < 0) { - return '?'; - } - $mbytes = round($bytes / (1024 * 1024), 1); - if ($bytes == 0) { - return '0'; - } - if ($mbytes < 0.1) { - return '< 0.1'; - } - if ($mbytes > 1000) { - return '> 1000'; - } else { - return number_format($mbytes, 1); - } -} - -function relative_modified_date($timestamp) { - $l=OC_L10N::get('lib'); - $timediff = time() - $timestamp; - $diffminutes = round($timediff/60); - $diffhours = round($diffminutes/60); - $diffdays = round($diffhours/24); - $diffmonths = round($diffdays/31); - - if($timediff < 60) { return $l->t('seconds ago'); } - else if($timediff < 120) { return $l->t('1 minute ago'); } - else if($timediff < 3600) { return $l->t('%d minutes ago', $diffminutes); } - else if($timediff < 7200) { return $l->t('1 hour ago'); } - else if($timediff < 86400) { return $l->t('%d hours ago', $diffhours); } - else if((date('G')-$diffhours) > 0) { return $l->t('today'); } - else if((date('G')-$diffhours) > -24) { return $l->t('yesterday'); } - else if($timediff < 2678400) { return $l->t('%d days ago', $diffdays); } - else if($timediff < 5184000) { return $l->t('last month'); } - else if((date('n')-$diffmonths) > 0) { return $l->t('%d months ago', $diffmonths); } - else if($timediff < 63113852) { return $l->t('last year'); } - else { return $l->t('years ago'); } -} - -function html_select_options($options, $selected, $params=array()) { - if (!is_array($selected)) { - $selected=array($selected); - } - if (isset($params['combine']) && $params['combine']) { - $options = array_combine($options, $options); - } - $value_name = $label_name = false; - if (isset($params['value'])) { - $value_name = $params['value']; - } - if (isset($params['label'])) { - $label_name = $params['label']; - } - $html = ''; - foreach($options as $value => $label) { - if ($value_name && is_array($label)) { - $value = $label[$value_name]; - } - if ($label_name && is_array($label)) { - $label = $label[$label_name]; - } - $select = in_array($value, $selected) ? ' selected="selected"' : ''; - $html .= '<option value="' . OC_Util::sanitizeHTML($value) . '"' . $select . '>' . OC_Util::sanitizeHTML($label) . '</option>'."\n"; - } - return $html; -} - -/** - * This class provides the templates for owncloud. - */ -class OC_Template{ +class OC_Template extends \OC\Template\Base { private $renderas; // Create a full page? - private $application; // template Application - private $vars; // Vars - private $template; // The path to the template - private $l10n; // The l10n-Object + private $path; // The path to the template private $headers=array(); //custom headers /** * @brief Constructor * @param string $app app providing the template - * @param string $file name of the template file (without suffix) + * @param string $name of the template file (without suffix) * @param string $renderas = ""; produce a full page * @return OC_Template object * @@ -177,13 +45,25 @@ class OC_Template{ * "admin". */ public function __construct( $app, $name, $renderas = "" ) { + // Read the selected theme from the config file + $theme = OC_Util::getTheme(); + + // Read the detected formfactor and use the right file name. + $fext = self::getFormFactorExtension(); + + $requesttoken = OC::$session ? OC_Util::callRegister() : ''; + + $parts = explode('/', $app); // fix translation when app is something like core/lostpassword + $l10n = OC_L10N::get($parts[0]); + $themeDefaults = new OC_Defaults(); + + list($path, $template) = $this->findTemplate($theme, $app, $name, $fext); + // Set the private data $this->renderas = $renderas; - $this->application = $app; - $this->vars = array(); - $this->vars['requesttoken'] = OC_Util::callRegister(); - $parts = explode('/', $app); // fix translation when app is something like core/lostpassword - $this->l10n = OC_L10N::get($parts[0]); + $this->path = $path; + + parent::__construct($template, $requesttoken, $l10n, $themeDefaults); // Some headers to enhance security header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters @@ -207,7 +87,6 @@ class OC_Template{ .'media-src *'); header('Content-Security-Policy:'.$policy); // Standard - $this->findTemplate($name); } /** @@ -243,6 +122,9 @@ class OC_Template{ */ static public function getFormFactorExtension() { + if (!\OC::$session) { + return ''; + } // if the formfactor is not yet autodetected do the // autodetection now. For possible formfactors check the // detectFormfactor documentation @@ -254,13 +136,13 @@ class OC_Template{ \OC::$session->set('formfactor', $_GET['formfactor']); } $formfactor = \OC::$session->get('formfactor'); - if($formfactor=='default') { + if($formfactor==='default') { $fext=''; - }elseif($formfactor=='mobile') { + }elseif($formfactor==='mobile') { $fext='.mobile'; - }elseif($formfactor=='tablet') { + }elseif($formfactor==='tablet') { $fext='.tablet'; - }elseif($formfactor=='standalone') { + }elseif($formfactor==='standalone') { $fext='.standalone'; }else{ $fext=''; @@ -275,112 +157,23 @@ class OC_Template{ * Will select the template file for the selected theme and formfactor. * Checking all the possible locations. */ - protected function findTemplate($name) - { - // Read the selected theme from the config file - $theme = OC_Util::getTheme(); - - // Read the detected formfactor and use the right file name. - $fext = self::getFormFactorExtension(); - - $app = $this->application; + protected function findTemplate($theme, $app, $name, $fext) { // Check if it is a app template or not. - if( $app != "" ) { - // Check if the app is in the app folder or in the root - if( file_exists(OC_App::getAppPath($app)."/templates/" )) { - // Check if the template is overwritten by the selected theme - if ($this->checkPathForTemplate(OC::$SERVERROOT."/themes/$theme/apps/$app/templates/", $name, $fext)) { - }elseif ($this->checkPathForTemplate(OC_App::getAppPath($app)."/templates/", $name, $fext)) { - } - }else{ - // Check if the template is overwritten by the selected theme - if ($this->checkPathForTemplate(OC::$SERVERROOT."/themes/$theme/$app/templates/", $name, $fext)) { - }elseif ($this->checkPathForTemplate(OC::$SERVERROOT."/$app/templates/", $name, $fext)) { - }else{ - echo('template not found: template:'.$name.' formfactor:'.$fext - .' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); - die(); - } - - } - }else{ - // Check if the template is overwritten by the selected theme - if ($this->checkPathForTemplate(OC::$SERVERROOT."/themes/$theme/core/templates/", $name, $fext)) { - } elseif ($this->checkPathForTemplate(OC::$SERVERROOT."/core/templates/", $name, $fext)) { - }else{ - echo('template not found: template:'.$name.' formfactor:'.$fext - .' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); - die(); - } - } - } - - /** - * @brief check Path For Template with and without $fext - * @param string $path to check - * @param string $name of the template file (without suffix) - * @param string $fext formfactor extension - * @return bool true when found - * - * Will set $this->template and $this->path if there is a template at - * the specific $path - */ - protected function checkPathForTemplate($path, $name, $fext) - { - if ($name =='') return false; - $template = null; - if( is_file( $path.$name.$fext.'.php' )) { - $template = $path.$name.$fext.'.php'; - }elseif( is_file( $path.$name.'.php' )) { - $template = $path.$name.'.php'; - } - if ($template) { - $this->template = $template; - $this->path = $path; - return true; - } - return false; - } - - /** - * @brief Assign variables - * @param string $key key - * @param string $value value - * @return bool - * - * This function assigns a variable. It can be accessed via $_[$key] in - * the template. - * - * If the key existed before, it will be overwritten - */ - public function assign( $key, $value) { - $this->vars[$key] = $value; - return true; - } - - /** - * @brief Appends a variable - * @param string $key key - * @param string $value value - * @return bool - * - * This function assigns a variable in an array context. If the key already - * exists, the value will be appended. It can be accessed via - * $_[$key][$position] in the template. - */ - public function append( $key, $value ) { - if( array_key_exists( $key, $this->vars )) { - $this->vars[$key][] = $value; - } - else{ - $this->vars[$key] = array( $value ); + if( $app !== '' ) { + $dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app)); + } else { + $dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT); } + $locator = new \OC\Template\TemplateFileLocator( $fext, $dirs ); + $template = $locator->find($name); + $path = $locator->getPath(); + return array($path, $template); } /** * @brief Add a custom element to the header * @param string $tag tag name of the element - * @param array $attributes array of attrobutes for the element + * @param array $attributes array of attributes for the element * @param string $text the text content for the element */ public function addHeader( $tag, $attributes, $text='') { @@ -388,31 +181,14 @@ class OC_Template{ } /** - * @brief Prints the proceeded template - * @return bool - * - * This function proceeds the template and prints its output. - */ - public function printPage() { - $data = $this->fetchPage(); - if( $data === false ) { - return false; - } - else{ - print $data; - return true; - } - } - - /** - * @brief Proceeds the template + * @brief Process the template * @return bool * - * This function proceeds the template. If $this->renderas is set, it + * This function process the template. If $this->renderas is set, it * will produce a full page. */ public function fetchPage() { - $data = $this->_fetch(); + $data = parent::fetchPage(); if( $this->renderas ) { $page = new OC_TemplateLayout($this->renderas); @@ -432,27 +208,6 @@ class OC_Template{ } /** - * @brief doing the actual work - * @return string content - * - * Includes the template file, fetches its output - */ - private function _fetch() { - // Register the variables - $_ = $this->vars; - $l = $this->l10n; - - // Execute the template - ob_start(); - include $this->template; // <-- we have to use include because we pass $_! - $data = ob_get_contents(); - @ob_end_clean(); - - // return the data - return $data; - } - - /** * @brief Include template * @return string returns content of included template * @@ -460,21 +215,7 @@ class OC_Template{ * do this. */ public function inc( $file, $additionalparams = null ) { - $_ = $this->vars; - $l = $this->l10n; - - if( !is_null($additionalparams)) { - $_ = array_merge( $additionalparams, $this->vars ); - } - - // Include - ob_start(); - include $this->path.$file.'.php'; - $data = ob_get_contents(); - @ob_end_clean(); - - // Return data - return $data; + return $this->load($this->path.$file.'.php', $additionalparams); } /** @@ -524,8 +265,8 @@ class OC_Template{ /** * @brief Print a fatal error page and terminates the script - * @param string $error The error message to show - * @param string $hint An optional hint message + * @param string $error_msg The error message to show + * @param string $hint An optional hint message * Warning: All data passed to $hint needs to get sanitized using OC_Util::sanitizeHTML */ public static function printErrorPage( $error_msg, $hint = '' ) { @@ -546,14 +287,25 @@ class OC_Template{ if ($exception->getCode()) { $error_msg = '['.$exception->getCode().'] '.$error_msg; } - $hint = $exception->getTraceAsString(); - while (method_exists($exception,'previous') && $exception = $exception->previous()) { - $error_msg .= '<br/>Caused by: '; - if ($exception->getCode()) { - $error_msg .= '['.$exception->getCode().'] '; + if (defined('DEBUG') and DEBUG) { + $hint = $exception->getTraceAsString(); + if (!empty($hint)) { + $hint = '<pre>'.$hint.'</pre>'; } - $error_msg .= $exception->getMessage(); - }; + $l = OC_L10N::get('lib'); + while (method_exists($exception, 'previous') && $exception = $exception->previous()) { + $error_msg .= '<br/>'.$l->t('Caused by:').' '; + if ($exception->getCode()) { + $error_msg .= '['.$exception->getCode().'] '; + } + $error_msg .= $exception->getMessage(); + }; + } else { + $hint = ''; + if ($exception instanceof \OC\HintException) { + $hint = $exception->getHint(); + } + } self::printErrorPage($error_msg, $hint); } } diff --git a/lib/template/base.php b/lib/template/base.php new file mode 100644 index 00000000000..88941bc7132 --- /dev/null +++ b/lib/template/base.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Template; + +class Base { + private $template; // The template + private $vars; // Vars + private $l10n; // The l10n-Object + private $theme; // theme defaults + + public function __construct( $template, $requesttoken, $l10n, $theme ) { + $this->vars = array(); + $this->vars['requesttoken'] = $requesttoken; + $this->l10n = $l10n; + $this->template = $template; + $this->theme = $theme; + } + + protected function getAppTemplateDirs($theme, $app, $serverroot, $app_dir) { + // Check if the app is in the app folder or in the root + if( file_exists($app_dir.'/templates/' )) { + return array( + $serverroot.'/themes/'.$theme.'/apps/'.$app.'/templates/', + $app_dir.'/templates/', + ); + } + return array( + $serverroot.'/themes/'.$theme.'/'.$app.'/templates/', + $serverroot.'/'.$app.'/templates/', + ); + } + + protected function getCoreTemplateDirs($theme, $serverroot) { + return array( + $serverroot.'/themes/'.$theme.'/core/templates/', + $serverroot.'/core/templates/', + ); + } + + /** + * @brief Assign variables + * @param string $key key + * @param string $value value + * @return bool + * + * This function assigns a variable. It can be accessed via $_[$key] in + * the template. + * + * If the key existed before, it will be overwritten + */ + public function assign( $key, $value) { + $this->vars[$key] = $value; + return true; + } + + /** + * @brief Appends a variable + * @param string $key key + * @param string $value value + * @return bool + * + * This function assigns a variable in an array context. If the key already + * exists, the value will be appended. It can be accessed via + * $_[$key][$position] in the template. + */ + public function append( $key, $value ) { + if( array_key_exists( $key, $this->vars )) { + $this->vars[$key][] = $value; + } + else{ + $this->vars[$key] = array( $value ); + } + } + + /** + * @brief Prints the proceeded template + * @return bool + * + * This function proceeds the template and prints its output. + */ + public function printPage() { + $data = $this->fetchPage(); + if( $data === false ) { + return false; + } + else{ + print $data; + return true; + } + } + + /** + * @brief Process the template + * @return bool + * + * This function processes the template. + */ + public function fetchPage() { + return $this->load($this->template); + } + + /** + * @brief doing the actual work + * @return string content + * + * Includes the template file, fetches its output + */ + protected function load( $file, $additionalparams = null ) { + // Register the variables + $_ = $this->vars; + $l = $this->l10n; + $theme = $this->theme; + + if( !is_null($additionalparams)) { + $_ = array_merge( $additionalparams, $this->vars ); + } + + // Include + ob_start(); + include $file; + $data = ob_get_contents(); + @ob_end_clean(); + + // Return data + return $data; + } + +} diff --git a/lib/template/cssresourcelocator.php b/lib/template/cssresourcelocator.php new file mode 100644 index 00000000000..8e7831ca549 --- /dev/null +++ b/lib/template/cssresourcelocator.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Template; + +class CSSResourceLocator extends ResourceLocator { + public function doFind( $style ) { + if (strpos($style, '3rdparty') === 0 + && $this->appendIfExist($this->thirdpartyroot, $style.'.css') + || $this->appendIfExist($this->serverroot, $style.$this->form_factor.'.css') + || $this->appendIfExist($this->serverroot, $style.'.css') + || $this->appendIfExist($this->serverroot, 'core/'.$style.$this->form_factor.'.css') + || $this->appendIfExist($this->serverroot, 'core/'.$style.'.css') + ) { + return; + } + $app = substr($style, 0, strpos($style, '/')); + $style = substr($style, strpos($style, '/')+1); + $app_path = \OC_App::getAppPath($app); + $app_url = $this->webroot . '/index.php/apps/' . $app; + if ($this->appendIfExist($app_path, $style.$this->form_factor.'.css', $app_url) + || $this->appendIfExist($app_path, $style.'.css', $app_url) + ) { + return; + } + throw new \Exception('css file not found: style:'.$style); + } + + public function doFindTheme( $style ) { + $theme_dir = 'themes/'.$this->theme.'/'; + $this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$style.$this->form_factor.'.css') + || $this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$style.'.css') + || $this->appendIfExist($this->serverroot, $theme_dir.$style.$this->form_factor.'.css') + || $this->appendIfExist($this->serverroot, $theme_dir.$style.'.css') + || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$style.$this->form_factor.'.css') + || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$style.'.css'); + } +} diff --git a/lib/template/functions.php b/lib/template/functions.php new file mode 100644 index 00000000000..2d43cae1c0c --- /dev/null +++ b/lib/template/functions.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +/** + * Prints an XSS escaped string + * @param string $string the string which will be escaped and printed + */ +function p($string) { + print(OC_Util::sanitizeHTML($string)); +} + +/** + * Prints an unescaped string + * @param string $string the string which will be printed as it is + */ +function print_unescaped($string) { + print($string); +} + +/** + * @brief make OC_Helper::linkTo available as a simple function + * @param string $app app + * @param string $file file + * @param array $args array with param=>value, will be appended to the returned url + * @return string link to the file + * + * For further information have a look at OC_Helper::linkTo + */ +function link_to( $app, $file, $args = array() ) { + return OC_Helper::linkTo( $app, $file, $args ); +} + +/** + * @brief make OC_Helper::imagePath available as a simple function + * @param string $app app + * @param string $image image + * @return string link to the image + * + * For further information have a look at OC_Helper::imagePath + */ +function image_path( $app, $image ) { + return OC_Helper::imagePath( $app, $image ); +} + +/** + * @brief make OC_Helper::mimetypeIcon available as a simple function + * @param string $mimetype mimetype + * @return string link to the image + * + * For further information have a look at OC_Helper::mimetypeIcon + */ +function mimetype_icon( $mimetype ) { + return OC_Helper::mimetypeIcon( $mimetype ); +} + +/** + * @brief make OC_Helper::humanFileSize available as a simple function + * @param int $bytes size in bytes + * @return string size as string + * + * For further information have a look at OC_Helper::humanFileSize + */ +function human_file_size( $bytes ) { + return OC_Helper::humanFileSize( $bytes ); +} + +function relative_modified_date($timestamp) { + $l=OC_L10N::get('lib'); + $timediff = time() - $timestamp; + $diffminutes = round($timediff/60); + $diffhours = round($diffminutes/60); + $diffdays = round($diffhours/24); + $diffmonths = round($diffdays/31); + + if($timediff < 60) { return $l->t('seconds ago'); } + else if($timediff < 120) { return $l->t('1 minute ago'); } + else if($timediff < 3600) { return $l->t('%d minutes ago', $diffminutes); } + else if($timediff < 7200) { return $l->t('1 hour ago'); } + else if($timediff < 86400) { return $l->t('%d hours ago', $diffhours); } + else if((date('G')-$diffhours) > 0) { return $l->t('today'); } + else if((date('G')-$diffhours) > -24) { return $l->t('yesterday'); } + else if($timediff < 2678400) { return $l->t('%d days ago', $diffdays); } + else if($timediff < 5184000) { return $l->t('last month'); } + else if((date('n')-$diffmonths) > 0) { return $l->t('%d months ago', $diffmonths); } + else if($timediff < 63113852) { return $l->t('last year'); } + else { return $l->t('years ago'); } +} + +function html_select_options($options, $selected, $params=array()) { + if (!is_array($selected)) { + $selected=array($selected); + } + if (isset($params['combine']) && $params['combine']) { + $options = array_combine($options, $options); + } + $value_name = $label_name = false; + if (isset($params['value'])) { + $value_name = $params['value']; + } + if (isset($params['label'])) { + $label_name = $params['label']; + } + $html = ''; + foreach($options as $value => $label) { + if ($value_name && is_array($label)) { + $value = $label[$value_name]; + } + if ($label_name && is_array($label)) { + $label = $label[$label_name]; + } + $select = in_array($value, $selected) ? ' selected="selected"' : ''; + $html .= '<option value="' . OC_Util::sanitizeHTML($value) . '"' . $select . '>' . OC_Util::sanitizeHTML($label) . '</option>'."\n"; + } + return $html; +} diff --git a/lib/template/jsresourcelocator.php b/lib/template/jsresourcelocator.php new file mode 100644 index 00000000000..f8fe3817ce6 --- /dev/null +++ b/lib/template/jsresourcelocator.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Template; + +class JSResourceLocator extends ResourceLocator { + public function doFind( $script ) { + $theme_dir = 'themes/'.$this->theme.'/'; + if (strpos($script, '3rdparty') === 0 + && $this->appendIfExist($this->thirdpartyroot, $script.'.js') + || $this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$script.$this->form_factor.'.js') + || $this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$script.'.js') + || $this->appendIfExist($this->serverroot, $theme_dir.$script.$this->form_factor.'.js') + || $this->appendIfExist($this->serverroot, $theme_dir.$script.'.js') + || $this->appendIfExist($this->serverroot, $script.$this->form_factor.'.js') + || $this->appendIfExist($this->serverroot, $script.'.js') + || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$script.$this->form_factor.'.js') + || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$script.'.js') + || $this->appendIfExist($this->serverroot, 'core/'.$script.$this->form_factor.'.js') + || $this->appendIfExist($this->serverroot, 'core/'.$script.'.js') + ) { + return; + } + $app = substr($script, 0, strpos($script, '/')); + $script = substr($script, strpos($script, '/')+1); + $app_path = \OC_App::getAppPath($app); + $app_url = \OC_App::getAppWebPath($app); + if ($this->appendIfExist($app_path, $script.$this->form_factor.'.js', $app_url) + || $this->appendIfExist($app_path, $script.'.js', $app_url) + ) { + return; + } + throw new \Exception('js file not found: script:'.$script); + } + + public function doFindTheme( $script ) { + } +} diff --git a/lib/template/resourcelocator.php b/lib/template/resourcelocator.php new file mode 100644 index 00000000000..9f83673664d --- /dev/null +++ b/lib/template/resourcelocator.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Template; + +abstract class ResourceLocator { + protected $theme; + protected $form_factor; + + protected $mapping; + protected $serverroot; + protected $thirdpartyroot; + protected $webroot; + + protected $resources = array(); + + public function __construct( $theme, $form_factor, $core_map, $party_map ) { + $this->theme = $theme; + $this->form_factor = $form_factor; + $this->mapping = $core_map + $party_map; + $this->serverroot = key($core_map); + $this->thirdpartyroot = key($party_map); + $this->webroot = $this->mapping[$this->serverroot]; + } + + abstract public function doFind( $resource ); + abstract public function doFindTheme( $resource ); + + public function find( $resources ) { + try { + foreach($resources as $resource) { + $this->doFind($resource); + } + if (!empty($this->theme)) { + foreach($resources as $resource) { + $this->doFindTheme($resource); + } + } + } catch (\Exception $e) { + throw new \Exception($e->getMessage().' formfactor:'.$this->form_factor + .' serverroot:'.$this->serverroot); + } + } + + /* + * @brief append the $file resource if exist at $root + * @param $root path to check + * @param $file the filename + * @param $web base for path, default map $root to $webroot + */ + protected function appendIfExist($root, $file, $webroot = null) { + if (is_file($root.'/'.$file)) { + if (!$webroot) { + $webroot = $this->mapping[$root]; + } + $this->resources[] = array($root, $webroot, $file); + return true; + } + return false; + } + + public function getResources() { + return $this->resources; + } +} diff --git a/lib/template/templatefilelocator.php b/lib/template/templatefilelocator.php new file mode 100644 index 00000000000..d5a484b1a14 --- /dev/null +++ b/lib/template/templatefilelocator.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Template; + +class TemplateFileLocator { + protected $form_factor; + protected $dirs; + private $path; + + public function __construct( $form_factor, $dirs ) { + $this->form_factor = $form_factor; + $this->dirs = $dirs; + } + + public function find( $template ) { + if ($template === '') { + throw new \InvalidArgumentException('Empty template name'); + } + + foreach($this->dirs as $dir) { + $file = $dir.$template.$this->form_factor.'.php'; + if (is_file($file)) { + $this->path = $dir; + return $file; + } + $file = $dir.$template.'.php'; + if (is_file($file)) { + $this->path = $dir; + return $file; + } + } + throw new \Exception('template file not found: template:'.$template.' formfactor:'.$this->form_factor); + } + + public function getPath() { + return $this->path; + } +} diff --git a/lib/templatelayout.php b/lib/templatelayout.php index 7115b8f0306..0024c9d4960 100644 --- a/lib/templatelayout.php +++ b/lib/templatelayout.php @@ -83,21 +83,6 @@ class OC_TemplateLayout extends OC_Template { } } - /* - * @brief append the $file-url if exist at $root - * @param $files array to append file info to - * @param $root path to check - * @param $web base for path - * @param $file the filename - */ - static public function appendIfExist(&$files, $root, $webroot, $file) { - if (is_file($root.'/'.$file)) { - $files[] = array($root, $webroot, $file); - return true; - } - return false; - } - static public function findStylesheetFiles($styles) { // Read the selected theme from the config file $theme = OC_Util::getTheme(); @@ -105,51 +90,11 @@ class OC_TemplateLayout extends OC_Template { // Read the detected formfactor and use the right file name. $fext = self::getFormFactorExtension(); - $files = array(); - foreach($styles as $style) { - // is it in 3rdparty? - if(strpos($style, '3rdparty') === 0 && - self::appendIfExist($files, OC::$THIRDPARTYROOT, OC::$THIRDPARTYWEBROOT, $style.'.css')) { - - // or in the owncloud root? - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "$style$fext.css" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "$style.css" )) { - - // or in core ? - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "core/$style$fext.css" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "core/$style.css" )) { - - }else{ - $app = substr($style, 0, strpos($style, '/')); - $style = substr($style, strpos($style, '/')+1); - $app_path = OC_App::getAppPath($app); - $app_url = OC::$WEBROOT . '/index.php/apps/' . $app; - if(self::appendIfExist($files, $app_path, $app_url, "$style$fext.css")) { - } - elseif(self::appendIfExist($files, $app_path, $app_url, "$style.css")) { - } - else { - echo('css file not found: style:'.$style.' formfactor:'.$fext - .' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); - die(); - } - } - } - // Add the theme css files. you can override the default values here - if(!empty($theme)) { - foreach($styles as $style) { - if(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$style$fext.css" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$style.css" )) { - - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$style$fext.css" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$style.css" )) { - - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$style$fext.css" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$style.css" )) { - } - } - } - return $files; + $locator = new \OC\Template\CSSResourceLocator( $theme, $fext, + array( OC::$SERVERROOT => OC::$WEBROOT ), + array( OC::$THIRDPARTYROOT => OC::$THIRDPARTYWEBROOT )); + $locator->find($styles); + return $locator->getResources(); } static public function findJavascriptFiles($scripts) { @@ -159,49 +104,10 @@ class OC_TemplateLayout extends OC_Template { // Read the detected formfactor and use the right file name. $fext = self::getFormFactorExtension(); - $files = array(); - foreach($scripts as $script) { - // Is it in 3rd party? - if(strpos($script, '3rdparty') === 0 && - self::appendIfExist($files, OC::$THIRDPARTYROOT, OC::$THIRDPARTYWEBROOT, $script.'.js')) { - - // Is it in apps and overwritten by the theme? - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$script$fext.js" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$script.js" )) { - - // Is it in the owncloud root but overwritten by the theme? - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$script$fext.js" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$script.js" )) { - - // Is it in the owncloud root ? - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "$script$fext.js" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "$script.js" )) { - - // Is in core but overwritten by a theme? - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$script$fext.js" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$script.js" )) { - - // Is it in core? - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "core/$script$fext.js" )) { - }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "core/$script.js" )) { - - }else{ - // Is it part of an app? - $app = substr($script, 0, strpos($script, '/')); - $script = substr($script, strpos($script, '/')+1); - $app_path = OC_App::getAppPath($app); - $app_url = OC_App::getAppWebPath($app); - if(self::appendIfExist($files, $app_path, $app_url, "$script$fext.js")) { - } - elseif(self::appendIfExist($files, $app_path, $app_url, "$script.js")) { - } - else { - echo('js file not found: script:'.$script.' formfactor:'.$fext - .' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); - die(); - } - } - } - return $files; + $locator = new \OC\Template\JSResourceLocator( $theme, $fext, + array( OC::$SERVERROOT => OC::$WEBROOT ), + array( OC::$THIRDPARTYROOT => OC::$THIRDPARTYWEBROOT )); + $locator->find($scripts); + return $locator->getResources(); } } diff --git a/lib/user.php b/lib/user.php index 830f13bb8df..ee20f2e0971 100644 --- a/lib/user.php +++ b/lib/user.php @@ -39,7 +39,7 @@ class OC_User { public static $userSession = null; - private static function getUserSession() { + public static function getUserSession() { if (!self::$userSession) { $manager = new \OC\User\Manager(); self::$userSession = new \OC\User\Session($manager, \OC::$session); @@ -83,7 +83,7 @@ class OC_User { /** * @return \OC\User\Manager */ - private static function getManager() { + public static function getManager() { return self::getUserSession()->getManager(); } @@ -316,7 +316,7 @@ class OC_User { * @return string uid or false */ public static function getUser() { - $uid = OC::$session->get('user_id'); + $uid = OC::$session ? OC::$session->get('user_id') : null; if (!is_null($uid)) { return $uid; } else { diff --git a/lib/user/manager.php b/lib/user/manager.php index d17cdf1a200..8dc9bfe2729 100644 --- a/lib/user/manager.php +++ b/lib/user/manager.php @@ -30,6 +30,9 @@ class Manager extends PublicEmitter { */ private $backends = array(); + /** + * @var \OC\User\User[] $cachedUsers + */ private $cachedUsers = array(); public function __construct() { diff --git a/lib/user/user.php b/lib/user/user.php index 55d7848a979..8115c43198c 100644 --- a/lib/user/user.php +++ b/lib/user/user.php @@ -44,7 +44,7 @@ class User { */ public function __construct($uid, $backend, $emitter = null) { $this->uid = $uid; - if ($backend->implementsActions(OC_USER_BACKEND_GET_DISPLAYNAME)) { + if ($backend and $backend->implementsActions(OC_USER_BACKEND_GET_DISPLAYNAME)) { $this->displayName = $backend->getDisplayName($uid); } else { $this->displayName = $uid; diff --git a/lib/util.php b/lib/util.php index 7f7b9f334b4..b7dc2207e6c 100755 --- a/lib/util.php +++ b/lib/util.php @@ -1,7 +1,5 @@ <?php -require_once 'Patchwork/PHP/Shim/Normalizer.php'; - /** * Class for utility functions * @@ -873,6 +871,10 @@ class OC_Util { if (function_exists('xcache_clear_cache')) { xcache_clear_cache(XC_TYPE_VAR, 0); } + // Opcache (PHP >= 5.5) + if (function_exists('opcache_reset')) { + opcache_reset(); + } } /** |