diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php index 5d4c2f21e8b..8561627ffd9 100644 --- a/apps/dav/lib/Files/FileSearchBackend.php +++ b/apps/dav/lib/Files/FileSearchBackend.php @@ -86,6 +86,7 @@ class FileSearchBackend implements ISearchBackend { new SearchPropertyDefinition('{DAV:}displayname', true, true, true), new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true), new SearchPropertyDefinition('{DAV:}getlastmodified', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME), + new SearchPropertyDefinition('{http://nextcloud.org/ns}creation_time', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME), new SearchPropertyDefinition('{http://nextcloud.org/ns}upload_time', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME), new SearchPropertyDefinition(FilesPlugin::SIZE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), new SearchPropertyDefinition(TagsPlugin::FAVORITE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_BOOLEAN), @@ -299,6 +300,8 @@ class FileSearchBackend implements ISearchBackend { return $node->getName(); case '{DAV:}getlastmodified': return $node->getLastModified(); + case '{http://nextcloud.org/ns}creation_time': + return $node->getNode()->getCreationTime(); case '{http://nextcloud.org/ns}upload_time': return $node->getNode()->getUploadTime(); case FilesPlugin::SIZE_PROPERTYNAME: @@ -461,6 +464,8 @@ class FileSearchBackend implements ISearchBackend { return 'mimetype'; case '{DAV:}getlastmodified': return 'mtime'; + case '{http://nextcloud.org/ns}creation_time': + return 'creation_time'; case '{http://nextcloud.org/ns}upload_time': return 'upload_time'; case FilesPlugin::SIZE_PROPERTYNAME: diff --git a/apps/files/src/components/FileEntry/FileEntryPreview.vue b/apps/files/src/components/FileEntry/FileEntryPreview.vue index 2696c2c00d7..7b6b4e954c3 100644 --- a/apps/files/src/components/FileEntry/FileEntryPreview.vue +++ b/apps/files/src/components/FileEntry/FileEntryPreview.vue @@ -42,6 +42,11 @@ + + + + + oneDayAgo + }, + + isRecentView(): boolean { + return this.$route?.params?.view === 'recent' + }, + userConfig(): UserConfig { return this.userConfigStore.userConfig }, diff --git a/apps/files/src/components/FileEntry/RecentlyCreatedIcon.vue b/apps/files/src/components/FileEntry/RecentlyCreatedIcon.vue new file mode 100644 index 00000000000..6e69f0f3e7c --- /dev/null +++ b/apps/files/src/components/FileEntry/RecentlyCreatedIcon.vue @@ -0,0 +1,78 @@ + + + + + + diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue index 14865d22733..256f9361ec0 100644 --- a/apps/files/src/components/FilesListVirtual.vue +++ b/apps/files/src/components/FilesListVirtual.vue @@ -743,7 +743,7 @@ export default defineComponent({ & > span { justify-content: flex-start; - &:not(.files-list__row-icon-favorite) svg { + &:not(.files-list__row-icon-favorite):not(.files-list__row-icon-recently-created) svg { width: var(--icon-preview-size); height: var(--icon-preview-size); } @@ -791,7 +791,8 @@ export default defineComponent({ } } - &-favorite { + &-favorite, + &-recently-created { position: absolute; top: 0px; inset-inline-end: -10px; @@ -993,8 +994,9 @@ export default defineComponent({ } } - // Star icon in the top right - .files-list__row-icon-favorite { + // Icon in the top right + .files-list__row-icon-favorite, + .files-list__row-icon-recently-created { position: absolute; top: 0; inset-inline-end: 0; diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index 9bd6e584974..b9de944b834 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -250,6 +250,12 @@ class Cache implements ICache { * @throws \RuntimeException */ public function put($file, array $data) { + // do not carry over creation_time to file versions, as each new version would otherwise + // create a filecache_extended entry with the same creation_time as the original file + if (str_starts_with($file, 'files_versions/')) { + unset($data['creation_time']); + } + if (($id = $this->getId($file)) > -1) { $this->update($id, $data); return $id; diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index 51f97baf489..284ba7f6ef0 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -153,7 +153,7 @@ class QuerySearchHelper { $requestedFields = $this->searchBuilder->extractRequestedFields($searchQuery->getSearchOperation()); - $joinExtendedCache = in_array('upload_time', $requestedFields); + $joinExtendedCache = in_array('creation_time', $requestedFields) || in_array('upload_time', $requestedFields); $query = $builder->selectFileCache('file', $joinExtendedCache); diff --git a/lib/private/Files/Cache/SearchBuilder.php b/lib/private/Files/Cache/SearchBuilder.php index 77fb5a2daa5..4f012c8f208 100644 --- a/lib/private/Files/Cache/SearchBuilder.php +++ b/lib/private/Files/Cache/SearchBuilder.php @@ -64,6 +64,7 @@ class SearchBuilder { 'share_with' => 'string', 'share_type' => 'integer', 'owner' => 'string', + 'creation_time' => 'integer', 'upload_time' => 'integer', ]; @@ -258,6 +259,7 @@ class SearchBuilder { 'share_with' => ['eq'], 'share_type' => ['eq'], 'owner' => ['eq'], + 'creation_time' => ['eq', 'gt', 'lt', 'gte', 'lte'], 'upload_time' => ['eq', 'gt', 'lt', 'gte', 'lte'], ]; diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index c27f0511080..14350fc435e 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -175,6 +175,7 @@ class Folder extends Node implements IFolder { throw new NotPermittedException('Could not create path "' . $fullPath . '"'); } $node = new File($this->root, $this->view, $fullPath, null, $this); + $this->view->putFileInfo($fullPath, ['creation_time' => time()]); $this->sendHooks(['postWrite', 'postCreate'], [$node]); return $node; }