diff options
author | Roeland Jago Douma <rullzer@users.noreply.github.com> | 2019-11-18 15:29:49 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-18 15:29:49 +0100 |
commit | 5320f08cd2eab3926104a314fb76b2de222d1d7b (patch) | |
tree | dc2965325422b4fedc6f5a765004063eb8ebf61e | |
parent | 497737f28f8ed176496c2ed4997ecb7d83eac2e8 (diff) | |
parent | 8800a7e89097a4994b99d8a8fc5023c53c06946e (diff) | |
download | nextcloud-server-5320f08cd2eab3926104a314fb76b2de222d1d7b.tar.gz nextcloud-server-5320f08cd2eab3926104a314fb76b2de222d1d7b.zip |
Merge pull request #17765 from nextcloud/filecache-extension
Upload time and Creation time
23 files changed, 869 insertions, 454 deletions
diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index 2d019b46b6a..3ce305d75f3 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -303,6 +303,19 @@ class File extends Node implements IFile { } } + $fileInfoUpdate = [ + 'upload_time' => time() + ]; + + // allow sync clients to send the creation time along in a header + if (isset($this->request->server['HTTP_X_OC_CTIME'])) { + $ctime = $this->sanitizeMtime($this->request->server['HTTP_X_OC_CTIME']); + $fileInfoUpdate['creation_time'] = $ctime; + $this->header('X-OC-CTime: accepted'); + } + + $this->fileView->putFileInfo($this->path, $fileInfoUpdate); + if ($view) { $this->emitPostHooks($exists); } diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index 99317f2bc1c..b2a0e9a31b4 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -70,6 +70,9 @@ class FilesPlugin extends ServerPlugin { const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview'; const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type'; const IS_ENCRYPTED_PROPERTYNAME = '{http://nextcloud.org/ns}is-encrypted'; + const METADATA_ETAG_PROPERTYNAME = '{http://nextcloud.org/ns}metadata_etag'; + const UPLOAD_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}upload_time'; + const CREATION_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}creation_time'; const SHARE_NOTE = '{http://nextcloud.org/ns}note'; /** @@ -400,6 +403,14 @@ class FilesPlugin extends ServerPlugin { return new ChecksumList($checksum); }); + $propFind->handle(self::CREATION_TIME_PROPERTYNAME, function() use ($node) { + return $node->getFileInfo()->getCreationTime(); + }); + + $propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function() use ($node) { + return $node->getFileInfo()->getUploadTime(); + }); + } if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) { @@ -470,6 +481,13 @@ class FilesPlugin extends ServerPlugin { } return false; }); + $propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function($time) use ($node) { + if (empty($time)) { + return false; + } + $node->setCreationTime((int) $time); + return true; + }); } /** diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php index f0917fe11b2..2a3e8145f6f 100644 --- a/apps/dav/lib/Connector/Sabre/Node.php +++ b/apps/dav/lib/Connector/Sabre/Node.php @@ -201,6 +201,14 @@ abstract class Node implements \Sabre\DAV\INode { return $this->fileView->putFileInfo($this->path, array('etag' => $etag)); } + public function setCreationTime(int $time) { + return $this->fileView->putFileInfo($this->path, array('creation_time' => $time)); + } + + public function setUploadTime(int $time) { + return $this->fileView->putFileInfo($this->path, array('upload_time' => $time)); + } + /** * Returns the size of the node, in bytes * diff --git a/apps/files_trashbin/lib/Trash/TrashItem.php b/apps/files_trashbin/lib/Trash/TrashItem.php index 0112b6dbc8e..71830d8b91e 100644 --- a/apps/files_trashbin/lib/Trash/TrashItem.php +++ b/apps/files_trashbin/lib/Trash/TrashItem.php @@ -177,4 +177,12 @@ class TrashItem implements ITrashItem { public function getTitle(): string { return $this->getOriginalLocation(); } + + public function getCreationTime(): int { + return $this->fileInfo->getCreationTime(); + } + + public function getUploadTime(): int { + return $this->fileInfo->getUploadTime(); + } } diff --git a/core/Migrations/Version17000Date20190514105811.php b/core/Migrations/Version17000Date20190514105811.php index 7f8084f9832..1cf4c9f5a37 100644 --- a/core/Migrations/Version17000Date20190514105811.php +++ b/core/Migrations/Version17000Date20190514105811.php @@ -44,7 +44,7 @@ class Version17000Date20190514105811 extends SimpleMigrationStep { $schema = $schemaClosure(); if(!$schema->hasTable('filecache_extended')) { $table = $schema->createTable('filecache_extended'); - $table->addColumn('fileid', Type::INTEGER, [ + $table->addColumn('fileid', Type::BIGINT, [ 'notnull' => true, 'length' => 4, 'unsigned' => true, diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 915efa90609..cf28f634cab 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -866,6 +866,7 @@ return array( 'OC\\Files\\Cache\\AbstractCacheEvent' => $baseDir . '/lib/private/Files/Cache/AbstractCacheEvent.php', 'OC\\Files\\Cache\\Cache' => $baseDir . '/lib/private/Files/Cache/Cache.php', 'OC\\Files\\Cache\\CacheEntry' => $baseDir . '/lib/private/Files/Cache/CacheEntry.php', + 'OC\\Files\\Cache\\CacheQueryBuilder' => $baseDir . '/lib/private/Files/Cache/CacheQueryBuilder.php', 'OC\\Files\\Cache\\FailedCache' => $baseDir . '/lib/private/Files/Cache/FailedCache.php', 'OC\\Files\\Cache\\HomeCache' => $baseDir . '/lib/private/Files/Cache/HomeCache.php', 'OC\\Files\\Cache\\HomePropagator' => $baseDir . '/lib/private/Files/Cache/HomePropagator.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 22a070774f8..aa12c20ac13 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -895,6 +895,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Files\\Cache\\AbstractCacheEvent' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/AbstractCacheEvent.php', 'OC\\Files\\Cache\\Cache' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Cache.php', 'OC\\Files\\Cache\\CacheEntry' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/CacheEntry.php', + 'OC\\Files\\Cache\\CacheQueryBuilder' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/CacheQueryBuilder.php', 'OC\\Files\\Cache\\FailedCache' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/FailedCache.php', 'OC\\Files\\Cache\\HomeCache' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/HomeCache.php', 'OC\\Files\\Cache\\HomePropagator' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/HomePropagator.php', diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index 3b67661c8b0..46bb536dfd2 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -72,9 +72,10 @@ class FunctionBuilder implements IFunctionBuilder { return new QueryFunction($this->helper->quoteColumnName($x) . ' - ' . $this->helper->quoteColumnName($y)); } - public function count($count, $alias = '') { + public function count($count = '', $alias = '') { $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; - return new QueryFunction('COUNT(' . $this->helper->quoteColumnName($count) . ')' . $alias); + $quotedName = $count === '' ? '*' : $this->helper->quoteColumnName($count); + return new QueryFunction('COUNT(' . $quotedName . ')' . $alias); } public function max($field) { diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 25d59fb7d7d..bff38c0c8d6 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -245,7 +245,7 @@ class QueryBuilder implements IQueryBuilder { * @param mixed $value The parameter value. * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setParameter($key, $value, $type = null) { $this->queryBuilder->setParameter($key, $value, $type); @@ -270,7 +270,7 @@ class QueryBuilder implements IQueryBuilder { * @param array $params The query parameters to set. * @param array $types The query parameters types to set. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setParameters(array $params, array $types = array()) { $this->queryBuilder->setParameters($params, $types); @@ -323,7 +323,7 @@ class QueryBuilder implements IQueryBuilder { * * @param integer $firstResult The first result to return. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setFirstResult($firstResult) { $this->queryBuilder->setFirstResult($firstResult); @@ -350,7 +350,7 @@ class QueryBuilder implements IQueryBuilder { * * @param integer $maxResults The maximum number of results to retrieve. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setMaxResults($maxResults) { $this->queryBuilder->setMaxResults($maxResults); @@ -381,7 +381,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$selects The selection expressions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * '@return $this This QueryBuilder instance. */ public function select(...$selects) { if (count($selects) === 1 && is_array($selects[0])) { @@ -408,7 +408,7 @@ class QueryBuilder implements IQueryBuilder { * @param mixed $select The selection expressions. * @param string $alias The column alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function selectAlias($select, $alias) { @@ -430,7 +430,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed $select The selection expressions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function selectDistinct($select) { @@ -454,7 +454,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$selects The selection expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function addSelect(...$selects) { if (count($selects) === 1 && is_array($selects[0])) { @@ -482,7 +482,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $delete The table whose rows are subject to the deletion. * @param string $alias The table alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function delete($delete = null, $alias = null) { $this->queryBuilder->delete( @@ -507,7 +507,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $update The table whose rows are subject to the update. * @param string $alias The table alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function update($update = null, $alias = null) { $this->queryBuilder->update( @@ -535,7 +535,7 @@ class QueryBuilder implements IQueryBuilder { * * @param string $insert The table into which the rows should be inserted. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function insert($insert = null) { $this->queryBuilder->insert( @@ -560,7 +560,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $from The table. * @param string|null $alias The alias of the table. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function from($from, $alias = null) { $this->queryBuilder->from( @@ -586,7 +586,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function join($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->join( @@ -614,7 +614,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function innerJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->innerJoin( @@ -642,7 +642,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function leftJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->leftJoin( @@ -670,7 +670,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function rightJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->rightJoin( @@ -696,7 +696,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $key The column to set. * @param string $value The value, expression, placeholder, etc. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function set($key, $value) { $this->queryBuilder->set( @@ -731,7 +731,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$predicates The restriction predicates. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function where(...$predicates) { call_user_func_array( @@ -756,7 +756,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$where The query restrictions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * * @see where() */ @@ -783,7 +783,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$where The WHERE statement. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * * @see where() */ @@ -809,7 +809,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$groupBys The grouping expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function groupBy(...$groupBys) { if (count($groupBys) === 1 && is_array($groupBys[0])) { @@ -837,7 +837,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$groupBy The grouping expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function addGroupBy(...$groupBys) { if (count($groupBys) === 1 && is_array($groupBys[0])) { @@ -869,7 +869,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $column The column into which the value should be inserted. * @param string $value The value that should be inserted into the column. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setValue($column, $value) { $this->queryBuilder->setValue( @@ -897,7 +897,7 @@ class QueryBuilder implements IQueryBuilder { * * @param array $values The values to specify for the insert query indexed by column names. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function values(array $values) { $quotedValues = []; @@ -916,7 +916,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$having The restriction over the groups. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function having(...$having) { call_user_func_array( @@ -933,7 +933,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$having The restriction to append. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function andHaving(...$having) { call_user_func_array( @@ -950,7 +950,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$having The restriction to add. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function orHaving(...$having) { call_user_func_array( @@ -968,7 +968,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $sort The ordering expression. * @param string $order The ordering direction. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function orderBy($sort, $order = null) { $this->queryBuilder->orderBy( @@ -985,7 +985,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $sort The ordering expression. * @param string $order The ordering direction. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function addOrderBy($sort, $order = null) { $this->queryBuilder->addOrderBy( @@ -1021,7 +1021,7 @@ class QueryBuilder implements IQueryBuilder { * * @param array|null $queryPartNames * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function resetQueryParts($queryPartNames = null) { $this->queryBuilder->resetQueryParts($queryPartNames); @@ -1034,7 +1034,7 @@ class QueryBuilder implements IQueryBuilder { * * @param string $queryPartName * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function resetQueryPart($queryPartName) { $this->queryBuilder->resetQueryPart($queryPartName); diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index f6139d8abed..f1d2c64e8fd 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -45,6 +45,7 @@ use OCP\Files\Cache\CacheInsertEvent; use OCP\Files\Cache\CacheUpdateEvent; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\FileInfo; use \OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchQuery; use OCP\Files\Storage\IStorage; @@ -68,7 +69,7 @@ class Cache implements ICache { /** * @var array partial data for the cache */ - protected $partial = array(); + protected $partial = []; /** * @var string @@ -112,6 +113,15 @@ class Cache implements ICache { $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader); } + private function getQueryBuilder() { + return new CacheQueryBuilder( + $this->connection, + \OC::$server->getSystemConfig(), + \OC::$server->getLogger(), + $this + ); + } + /** * Get the numeric storage id for this cache's storage * @@ -128,34 +138,24 @@ class Cache implements ICache { * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache */ public function get($file) { + $query = $this->getQueryBuilder(); + $query->selectFileCache(); + if (is_string($file) or $file == '') { // normalize file $file = $this->normalize($file); - $where = 'WHERE `storage` = ? AND `path_hash` = ?'; - $params = array($this->getNumericStorageId(), md5($file)); + $query->whereStorageId() + ->wherePath($file); } else { //file id - $where = 'WHERE `fileid` = ?'; - $params = array($file); + $query->whereFileId($file); } - $sql = 'SELECT `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, - `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum` - FROM `*PREFIX*filecache` ' . $where; - $result = $this->connection->executeQuery($sql, $params); - $data = $result->fetch(); - //FIXME hide this HACK in the next database layer, or just use doctrine and get rid of MDB2 and PDO - //PDO returns false, MDB2 returns null, oracle always uses MDB2, so convert null to false - if ($data === null) { - $data = false; - } + $data = $query->execute()->fetch(); //merge partial data - if (!$data and is_string($file)) { - if (isset($this->partial[$file])) { - $data = $this->partial[$file]; - } - return $data; + if (!$data and is_string($file) and isset($this->partial[$file])) { + return $this->partial[$file]; } else if (!$data) { return $data; } else { @@ -187,6 +187,12 @@ class Cache implements ICache { $data['storage_mtime'] = $data['mtime']; } $data['permissions'] = (int)$data['permissions']; + if (isset($data['creation_time'])) { + $data['creation_time'] = (int) $data['creation_time']; + } + if (isset($data['upload_time'])) { + $data['upload_time'] = (int) $data['upload_time']; + } return new CacheEntry($data); } @@ -209,11 +215,12 @@ class Cache implements ICache { */ public function getFolderContentsById($fileId) { if ($fileId > -1) { - $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, - `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum` - FROM `*PREFIX*filecache` WHERE `parent` = ? ORDER BY `name` ASC'; - $result = $this->connection->executeQuery($sql, [$fileId]); - $files = $result->fetchAll(); + $query = $this->getQueryBuilder(); + $query->selectFileCache() + ->whereParent($fileId) + ->orderBy('name', 'ASC'); + + $files = $query->execute()->fetchAll(); return array_map(function (array $data) { return self::cacheEntryFromData($data, $this->mimetypeLoader); }, $files); @@ -259,7 +266,7 @@ class Cache implements ICache { unset($this->partial[$file]); } - $requiredFields = array('size', 'mtime', 'mimetype'); + $requiredFields = ['size', 'mtime', 'mimetype']; foreach ($requiredFields as $field) { if (!isset($data[$field])) { //data not complete save as partial and return $this->partial[$file] = $data; @@ -271,14 +278,8 @@ class Cache implements ICache { $data['parent'] = $this->getParentId($file); $data['name'] = basename($file); - list($queryParts, $params) = $this->buildParts($data); - $queryParts[] = '`storage`'; - $params[] = $this->getNumericStorageId(); - - $queryParts = array_map(function ($item) { - return trim($item, "`"); - }, $queryParts); - $values = array_combine($queryParts, $params); + [$values, $extensionValues] = $this->normalizeData($data); + $values['storage'] = $this->getNumericStorageId(); try { $builder = $this->connection->getQueryBuilder(); @@ -289,7 +290,19 @@ class Cache implements ICache { } if ($builder->execute()) { - $fileId = (int)$this->connection->lastInsertId('*PREFIX*filecache'); + $fileId = $builder->getLastInsertId(); + + if (count($extensionValues)) { + $query = $this->getQueryBuilder(); + $query->insert('filecache_extended'); + + $query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); + foreach ($extensionValues as $column => $value) { + $query->setValue($column, $query->createNamedParameter($value)); + } + $query->execute(); + } + $this->eventDispatcher->dispatch(CacheInsertEvent::class, new CacheInsertEvent($this->storage, $file, $fileId)); return $fileId; } @@ -324,20 +337,56 @@ class Cache implements ICache { $data['name'] = $this->normalize($data['name']); } - list($queryParts, $params) = $this->buildParts($data); - // duplicate $params because we need the parts twice in the SQL statement - // once for the SET part, once in the WHERE clause - $params = array_merge($params, $params); - $params[] = $id; + [$values, $extensionValues] = $this->normalizeData($data); - // don't update if the data we try to set is the same as the one in the record - // some databases (Postgres) don't like superfluous updates - $sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' . - 'WHERE (' . - implode(' <> ? OR ', $queryParts) . ' <> ? OR ' . - implode(' IS NULL OR ', $queryParts) . ' IS NULL' . - ') AND `fileid` = ? '; - $this->connection->executeQuery($sql, $params); + if (count($values)) { + $query = $this->getQueryBuilder(); + + $query->update('filecache') + ->whereFileId($id) + ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) { + return $query->expr()->orX( + $query->expr()->neq($key, $query->createNamedParameter($value)), + $query->expr()->isNull($key) + ); + }, array_keys($values), array_values($values)))); + + foreach ($values as $key => $value) { + $query->set($key, $query->createNamedParameter($value)); + } + + $query->execute(); + } + + if (count($extensionValues)) { + try { + $query = $this->getQueryBuilder(); + $query->insert('filecache_extended'); + + $query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)); + foreach ($extensionValues as $column => $value) { + $query->setValue($column, $query->createNamedParameter($value)); + } + + $query->execute(); + } catch (UniqueConstraintViolationException $e) { + $query = $this->getQueryBuilder(); + $query->update('filecache_extended') + ->whereFileId($id) + ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) { + return $query->expr()->orX( + $query->expr()->neq($key, $query->createNamedParameter($value)), + $query->expr()->isNull($key) + ); + }, array_keys($extensionValues), array_values($extensionValues)))); + + foreach ($extensionValues as $key => $value) { + $query->set($key, $query->createNamedParameter($value)); + } + + $query->execute(); + } + } $path = $this->getPathById($id); // path can still be null if the file doesn't exist @@ -350,14 +399,13 @@ class Cache implements ICache { * extract query parts and params array from data array * * @param array $data - * @return array [$queryParts, $params] - * $queryParts: string[], the (escaped) column names to be set in the query - * $params: mixed[], the new values for the columns, to be passed as params to the query + * @return array */ - protected function buildParts(array $data) { - $fields = array( + protected function normalizeData(array $data): array { + $fields = [ 'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', - 'etag', 'permissions', 'checksum', 'storage'); + 'etag', 'permissions', 'checksum', 'storage']; + $extensionFields = ['metadata_etag', 'creation_time', 'upload_time']; $doNotCopyStorageMTime = false; if (array_key_exists('mtime', $data) && $data['mtime'] === null) { @@ -366,23 +414,20 @@ class Cache implements ICache { $doNotCopyStorageMTime = true; } - $params = array(); - $queryParts = array(); + $params = []; + $extensionParams = []; foreach ($data as $name => $value) { if (array_search($name, $fields) !== false) { if ($name === 'path') { - $params[] = md5($value); - $queryParts[] = '`path_hash`'; - } elseif ($name === 'mimetype') { - $params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/'))); - $queryParts[] = '`mimepart`'; + $params['path_hash'] = md5($value); + } else if ($name === 'mimetype') { + $params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/'))); $value = $this->mimetypeLoader->getId($value); - } elseif ($name === 'storage_mtime') { + } else if ($name === 'storage_mtime') { if (!$doNotCopyStorageMTime && !isset($data['mtime'])) { - $params[] = $value; - $queryParts[] = '`mtime`'; + $params['mtime'] = $value; } - } elseif ($name === 'encrypted') { + } else if ($name === 'encrypted') { if (isset($data['encryptedVersion'])) { $value = $data['encryptedVersion']; } else { @@ -390,11 +435,13 @@ class Cache implements ICache { $value = $value ? 1 : 0; } } - $params[] = $value; - $queryParts[] = '`' . $name . '`'; + $params[$name] = $value; + } + if (array_search($name, $extensionFields) !== false) { + $extensionParams[$name] = $value; } } - return array($queryParts, $params); + return [$params, array_filter($extensionParams)]; } /** @@ -411,15 +458,14 @@ class Cache implements ICache { // normalize file $file = $this->normalize($file); - $pathHash = md5($file); + $query = $this->getQueryBuilder(); + $query->select('fileid') + ->from('filecache') + ->whereStorageId() + ->wherePath($file); - $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'; - $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash)); - if ($row = $result->fetch()) { - return (int)$row['fileid']; - } else { - return -1; - } + $id = $query->execute()->fetchColumn(); + return $id === false ? -1 : (int)$id; } /** @@ -464,39 +510,64 @@ class Cache implements ICache { */ public function remove($file) { $entry = $this->get($file); - $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?'; - $this->connection->executeQuery($sql, array($entry['fileid'])); - if ($entry['mimetype'] === 'httpd/unix-directory') { - $this->removeChildren($entry); + + if ($entry) { + $query = $this->getQueryBuilder(); + $query->delete('filecache') + ->whereFileId($entry->getId()); + $query->execute(); + + $query = $this->getQueryBuilder(); + $query->delete('filecache_extended') + ->whereFileId($entry->getId()); + $query->execute(); + + if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) { + $this->removeChildren($entry); + } } } /** * Get all sub folders of a folder * - * @param array $entry the cache entry of the folder to get the subfolders for - * @return array[] the cache entries for the subfolders + * @param ICacheEntry $entry the cache entry of the folder to get the subfolders for + * @return ICacheEntry[] the cache entries for the subfolders */ - private function getSubFolders($entry) { - $children = $this->getFolderContentsById($entry['fileid']); + private function getSubFolders(ICacheEntry $entry) { + $children = $this->getFolderContentsById($entry->getId()); return array_filter($children, function ($child) { - return $child['mimetype'] === 'httpd/unix-directory'; + return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; }); } /** * Recursively remove all children of a folder * - * @param array $entry the cache entry of the folder to remove the children of + * @param ICacheEntry $entry the cache entry of the folder to remove the children of * @throws \OC\DatabaseException */ - private function removeChildren($entry) { - $subFolders = $this->getSubFolders($entry); - foreach ($subFolders as $folder) { + private function removeChildren(ICacheEntry $entry) { + $children = $this->getFolderContentsById($entry->getId()); + $childIds = array_map(function(ICacheEntry $cacheEntry) { + return $cacheEntry->getId(); + }, $children); + $childFolders = array_filter($children, function ($child) { + return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; + }); + foreach ($childFolders as $folder) { $this->removeChildren($folder); } - $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `parent` = ?'; - $this->connection->executeQuery($sql, array($entry['fileid'])); + + $query = $this->getQueryBuilder(); + $query->delete('filecache') + ->whereParent($entry->getId()); + $query->execute(); + + $query = $this->getQueryBuilder(); + $query->delete('filecache_extended') + ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY))); + $query->execute(); } /** @@ -575,8 +646,16 @@ class Cache implements ICache { } } - $sql = 'UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` = ? WHERE `fileid` = ?'; - $this->connection->executeQuery($sql, array($targetStorageId, $targetPath, md5($targetPath), basename($targetPath), $newParentId, $sourceId)); + $query = $this->getQueryBuilder(); + $query->update('filecache') + ->set('storage', $query->createNamedParameter($targetStorageId)) + ->set('path', $query->createNamedParameter($targetPath)) + ->set('path_hash', $query->createNamedParameter(md5($targetPath))) + ->set('name', $query->createNamedParameter(basename($targetPath))) + ->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT)) + ->whereFileId($sourceId); + $query->execute(); + $this->connection->commit(); } else { $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath); @@ -587,11 +666,15 @@ class Cache implements ICache { * remove all entries for files that are stored on the storage from the cache */ public function clear() { - $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?'; - $this->connection->executeQuery($sql, array($this->getNumericStorageId())); + $query = $this->getQueryBuilder(); + $query->delete('filecache') + ->whereStorageId(); + $query->execute(); - $sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?'; - $this->connection->executeQuery($sql, array($this->storageId)); + $query = $this->connection->getQueryBuilder(); + $query->delete('storages') + ->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId))); + $query->execute(); } /** @@ -610,11 +693,14 @@ class Cache implements ICache { // normalize file $file = $this->normalize($file); - $pathHash = md5($file); - $sql = 'SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'; - $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash)); - if ($row = $result->fetch()) { - if ((int)$row['size'] === -1) { + $query = $this->getQueryBuilder(); + $query->select('size') + ->from('filecache') + ->whereStorageId() + ->wherePath($file); + $size = $query->execute()->fetchColumn(); + if ($size !== false) { + if ((int)$size === -1) { return self::SHALLOW; } else { return self::COMPLETE; @@ -642,18 +728,14 @@ class Cache implements ICache { return []; } + $query = $this->getQueryBuilder(); + $query->selectFileCache() + ->whereStorageId() + ->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern))); - $sql = ' - SELECT `fileid`, `storage`, `path`, `parent`, `name`, - `mimetype`, `storage_mtime`, `mimepart`, `size`, `mtime`, - `encrypted`, `etag`, `permissions`, `checksum` - FROM `*PREFIX*filecache` - WHERE `storage` = ? AND `name` ILIKE ?'; - $result = $this->connection->executeQuery($sql, - [$this->getNumericStorageId(), $pattern] - ); - - return $this->searchResultToCacheEntries($result); + return array_map(function (array $data) { + return self::cacheEntryFromData($data, $this->mimetypeLoader); + }, $query->execute()->fetchAll()); } /** @@ -676,26 +758,29 @@ class Cache implements ICache { * @return ICacheEntry[] an array of cache entries where the mimetype matches the search */ public function searchByMime($mimetype) { + $mimeId = $this->mimetypeLoader->getId($mimetype); + + $query = $this->getQueryBuilder(); + $query->selectFileCache() + ->whereStorageId(); + if (strpos($mimetype, '/')) { - $where = '`mimetype` = ?'; + $query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT))); } else { - $where = '`mimepart` = ?'; + $query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT))); } - $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `storage_mtime`, `mtime`, `encrypted`, `etag`, `permissions`, `checksum` - FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?'; - $mimetype = $this->mimetypeLoader->getId($mimetype); - $result = $this->connection->executeQuery($sql, array($mimetype, $this->getNumericStorageId())); - return $this->searchResultToCacheEntries($result); + return array_map(function (array $data) { + return self::cacheEntryFromData($data, $this->mimetypeLoader); + }, $query->execute()->fetchAll()); } public function searchQuery(ISearchQuery $searchQuery) { - $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + $builder = $this->getQueryBuilder(); - $query = $builder->select(['fileid', 'storage', 'path', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum']) - ->from('filecache', 'file'); + $query = $builder->selectFileCache('file'); - $query->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->getNumericStorageId()))); + $query->whereStorageId(); if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) { $query @@ -755,10 +840,13 @@ class Cache implements ICache { */ public function getIncompleteChildrenCount($fileId) { if ($fileId > -1) { - $sql = 'SELECT count(*) - FROM `*PREFIX*filecache` WHERE `parent` = ? AND size = -1'; - $result = $this->connection->executeQuery($sql, [$fileId]); - return (int)$result->fetchColumn(); + $query = $this->getQueryBuilder(); + $query->select($query->func()->count()) + ->from('filecache') + ->whereParent($fileId) + ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))); + + return (int)$query->execute()->fetchColumn(); } return -1; } @@ -775,14 +863,17 @@ class Cache implements ICache { if (is_null($entry) or !isset($entry['fileid'])) { $entry = $this->get($path); } - if (isset($entry['mimetype']) && $entry['mimetype'] === 'httpd/unix-directory') { + if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) { $id = $entry['fileid']; - $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 ' . - 'FROM `*PREFIX*filecache` ' . - 'WHERE `parent` = ? AND `storage` = ?'; - $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId())); - if ($row = $result->fetch()) { - $result->closeCursor(); + + $query = $this->getQueryBuilder(); + $query->selectAlias($query->func()->sum('size'), 'f1') + ->selectAlias($query->func()->min('size'), 'f2') + ->from('filecache') + ->whereStorageId() + ->whereParent($id); + + if ($row = $query->execute()->fetch()) { list($sum, $min) = array_values($row); $sum = 0 + $sum; $min = 0 + $min; @@ -791,15 +882,9 @@ class Cache implements ICache { } else { $totalSize = $sum; } - $update = array(); if ($entry['size'] !== $totalSize) { - $update['size'] = $totalSize; + $this->update($id, ['size' => $totalSize]); } - if (count($update) > 0) { - $this->update($id, $update); - } - } else { - $result->closeCursor(); } } return $totalSize; @@ -811,13 +896,14 @@ class Cache implements ICache { * @return int[] */ public function getAll() { - $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?'; - $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId())); - $ids = array(); - while ($row = $result->fetch()) { - $ids[] = $row['fileid']; - } - return $ids; + $query = $this->getQueryBuilder(); + $query->select('fileid') + ->from('filecache') + ->whereStorageId(); + + return array_map(function ($id) { + return (int)$id; + }, $query->execute()->fetchAll(\PDO::FETCH_COLUMN)); } /** @@ -830,14 +916,14 @@ class Cache implements ICache { * @return string|bool the path of the folder or false when no folder matched */ public function getIncomplete() { - $query = $this->connection->prepare('SELECT `path` FROM `*PREFIX*filecache`' - . ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC', 1); - $query->execute([$this->getNumericStorageId()]); - if ($row = $query->fetch()) { - return $row['path']; - } else { - return false; - } + $query = $this->getQueryBuilder(); + $query->select('path') + ->from('filecache') + ->whereStorageId() + ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))) + ->orderBy('fileid', 'DESC'); + + return $query->execute()->fetchColumn(); } /** @@ -847,17 +933,14 @@ class Cache implements ICache { * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache */ public function getPathById($id) { - $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?'; - $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId())); - if ($row = $result->fetch()) { - // Oracle stores empty strings as null... - if ($row['path'] === null) { - return ''; - } - return $row['path']; - } else { - return null; - } + $query = $this->getQueryBuilder(); + $query->select('path') + ->from('filecache') + ->whereStorageId() + ->whereFileId($id); + + $path = $query->execute()->fetchColumn(); + return $path === false ? null : $path; } /** @@ -866,14 +949,15 @@ class Cache implements ICache { * instead does a global search in the cache table * * @param int $id - * @deprecated use getPathById() instead * @return array first element holding the storage id, second the path + * @deprecated use getPathById() instead */ static public function getById($id) { - $connection = \OC::$server->getDatabaseConnection(); - $sql = 'SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?'; - $result = $connection->executeQuery($sql, array($id)); - if ($row = $result->fetch()) { + $query = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + $query->select('path', 'storage') + ->from('filecache') + ->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); + if ($row = $query->execute()->fetch()) { $numericId = $row['storage']; $path = $row['path']; } else { @@ -881,7 +965,7 @@ class Cache implements ICache { } if ($id = Storage::getStorageId($numericId)) { - return array($id, $path); + return [$id, $path]; } else { return null; } diff --git a/lib/private/Files/Cache/CacheEntry.php b/lib/private/Files/Cache/CacheEntry.php index 4a2579a88f8..176a0bf27ed 100644 --- a/lib/private/Files/Cache/CacheEntry.php +++ b/lib/private/Files/Cache/CacheEntry.php @@ -109,6 +109,18 @@ class CacheEntry implements ICacheEntry, \ArrayAccess { return isset($this->data['encrypted']) && $this->data['encrypted']; } + public function getMetadataEtag(): ?string { + return $this->data['metadata_etag']; + } + + public function getCreationTime(): ?int { + return $this->data['creation_time']; + } + + public function getUploadTime(): ?int { + return $this->data['upload_time']; + } + public function getData() { return $this->data; } diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php new file mode 100644 index 00000000000..a5ff2129de8 --- /dev/null +++ b/lib/private/Files/Cache/CacheQueryBuilder.php @@ -0,0 +1,92 @@ +<?php declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Files\Cache; + +use OC\DB\QueryBuilder\QueryBuilder; +use OC\SystemConfig; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\ILogger; + +/** + * Query builder with commonly used helpers for filecache queries + */ +class CacheQueryBuilder extends QueryBuilder { + private $cache; + private $alias = null; + + public function __construct(IDBConnection $connection, SystemConfig $systemConfig, ILogger $logger, Cache $cache) { + parent::__construct($connection, $systemConfig, $logger); + + $this->cache = $cache; + } + + public function selectFileCache(string $alias = null) { + $name = $alias ? $alias : 'filecache'; + $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", 'name', 'mimetype', 'mimepart', 'size', 'mtime', + 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'metadata_etag', 'creation_time', 'upload_time') + ->from('filecache', $name) + ->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid')); + + $this->alias = $name; + + return $this; + } + + public function whereStorageId() { + $this->andWhere($this->expr()->eq('storage', $this->createNamedParameter($this->cache->getNumericStorageId(), IQueryBuilder::PARAM_INT))); + + return $this; + } + + public function whereFileId(int $fileId) { + $alias = $this->alias; + if ($alias) { + $alias .= '.'; + } else { + $alias = ''; + } + + $this->andWhere($this->expr()->eq("{$alias}fileid", $this->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); + + return $this; + } + + public function wherePath(string $path) { + $this->andWhere($this->expr()->eq('path_hash', $this->createNamedParameter(md5($path)))); + + return $this; + } + + public function whereParent(int $parent) { + $alias = $this->alias; + if ($alias) { + $alias .= '.'; + } else { + $alias = ''; + } + + $this->andWhere($this->expr()->eq("{$alias}parent", $this->createNamedParameter($parent, IQueryBuilder::PARAM_INT))); + + return $this; + } +} diff --git a/lib/private/Files/Cache/MoveFromCacheTrait.php b/lib/private/Files/Cache/MoveFromCacheTrait.php index a814a081dca..e9b41bd9b37 100644 --- a/lib/private/Files/Cache/MoveFromCacheTrait.php +++ b/lib/private/Files/Cache/MoveFromCacheTrait.php @@ -82,7 +82,10 @@ trait MoveFromCacheTrait { 'mimepart' => $entry->getMimePart(), 'etag' => $entry->getEtag(), 'permissions' => $entry->getPermissions(), - 'encrypted' => $entry->isEncrypted() + 'encrypted' => $entry->isEncrypted(), + 'creation_time' => $entry->getCreationTime(), + 'upload_time' => $entry->getUploadTime(), + 'metadata_etag' => $entry->getMetadataEtag(), ]; } } diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php index 19b95cd0355..93f876db17b 100644 --- a/lib/private/Files/FileInfo.php +++ b/lib/private/Files/FileInfo.php @@ -406,4 +406,12 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { public function getExtension(): string { return pathinfo($this->getName(), PATHINFO_EXTENSION); } + + public function getCreationTime(): int { + return (int) $this->data['creation_time']; + } + + public function getUploadTime(): int { + return (int) $this->data['upload_time']; + } } diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php index 01b4ca52765..f5d8cdc91a9 100644 --- a/lib/private/Files/Node/LazyRoot.php +++ b/lib/private/Files/Node/LazyRoot.php @@ -480,4 +480,18 @@ class LazyRoot implements IRootFolder { public function getRecent($limit, $offset = 0) { return $this->__call(__FUNCTION__, func_get_args()); } + + /** + * @inheritDoc + */ + public function getCreationTime(): int { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * @inheritDoc + */ + public function getUploadTime(): int { + return $this->__call(__FUNCTION__, func_get_args()); + } } diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index c440dd4a8f1..95d16cf5c99 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -444,4 +444,12 @@ class Node implements \OCP\Files\Node { } } + public function getCreationTime(): int { + return $this->getFileInfo()->getCreationTime(); + } + + public function getUploadTime(): int { + return $this->getFileInfo()->getUploadTime(); + } + } diff --git a/lib/public/DB/QueryBuilder/IFunctionBuilder.php b/lib/public/DB/QueryBuilder/IFunctionBuilder.php index 066be470150..861a576914a 100644 --- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php +++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php @@ -104,7 +104,7 @@ interface IFunctionBuilder { * @return IQueryFunction * @since 14.0.0 */ - public function count($count, $alias = ''); + public function count($count = '', $alias = ''); /** * Takes the maximum of all rows in a column diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 7fea31f4688..fe5182f21b9 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -180,7 +180,7 @@ interface IQueryBuilder { * @param mixed $value The parameter value. * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function setParameter($key, $value, $type = null); @@ -202,7 +202,7 @@ interface IQueryBuilder { * @param array $params The query parameters to set. * @param array $types The query parameters types to set. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function setParameters(array $params, array $types = array()); @@ -248,7 +248,7 @@ interface IQueryBuilder { * * @param integer $firstResult The first result to return. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function setFirstResult($firstResult); @@ -267,7 +267,7 @@ interface IQueryBuilder { * * @param integer $maxResults The maximum number of results to retrieve. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function setMaxResults($maxResults); @@ -294,7 +294,7 @@ interface IQueryBuilder { * * @param mixed ...$selects The selection expressions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function select(...$selects); @@ -312,7 +312,7 @@ interface IQueryBuilder { * @param mixed $select The selection expressions. * @param string $alias The column alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.1 */ public function selectAlias($select, $alias); @@ -328,7 +328,7 @@ interface IQueryBuilder { * * @param mixed $select The selection expressions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 9.0.0 */ public function selectDistinct($select); @@ -346,7 +346,7 @@ interface IQueryBuilder { * * @param mixed ...$select The selection expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function addSelect(...$select); @@ -365,7 +365,7 @@ interface IQueryBuilder { * @param string $delete The table whose rows are subject to the deletion. * @param string $alias The table alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function delete($delete = null, $alias = null); @@ -384,7 +384,7 @@ interface IQueryBuilder { * @param string $update The table whose rows are subject to the update. * @param string $alias The table alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function update($update = null, $alias = null); @@ -406,7 +406,7 @@ interface IQueryBuilder { * * @param string $insert The table into which the rows should be inserted. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function insert($insert = null); @@ -424,7 +424,7 @@ interface IQueryBuilder { * @param string $from The table. * @param string|null $alias The alias of the table. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function from($from, $alias = null); @@ -444,7 +444,7 @@ interface IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function join($fromAlias, $join, $alias, $condition = null); @@ -464,7 +464,7 @@ interface IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function innerJoin($fromAlias, $join, $alias, $condition = null); @@ -484,7 +484,7 @@ interface IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function leftJoin($fromAlias, $join, $alias, $condition = null); @@ -504,7 +504,7 @@ interface IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function rightJoin($fromAlias, $join, $alias, $condition = null); @@ -522,7 +522,7 @@ interface IQueryBuilder { * @param string $key The column to set. * @param string $value The value, expression, placeholder, etc. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function set($key, $value); @@ -551,7 +551,7 @@ interface IQueryBuilder { * * @param mixed $predicates The restriction predicates. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function where(...$predicates); @@ -570,7 +570,7 @@ interface IQueryBuilder { * * @param mixed ...$where The query restrictions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * * @see where() * @since 8.2.0 @@ -591,7 +591,7 @@ interface IQueryBuilder { * * @param mixed ...$where The WHERE statement. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * * @see where() * @since 8.2.0 @@ -611,7 +611,7 @@ interface IQueryBuilder { * * @param mixed ...$groupBys The grouping expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function groupBy(...$groupBys); @@ -629,7 +629,7 @@ interface IQueryBuilder { * * @param mixed ...$groupBy The grouping expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function addGroupBy(...$groupBy); @@ -651,7 +651,7 @@ interface IQueryBuilder { * @param string $column The column into which the value should be inserted. * @param string $value The value that should be inserted into the column. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function setValue($column, $value); @@ -673,7 +673,7 @@ interface IQueryBuilder { * * @param array $values The values to specify for the insert query indexed by column names. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function values(array $values); @@ -684,7 +684,7 @@ interface IQueryBuilder { * * @param mixed ...$having The restriction over the groups. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function having(...$having); @@ -695,7 +695,7 @@ interface IQueryBuilder { * * @param mixed ...$having The restriction to append. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function andHaving(...$having); @@ -706,7 +706,7 @@ interface IQueryBuilder { * * @param mixed ...$having The restriction to add. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function orHaving(...$having); @@ -718,7 +718,7 @@ interface IQueryBuilder { * @param string $sort The ordering expression. * @param string $order The ordering direction. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function orderBy($sort, $order = null); @@ -729,7 +729,7 @@ interface IQueryBuilder { * @param string $sort The ordering expression. * @param string $order The ordering direction. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function addOrderBy($sort, $order = null); @@ -757,7 +757,7 @@ interface IQueryBuilder { * * @param array|null $queryPartNames * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function resetQueryParts($queryPartNames = null); @@ -767,7 +767,7 @@ interface IQueryBuilder { * * @param string $queryPartName * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * @since 8.2.0 */ public function resetQueryPart($queryPartName); diff --git a/lib/public/Files/Cache/ICacheEntry.php b/lib/public/Files/Cache/ICacheEntry.php index bbc9982935e..5223720b006 100644 --- a/lib/public/Files/Cache/ICacheEntry.php +++ b/lib/public/Files/Cache/ICacheEntry.php @@ -132,4 +132,28 @@ interface ICacheEntry { * @since 9.0.0 */ public function isEncrypted(); + + /** + * Get the metadata etag for the file + * + * @return string | null + * @since 18.0.0 + */ + public function getMetadataEtag(): ?string; + + /** + * Get the last modified date as unix timestamp + * + * @return int | null + * @since 18.0.0 + */ + public function getCreationTime(): ?int; + + /** + * Get the last modified date as unix timestamp + * + * @return int | null + * @since 18.0.0 + */ + public function getUploadTime(): ?int; } diff --git a/lib/public/Files/FileInfo.php b/lib/public/Files/FileInfo.php index c256f0980f0..4d9e76b27f3 100644 --- a/lib/public/Files/FileInfo.php +++ b/lib/public/Files/FileInfo.php @@ -268,4 +268,30 @@ interface FileInfo { * @since 15.0.0 */ public function getExtension(): string; + + /** + * Get the creation date as unix timestamp + * + * If the creation time is not known, 0 will be returned + * + * creation time is not set automatically by the server and is generally only available + * for files uploaded by the sync clients + * + * @return int + * @since 18.0.0 + */ + public function getCreationTime(): int; + + /** + * Get the upload date as unix timestamp + * + * If the upload time is not known, 0 will be returned + * + * Upload time will be set automatically by the server for files uploaded over DAV + * files created by Nextcloud apps generally do not have an the upload time set + * + * @return int + * @since 18.0.0 + */ + public function getUploadTime(): int; } diff --git a/tests/lib/Files/Cache/CacheTest.php b/tests/lib/Files/Cache/CacheTest.php index eaea1692fe2..ccdfbbd065b 100644 --- a/tests/lib/Files/Cache/CacheTest.php +++ b/tests/lib/Files/Cache/CacheTest.php @@ -707,6 +707,76 @@ class CacheTest extends \Test\TestCase { } } + public function testExtended() { + $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory']; + $this->cache->put("", $folderData); + + $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'creation_time' => 20]; + $id1 = $this->cache->put("foo1", $data); + $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'upload_time' => 30]; + $this->cache->put("foo2", $data); + $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'metadata_etag' => 'foo']; + $this->cache->put("foo3", $data); + $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain']; + $id4 = $this->cache->put("foo4", $data); + + $entry = $this->cache->get($id1); + $this->assertEquals(20, $entry->getCreationTime()); + $this->assertEquals(0, $entry->getUploadTime()); + $this->assertEquals(null, $entry->getMetadataEtag()); + + $entries = $this->cache->getFolderContents(""); + $this->assertCount(4, $entries); + + $this->assertEquals("foo1", $entries[0]->getName()); + $this->assertEquals("foo2", $entries[1]->getName()); + $this->assertEquals("foo3", $entries[2]->getName()); + $this->assertEquals("foo4", $entries[3]->getName()); + + $this->assertEquals(20, $entries[0]->getCreationTime()); + $this->assertEquals(0, $entries[0]->getUploadTime()); + $this->assertEquals(null, $entries[0]->getMetadataEtag()); + + $this->assertEquals(0, $entries[1]->getCreationTime()); + $this->assertEquals(30, $entries[1]->getUploadTime()); + $this->assertEquals(null, $entries[1]->getMetadataEtag()); + + $this->assertEquals(0, $entries[2]->getCreationTime()); + $this->assertEquals(0, $entries[2]->getUploadTime()); + $this->assertEquals('foo', $entries[2]->getMetadataEtag()); + + $this->assertEquals(0, $entries[3]->getCreationTime()); + $this->assertEquals(0, $entries[3]->getUploadTime()); + $this->assertEquals(null, $entries[3]->getMetadataEtag()); + + $this->cache->update($id1, ['upload_time' => 25]); + + $entry = $this->cache->get($id1); + $this->assertEquals(20, $entry->getCreationTime()); + $this->assertEquals(25, $entry->getUploadTime()); + $this->assertEquals(null, $entry->getMetadataEtag()); + + $this->cache->put("sub", $folderData); + + $this->cache->move("foo1", "sub/foo1"); + + $entries = $this->cache->getFolderContents("sub"); + $this->assertCount(1, $entries); + + $this->assertEquals(20, $entries[0]->getCreationTime()); + $this->assertEquals(25, $entries[0]->getUploadTime()); + $this->assertEquals(null, $entries[0]->getMetadataEtag()); + + $this->cache->update($id4, ['upload_time' => 25]); + + $entry = $this->cache->get($id4); + $this->assertEquals(0, $entry->getCreationTime()); + $this->assertEquals(25, $entry->getUploadTime()); + $this->assertEquals(null, $entry->getMetadataEtag()); + + $this->cache->remove("sub"); + } + protected function tearDown() { if ($this->cache) { $this->cache->clear(); diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 4697dbe89d8..5c37c9a0e93 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -25,6 +25,7 @@ use OCP\Share; use OCP\Util; use Test\TestMoveableMountPoint; use Test\HookHelper; +use Test\Traits\UserTrait; class TemporaryNoTouch extends Temporary { public function touch($path, $mtime = null) { @@ -60,10 +61,12 @@ class TemporaryNoLocal extends Temporary { * @package Test\Files */ class ViewTest extends \Test\TestCase { + use UserTrait; + /** * @var \OC\Files\Storage\Storage[] $storages */ - private $storages = array(); + private $storages = []; /** * @var string @@ -138,9 +141,9 @@ class ViewTest extends \Test\TestCase { $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); $root = self::getUniqueID('/'); - Filesystem::mount($storage1, array(), $root . '/'); - Filesystem::mount($storage2, array(), $root . '/substorage'); - Filesystem::mount($storage3, array(), $root . '/folder/anotherstorage'); + Filesystem::mount($storage1, [], $root . '/'); + Filesystem::mount($storage2, [], $root . '/substorage'); + Filesystem::mount($storage3, [], $root . '/folder/anotherstorage'); $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png'); $storageSize = $textSize * 2 + $imageSize; @@ -204,13 +207,13 @@ class ViewTest extends \Test\TestCase { $cachedData = $rootView->getFileInfo('/foo.txt'); $this->assertFalse($cachedData['encrypted']); - $id = $rootView->putFileInfo('/foo.txt', array('encrypted' => true)); + $id = $rootView->putFileInfo('/foo.txt', ['encrypted' => true]); $cachedData = $rootView->getFileInfo('/foo.txt'); $this->assertTrue($cachedData['encrypted']); $this->assertEquals($cachedData['fileid'], $id); $this->assertFalse($rootView->getFileInfo('/non/existing')); - $this->assertEquals(array(), $rootView->getDirectoryContent('/non/existing')); + $this->assertEquals([], $rootView->getDirectoryContent('/non/existing')); } /** @@ -220,9 +223,9 @@ class ViewTest extends \Test\TestCase { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); - Filesystem::mount($storage3, array(), '/folder/anotherstorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/substorage'); + Filesystem::mount($storage3, [], '/folder/anotherstorage'); $rootView = new View(''); @@ -262,8 +265,8 @@ class ViewTest extends \Test\TestCase { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(); $storage1->mkdir('substorage'); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/substorage'); $rootView = new View(''); $folderContent = $rootView->getDirectoryContent('/'); @@ -292,8 +295,8 @@ class ViewTest extends \Test\TestCase { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/mount'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/mount'); $view = new View('/'); @@ -313,7 +316,7 @@ class ViewTest extends \Test\TestCase { public function testCacheIncompleteFolder() { $storage1 = $this->getTestStorage(false); Filesystem::clearMounts(); - Filesystem::mount($storage1, array(), '/incomplete'); + Filesystem::mount($storage1, [], '/incomplete'); $rootView = new View('/incomplete'); $entries = $rootView->getDirectoryContent('/'); @@ -327,8 +330,8 @@ class ViewTest extends \Test\TestCase { public function testAutoScan() { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(false); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/substorage'); $textSize = strlen("dummy file data\n"); $rootView = new View(''); @@ -349,15 +352,15 @@ class ViewTest extends \Test\TestCase { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); - Filesystem::mount($storage3, array(), '/folder/anotherstorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/substorage'); + Filesystem::mount($storage3, [], '/folder/anotherstorage'); $rootView = new View(''); $results = $rootView->search('foo'); $this->assertCount(6, $results); - $paths = array(); + $paths = []; foreach ($results as $result) { $this->assertEquals($result['path'], Filesystem::normalizePath($result['path'])); $paths[] = $result['path']; @@ -372,7 +375,7 @@ class ViewTest extends \Test\TestCase { $folderView = new View('/folder'); $results = $folderView->search('bar'); $this->assertCount(2, $results); - $paths = array(); + $paths = []; foreach ($results as $result) { $paths[] = $result['path']; } @@ -381,7 +384,7 @@ class ViewTest extends \Test\TestCase { $results = $folderView->search('foo'); $this->assertCount(2, $results); - $paths = array(); + $paths = []; foreach ($results as $result) { $paths[] = $result['path']; } @@ -397,7 +400,7 @@ class ViewTest extends \Test\TestCase { */ public function testWatcher() { $storage1 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); + Filesystem::mount($storage1, [], '/'); $storage1->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS); $rootView = new View(''); @@ -405,7 +408,7 @@ class ViewTest extends \Test\TestCase { $cachedData = $rootView->getFileInfo('foo.txt'); $this->assertEquals(16, $cachedData['size']); - $rootView->putFileInfo('foo.txt', array('storage_mtime' => 10)); + $rootView->putFileInfo('foo.txt', ['storage_mtime' => 10]); $storage1->file_put_contents('foo.txt', 'foo'); clearstatcache(); @@ -441,8 +444,8 @@ class ViewTest extends \Test\TestCase { } public function copyBetweenStorages($storage1, $storage2) { - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/substorage'); $rootView = new View(''); $rootView->mkdir('substorage/emptyfolder'); @@ -487,8 +490,8 @@ class ViewTest extends \Test\TestCase { } public function moveBetweenStorages($storage1, $storage2) { - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/substorage'); $rootView = new View(''); $rootView->rename('foo.txt', 'substorage/folder/foo.txt'); @@ -506,8 +509,8 @@ class ViewTest extends \Test\TestCase { public function testUnlink() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/substorage'); $rootView = new View(''); $rootView->file_put_contents('/foo.txt', 'asd'); @@ -552,8 +555,8 @@ class ViewTest extends \Test\TestCase { public function testUnlinkRootMustFail() { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], '/substorage'); $rootView = new View(''); $rootView->file_put_contents('/foo.txt', 'asd'); @@ -571,7 +574,7 @@ class ViewTest extends \Test\TestCase { public function testTouch() { $storage = $this->getTestStorage(true, TemporaryNoTouch::class); - Filesystem::mount($storage, array(), '/'); + Filesystem::mount($storage, [], '/'); $rootView = new View(''); $oldCachedData = $rootView->getFileInfo('foo.txt'); @@ -582,7 +585,7 @@ class ViewTest extends \Test\TestCase { $this->assertEquals(500, $cachedData['mtime']); $this->assertEquals($oldCachedData['storage_mtime'], $cachedData['storage_mtime']); - $rootView->putFileInfo('foo.txt', array('storage_mtime' => 1000)); //make sure the watcher detects the change + $rootView->putFileInfo('foo.txt', ['storage_mtime' => 1000]); //make sure the watcher detects the change $rootView->file_put_contents('foo.txt', 'asd'); $cachedData = $rootView->getFileInfo('foo.txt'); $this->assertGreaterThanOrEqual($oldCachedData['mtime'], $cachedData['mtime']); @@ -595,7 +598,7 @@ class ViewTest extends \Test\TestCase { public function testTouchFloat() { $storage = $this->getTestStorage(true, TemporaryNoTouch::class); - Filesystem::mount($storage, array(), '/'); + Filesystem::mount($storage, [], '/'); $rootView = new View(''); $oldCachedData = $rootView->getFileInfo('foo.txt'); @@ -613,8 +616,8 @@ class ViewTest extends \Test\TestCase { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), $defaultRoot . '/substorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], $defaultRoot . '/substorage'); \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook'); $rootView = new View(''); @@ -636,7 +639,7 @@ class ViewTest extends \Test\TestCase { public function testSearchNotOutsideView() { $storage1 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); + Filesystem::mount($storage1, [], '/'); $storage1->rename('folder', 'foo'); $scanner = $storage1->getScanner(); $scanner->scan(''); @@ -656,7 +659,7 @@ class ViewTest extends \Test\TestCase { /** * @var \OC\Files\Storage\Storage $storage */ - $storage = new $class(array()); + $storage = new $class([]); $textData = "dummy file data\n"; $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png'); $storage->mkdir('folder'); @@ -679,8 +682,8 @@ class ViewTest extends \Test\TestCase { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), $defaultRoot . '_substorage'); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], $defaultRoot . '_substorage'); \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook'); $subView = new View($defaultRoot . '_substorage'); @@ -710,8 +713,8 @@ class ViewTest extends \Test\TestCase { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $defaultRoot = Filesystem::getRoot(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), $defaultRoot); + Filesystem::mount($storage1, [], '/'); + Filesystem::mount($storage2, [], $defaultRoot); \OC_Hook::connect('OC_Filesystem', 'post_create', $this, 'dummyHookCreate'); \OC_Hook::connect('OC_Filesystem', 'post_update', $this, 'dummyHookUpdate'); \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHookWrite'); @@ -741,7 +744,7 @@ class ViewTest extends \Test\TestCase { */ public function testResolvePath($expected, $pathToTest) { $storage1 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); + Filesystem::mount($storage1, [], '/'); $view = new View(''); @@ -756,25 +759,25 @@ class ViewTest extends \Test\TestCase { } public function resolvePathTestProvider() { - return array( - array('foo.txt', 'foo.txt'), - array('foo.txt', '/foo.txt'), - array('folder', 'folder'), - array('folder', '/folder'), - array('folder', 'folder/'), - array('folder', '/folder/'), - array('folder/bar.txt', 'folder/bar.txt'), - array('folder/bar.txt', '/folder/bar.txt'), - array('', ''), - array('', '/'), - ); + return [ + ['foo.txt', 'foo.txt'], + ['foo.txt', '/foo.txt'], + ['folder', 'folder'], + ['folder', '/folder'], + ['folder', 'folder/'], + ['folder', '/folder/'], + ['folder/bar.txt', 'folder/bar.txt'], + ['folder/bar.txt', '/folder/bar.txt'], + ['', ''], + ['', '/'], + ]; } public function testUTF8Names() { - $names = array('虚', '和知しゃ和で', 'regular ascii', 'sɨˈrɪlɪk', 'ѨѬ', 'أنا أحب القراءة كثيرا'); + $names = ['虚', '和知しゃ和で', 'regular ascii', 'sɨˈrɪlɪk', 'ѨѬ', 'أنا أحب القراءة كثيرا']; - $storage = new Temporary(array()); - Filesystem::mount($storage, array(), '/'); + $storage = new Temporary([]); + Filesystem::mount($storage, [], '/'); $rootView = new View(''); foreach ($names as $name) { @@ -802,8 +805,8 @@ class ViewTest extends \Test\TestCase { public function xtestLongPath() { - $storage = new Temporary(array()); - Filesystem::mount($storage, array(), '/'); + $storage = new Temporary([]); + Filesystem::mount($storage, [], '/'); $rootView = new View(''); @@ -851,9 +854,9 @@ class ViewTest extends \Test\TestCase { } public function testTouchNotSupported() { - $storage = new TemporaryNoTouch(array()); + $storage = new TemporaryNoTouch([]); $scanner = $storage->getScanner(); - Filesystem::mount($storage, array(), '/test/'); + Filesystem::mount($storage, [], '/test/'); $past = time() - 100; $storage->file_put_contents('test', 'foobar'); $scanner->scan(''); @@ -868,13 +871,13 @@ class ViewTest extends \Test\TestCase { } public function testWatcherEtagCrossStorage() { - $storage1 = new Temporary(array()); - $storage2 = new Temporary(array()); + $storage1 = new Temporary([]); + $storage2 = new Temporary([]); $scanner1 = $storage1->getScanner(); $scanner2 = $storage2->getScanner(); $storage1->mkdir('sub'); - Filesystem::mount($storage1, array(), '/test/'); - Filesystem::mount($storage2, array(), '/test/sub/storage'); + Filesystem::mount($storage1, [], '/test/'); + Filesystem::mount($storage2, [], '/test/sub/storage'); $past = time() - 100; $storage2->file_put_contents('test.txt', 'foobar'); @@ -887,9 +890,9 @@ class ViewTest extends \Test\TestCase { $oldFileInfo = $view->getFileInfo('/test/sub/storage/test.txt'); $oldFolderInfo = $view->getFileInfo('/test'); - $storage2->getCache()->update($oldFileInfo->getId(), array( - 'storage_mtime' => $past - )); + $storage2->getCache()->update($oldFileInfo->getId(), [ + 'storage_mtime' => $past, + ]); $oldEtag = $oldFolderInfo->getEtag(); @@ -908,9 +911,9 @@ class ViewTest extends \Test\TestCase { } public function testPartFileInfo() { - $storage = new Temporary(array()); + $storage = new Temporary([]); $scanner = $storage->getScanner(); - Filesystem::mount($storage, array(), '/test/'); + Filesystem::mount($storage, [], '/test/'); $storage->file_put_contents('test.part', 'foobar'); $scanner->scan(''); $view = new View('/test'); @@ -922,15 +925,15 @@ class ViewTest extends \Test\TestCase { } public function absolutePathProvider() { - return array( - array('/files/', ''), - array('/files/0', '0'), - array('/files/false', 'false'), - array('/files/true', 'true'), - array('/files/', '/'), - array('/files/test', 'test'), - array('/files/test', '/test'), - ); + return [ + ['/files/', ''], + ['/files/0', '0'], + ['/files/false', 'false'], + ['/files/true', 'true'], + ['/files/', '/'], + ['/files/test', 'test'], + ['/files/test', '/test'], + ]; } /** @@ -959,81 +962,81 @@ class ViewTest extends \Test\TestCase { } public function relativePathProvider($missingRootExpectedPath) { - return array( + return [ // No root - returns the path - array('', '/files', '/files'), - array('', '/files/', '/files/'), + ['', '/files', '/files'], + ['', '/files/', '/files/'], // Root equals path - / - array('/files/', '/files/', '/'), - array('/files/', '/files', '/'), - array('/files', '/files/', '/'), - array('/files', '/files', '/'), + ['/files/', '/files/', '/'], + ['/files/', '/files', '/'], + ['/files', '/files/', '/'], + ['/files', '/files', '/'], // False negatives: chroot fixes those by adding the leading slash. // But setting them up with this root (instead of chroot($root)) // will fail them, although they should be the same. // TODO init should be fixed, so it also adds the leading slash - array('files/', '/files/', $missingRootExpectedPath), - array('files', '/files/', $missingRootExpectedPath), - array('files/', '/files', $missingRootExpectedPath), - array('files', '/files', $missingRootExpectedPath), + ['files/', '/files/', $missingRootExpectedPath], + ['files', '/files/', $missingRootExpectedPath], + ['files/', '/files', $missingRootExpectedPath], + ['files', '/files', $missingRootExpectedPath], // False negatives: Paths provided to the method should have a leading slash // TODO input should be checked to have a leading slash - array('/files/', 'files/', null), - array('/files', 'files/', null), - array('/files/', 'files', null), - array('/files', 'files', null), + ['/files/', 'files/', null], + ['/files', 'files/', null], + ['/files/', 'files', null], + ['/files', 'files', null], // with trailing slashes - array('/files/', '/files/0', '0'), - array('/files/', '/files/false', 'false'), - array('/files/', '/files/true', 'true'), - array('/files/', '/files/test', 'test'), - array('/files/', '/files/test/foo', 'test/foo'), + ['/files/', '/files/0', '0'], + ['/files/', '/files/false', 'false'], + ['/files/', '/files/true', 'true'], + ['/files/', '/files/test', 'test'], + ['/files/', '/files/test/foo', 'test/foo'], // without trailing slashes // TODO false expectation: Should match "with trailing slashes" - array('/files', '/files/0', '/0'), - array('/files', '/files/false', '/false'), - array('/files', '/files/true', '/true'), - array('/files', '/files/test', '/test'), - array('/files', '/files/test/foo', '/test/foo'), + ['/files', '/files/0', '/0'], + ['/files', '/files/false', '/false'], + ['/files', '/files/true', '/true'], + ['/files', '/files/test', '/test'], + ['/files', '/files/test/foo', '/test/foo'], // leading slashes - array('/files/', '/files_trashbin/', null), - array('/files', '/files_trashbin/', null), - array('/files/', '/files_trashbin', null), - array('/files', '/files_trashbin', null), + ['/files/', '/files_trashbin/', null], + ['/files', '/files_trashbin/', null], + ['/files/', '/files_trashbin', null], + ['/files', '/files_trashbin', null], // no leading slashes - array('files/', 'files_trashbin/', null), - array('files', 'files_trashbin/', null), - array('files/', 'files_trashbin', null), - array('files', 'files_trashbin', null), + ['files/', 'files_trashbin/', null], + ['files', 'files_trashbin/', null], + ['files/', 'files_trashbin', null], + ['files', 'files_trashbin', null], // mixed leading slashes - array('files/', '/files_trashbin/', null), - array('/files/', 'files_trashbin/', null), - array('files', '/files_trashbin/', null), - array('/files', 'files_trashbin/', null), - array('files/', '/files_trashbin', null), - array('/files/', 'files_trashbin', null), - array('files', '/files_trashbin', null), - array('/files', 'files_trashbin', null), - - array('files', 'files_trashbin/test', null), - array('/files', '/files_trashbin/test', null), - array('/files', 'files_trashbin/test', null), - ); + ['files/', '/files_trashbin/', null], + ['/files/', 'files_trashbin/', null], + ['files', '/files_trashbin/', null], + ['/files', 'files_trashbin/', null], + ['files/', '/files_trashbin', null], + ['/files/', 'files_trashbin', null], + ['files', '/files_trashbin', null], + ['/files', 'files_trashbin', null], + + ['files', 'files_trashbin/test', null], + ['/files', '/files_trashbin/test', null], + ['/files', 'files_trashbin/test', null], + ]; } public function testFileView() { - $storage = new Temporary(array()); + $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); - Filesystem::mount($storage, array(), '/test/'); + Filesystem::mount($storage, [], '/test/'); $scanner->scan(''); $view = new View('/test/foo.txt'); @@ -1059,9 +1062,9 @@ class ViewTest extends \Test\TestCase { $longPath .= '/' . $folderName; } - $storage = new Temporary(array()); + $storage = new Temporary([]); $this->tempStorage = $storage; // for later hard cleanup - Filesystem::mount($storage, array(), '/'); + Filesystem::mount($storage, [], '/'); $rootView = new View(''); @@ -1074,64 +1077,64 @@ class ViewTest extends \Test\TestCase { $longPath = 'md5'; } - call_user_func(array($rootView, $operation), $longPath, $param0); + call_user_func([$rootView, $operation], $longPath, $param0); } public function tooLongPathDataProvider() { - return array( - array('getAbsolutePath'), - array('getRelativePath'), - array('getMountPoint'), - array('resolvePath'), - array('getLocalFile'), - array('getLocalFolder'), - array('mkdir'), - array('rmdir'), - array('opendir'), - array('is_dir'), - array('is_file'), - array('stat'), - array('filetype'), - array('filesize'), - array('readfile'), - array('isCreatable'), - array('isReadable'), - array('isUpdatable'), - array('isDeletable'), - array('isSharable'), - array('file_exists'), - array('filemtime'), - array('touch'), - array('file_get_contents'), - array('unlink'), - array('deleteAll'), - array('toTmpFile'), - array('getMimeType'), - array('free_space'), - array('getFileInfo'), - array('getDirectoryContent'), - array('getOwner'), - array('getETag'), - array('file_put_contents', 'ipsum'), - array('rename', '@0'), - array('copy', '@0'), - array('fopen', 'r'), - array('fromTmpFile', '@0'), - array('hash'), - array('hasUpdated', 0), - array('putFileInfo', array()), - ); + return [ + ['getAbsolutePath'], + ['getRelativePath'], + ['getMountPoint'], + ['resolvePath'], + ['getLocalFile'], + ['getLocalFolder'], + ['mkdir'], + ['rmdir'], + ['opendir'], + ['is_dir'], + ['is_file'], + ['stat'], + ['filetype'], + ['filesize'], + ['readfile'], + ['isCreatable'], + ['isReadable'], + ['isUpdatable'], + ['isDeletable'], + ['isSharable'], + ['file_exists'], + ['filemtime'], + ['touch'], + ['file_get_contents'], + ['unlink'], + ['deleteAll'], + ['toTmpFile'], + ['getMimeType'], + ['free_space'], + ['getFileInfo'], + ['getDirectoryContent'], + ['getOwner'], + ['getETag'], + ['file_put_contents', 'ipsum'], + ['rename', '@0'], + ['copy', '@0'], + ['fopen', 'r'], + ['fromTmpFile', '@0'], + ['hash'], + ['hasUpdated', 0], + ['putFileInfo', []], + ]; } public function testRenameCrossStoragePreserveMtime() { - $storage1 = new Temporary(array()); - $storage2 = new Temporary(array()); + $storage1 = new Temporary([]); + $storage2 = new Temporary([]); $storage1->mkdir('sub'); $storage1->mkdir('foo'); $storage1->file_put_contents('foo.txt', 'asd'); $storage1->file_put_contents('foo/bar.txt', 'asd'); - Filesystem::mount($storage1, array(), '/test/'); - Filesystem::mount($storage2, array(), '/test/sub/storage'); + Filesystem::mount($storage1, [], '/test/'); + Filesystem::mount($storage2, [], '/test/sub/storage'); $view = new View(''); $time = time() - 200; @@ -1157,7 +1160,7 @@ class ViewTest extends \Test\TestCase { } private function doTestCopyRenameFail($operation) { - $storage1 = new Temporary(array()); + $storage1 = new Temporary([]); /** @var \PHPUnit_Framework_MockObject_MockObject|Temporary $storage2 */ $storage2 = $this->getMockBuilder(TemporaryNoCross::class) ->setConstructorArgs([[]]) @@ -1180,8 +1183,8 @@ class ViewTest extends \Test\TestCase { $storage2->file_put_contents('existing.txt', '0123'); $storage1->getScanner()->scan(''); $storage2->getScanner()->scan(''); - Filesystem::mount($storage1, array(), '/test/'); - Filesystem::mount($storage2, array(), '/test/sub/storage'); + Filesystem::mount($storage1, [], '/test/'); + Filesystem::mount($storage2, [], '/test/sub/storage'); // move file $view = new View(''); @@ -1212,8 +1215,8 @@ class ViewTest extends \Test\TestCase { public function testDeleteFailKeepCache() { /** @var Temporary|\PHPUnit_Framework_MockObject_MockObject $storage */ $storage = $this->getMockBuilder(Temporary::class) - ->setConstructorArgs(array(array())) - ->setMethods(array('unlink')) + ->setConstructorArgs([[]]) + ->setMethods(['unlink']) ->getMock(); $storage->expects($this->once()) ->method('unlink') @@ -1222,7 +1225,7 @@ class ViewTest extends \Test\TestCase { $cache = $storage->getCache(); $storage->file_put_contents('foo.txt', 'asd'); $scanner->scan(''); - Filesystem::mount($storage, array(), '/test/'); + Filesystem::mount($storage, [], '/test/'); $view = new View('/test'); @@ -1248,14 +1251,14 @@ class ViewTest extends \Test\TestCase { } public function testRenameOverWrite() { - $storage = new Temporary(array()); + $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->mkdir('sub'); $storage->mkdir('foo'); $storage->file_put_contents('foo.txt', 'asd'); $storage->file_put_contents('foo/bar.txt', 'asd'); $scanner->scan(''); - Filesystem::mount($storage, array(), '/test/'); + Filesystem::mount($storage, [], '/test/'); $view = new View(''); $this->assertTrue($view->rename('/test/foo.txt', '/test/foo/bar.txt')); } @@ -1309,7 +1312,7 @@ class ViewTest extends \Test\TestCase { $pathPrefix = str_replace('{folder}', 'files', $pathPrefix); $view = new View($rootPath); - $storage = new Temporary(array()); + $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); $this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE)); $view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED); @@ -1328,7 +1331,7 @@ class ViewTest extends \Test\TestCase { $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix); $view = new View($rootPath); - $storage = new Temporary(array()); + $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE)); $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED)); @@ -1349,7 +1352,7 @@ class ViewTest extends \Test\TestCase { $pathPrefix = str_replace('{folder}', 'files', $pathPrefix); $view = new View($rootPath); - $storage = new Temporary(array()); + $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); $this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED)); $view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE); @@ -1368,7 +1371,7 @@ class ViewTest extends \Test\TestCase { $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix); $view = new View($rootPath); - $storage = new Temporary(array()); + $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED)); $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE)); @@ -1516,7 +1519,7 @@ class ViewTest extends \Test\TestCase { public function testChangeLock() { $view = new View('/testuser/files/'); - $storage = new Temporary(array()); + $storage = new Temporary([]); Filesystem::mount($storage, [], '/'); $view->lockFile('/test/sub', ILockingProvider::LOCK_SHARED); @@ -1544,7 +1547,7 @@ class ViewTest extends \Test\TestCase { ['/foo', '/files/foo', true], ['/foo', 'filesfoo', false], ['', '/foo/files', true], - ['', '/foo/files/bar.txt', true] + ['', '/foo/files/bar.txt', true], ]; } @@ -1828,7 +1831,7 @@ class ViewTest extends \Test\TestCase { ->setMethods([$operation]) ->getMock(); - Filesystem::mount($storage, array(), $this->user . '/'); + Filesystem::mount($storage, [], $this->user . '/'); // work directly on disk because mkdir might be mocked $realPath = $storage->getSourcePath(''); @@ -1852,7 +1855,7 @@ class ViewTest extends \Test\TestCase { $this->connectMockHooks($hookType, $view, $lockedPath, $lockTypePre, $lockTypePost); // do operation - call_user_func_array(array($view, $operation), $operationArgs); + call_user_func_array([$view, $operation], $operationArgs); if ($hookType !== null) { $this->assertEquals($expectedLockBefore, $lockTypePre, 'File locked properly during pre-hook'); @@ -1878,7 +1881,7 @@ class ViewTest extends \Test\TestCase { ->setMethods(['fopen']) ->getMock(); - Filesystem::mount($storage, array(), $this->user . '/'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $storage->expects($this->once()) @@ -1917,7 +1920,7 @@ class ViewTest extends \Test\TestCase { ->setMethods(['fopen']) ->getMock(); - Filesystem::mount($storage, array(), $this->user . '/'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $storage->expects($this->once()) @@ -1972,7 +1975,7 @@ class ViewTest extends \Test\TestCase { ->setMethods([$operation]) ->getMock(); - Filesystem::mount($storage, array(), $this->user . '/'); + Filesystem::mount($storage, [], $this->user . '/'); // work directly on disk because mkdir might be mocked $realPath = $storage->getSourcePath(''); @@ -1991,7 +1994,7 @@ class ViewTest extends \Test\TestCase { $thrown = false; try { - call_user_func_array(array($view, $operation), $operationArgs); + call_user_func_array([$view, $operation], $operationArgs); } catch (\Exception $e) { $thrown = true; $this->assertEquals('Simulated exception', $e->getMessage()); @@ -2005,7 +2008,7 @@ class ViewTest extends \Test\TestCase { $storage = new Temporary([]); - Filesystem::mount($storage, array(), $this->user . '/'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $storage->mkdir('files/dir'); @@ -2054,7 +2057,7 @@ class ViewTest extends \Test\TestCase { ->setMethods([$operation]) ->getMock(); - Filesystem::mount($storage, array(), $this->user . '/'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); Util::connectHook( @@ -2064,7 +2067,7 @@ class ViewTest extends \Test\TestCase { 'cancellingCallback' ); - call_user_func_array(array($view, $operation), $operationArgs); + call_user_func_array([$view, $operation], $operationArgs); $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception'); } @@ -2100,7 +2103,7 @@ class ViewTest extends \Test\TestCase { $sourcePath = 'original.txt'; $targetPath = 'target.txt'; - Filesystem::mount($storage, array(), $this->user . '/'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); @@ -2152,7 +2155,7 @@ class ViewTest extends \Test\TestCase { $sourcePath = 'original.txt'; $targetPath = 'target.txt'; - Filesystem::mount($storage, array(), $this->user . '/'); + Filesystem::mount($storage, [], $this->user . '/'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); @@ -2279,8 +2282,8 @@ class ViewTest extends \Test\TestCase { $sourcePath = 'original.txt'; $targetPath = 'substorage/target.txt'; - Filesystem::mount($storage, array(), $this->user . '/'); - Filesystem::mount($storage2, array(), $this->user . '/files/substorage'); + Filesystem::mount($storage, [], $this->user . '/'); + Filesystem::mount($storage2, [], $this->user . '/files/substorage'); $storage->mkdir('files'); $view->file_put_contents($sourcePath, 'meh'); @@ -2513,7 +2516,7 @@ class ViewTest extends \Test\TestCase { public function testGetDirectoryContentMimeFilter($filter, $expected) { $storage1 = new Temporary(); $root = self::getUniqueID('/'); - Filesystem::mount($storage1, array(), $root . '/'); + Filesystem::mount($storage1, [], $root . '/'); $view = new View($root); $view->file_put_contents('test1.txt', 'asd'); @@ -2532,10 +2535,10 @@ class ViewTest extends \Test\TestCase { } public function testFilePutContentsClearsChecksum() { - $storage = new Temporary(array()); + $storage = new Temporary([]); $scanner = $storage->getScanner(); $storage->file_put_contents('foo.txt', 'bar'); - Filesystem::mount($storage, array(), '/test/'); + Filesystem::mount($storage, [], '/test/'); $scanner->scan(''); $view = new View('/test/foo.txt'); @@ -2552,11 +2555,11 @@ class ViewTest extends \Test\TestCase { } public function testDeleteGhostFile() { - $storage = new Temporary(array()); + $storage = new Temporary([]); $scanner = $storage->getScanner(); $cache = $storage->getCache(); $storage->file_put_contents('foo.txt', 'bar'); - Filesystem::mount($storage, array(), '/test/'); + Filesystem::mount($storage, [], '/test/'); $scanner->scan(''); $storage->unlink('foo.txt'); @@ -2575,12 +2578,12 @@ class ViewTest extends \Test\TestCase { } public function testDeleteGhostFolder() { - $storage = new Temporary(array()); + $storage = new Temporary([]); $scanner = $storage->getScanner(); $cache = $storage->getCache(); $storage->mkdir('foo'); $storage->file_put_contents('foo/foo.txt', 'bar'); - Filesystem::mount($storage, array(), '/test/'); + Filesystem::mount($storage, [], '/test/'); $scanner->scan(''); $storage->rmdir('foo'); @@ -2669,4 +2672,24 @@ class ViewTest extends \Test\TestCase { ->willReturn(true); $this->assertFalse(self::invokePrivate($view, 'createParentDirectories', ['/file.txt/folder/structure'])); } + + public function testCacheExtension() { + $storage = new Temporary([]); + $scanner = $storage->getScanner(); + $storage->file_put_contents('foo.txt', 'bar'); + $scanner->scan(''); + + Filesystem::mount($storage, [], '/test/'); + $view = new View('/test'); + + $info = $view->getFileInfo('/foo.txt'); + $this->assertEquals(0, $info->getUploadTime()); + $this->assertEquals(0, $info->getCreationTime()); + + $view->putFileInfo('/foo.txt', ['upload_time' => 25]); + + $info = $view->getFileInfo('/foo.txt'); + $this->assertEquals(25, $info->getUploadTime()); + $this->assertEquals(0, $info->getCreationTime()); + } } diff --git a/tests/lib/HelperStorageTest.php b/tests/lib/HelperStorageTest.php index d42c43c5ab8..dacd73a4f01 100644 --- a/tests/lib/HelperStorageTest.php +++ b/tests/lib/HelperStorageTest.php @@ -26,6 +26,7 @@ class HelperStorageTest extends \Test\TestCase { parent::setUp(); $this->user = $this->getUniqueID('user_'); + \OC_User::useBackend('dummy'); \OC::$server->getUserManager()->createUser($this->user, $this->user); $this->storage = \OC\Files\Filesystem::getStorage('/'); |