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;
}