Skip to content

Commit df1f5c7

Browse files
authored
Merge pull request #58692 from nextcloud/backport/58562/stable31
[stable31] feat: set creation_time on file creation and render recently created icon
2 parents 1bdc3c2 + 7991928 commit df1f5c7

14 files changed

Lines changed: 137 additions & 10 deletions

File tree

apps/dav/lib/Capabilities.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ public function __construct(
1717
}
1818

1919
/**
20-
* @return array{dav: array{chunking: string, bulkupload?: string, absence-supported?: bool, absence-replacement?: bool}}
20+
* @return array{dav: array{chunking: string, search_supports_creation_time: bool, search_supports_upload_time: bool, bulkupload?: string, absence-supported?: bool, absence-replacement?: bool}}
2121
*/
2222
public function getCapabilities() {
2323
$capabilities = [
2424
'dav' => [
2525
'chunking' => '1.0',
26+
'search_supports_creation_time' => true,
2627
'search_supports_upload_time' => true,
2728
]
2829
];

apps/dav/lib/Files/FileSearchBackend.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public function getPropertyDefinitionsForScope(string $href, ?string $path): arr
8585
new SearchPropertyDefinition('{DAV:}displayname', true, true, true),
8686
new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true),
8787
new SearchPropertyDefinition('{DAV:}getlastmodified', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME),
88+
new SearchPropertyDefinition('{DAV:}creationdate', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME),
8889
new SearchPropertyDefinition('{http://nextcloud.org/ns}upload_time', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME),
8990
new SearchPropertyDefinition(FilesPlugin::SIZE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
9091
new SearchPropertyDefinition(TagsPlugin::FAVORITE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_BOOLEAN),
@@ -298,6 +299,8 @@ private function getSearchResultProperty(SearchResult $result, SearchPropertyDef
298299
return $node->getName();
299300
case '{DAV:}getlastmodified':
300301
return $node->getLastModified();
302+
case '{DAV:}creationdate':
303+
return $node->getNode()->getCreationTime();
301304
case '{http://nextcloud.org/ns}upload_time':
302305
return $node->getNode()->getUploadTime();
303306
case FilesPlugin::SIZE_PROPERTYNAME:
@@ -454,6 +457,8 @@ private function mapPropertyNameToColumn(SearchPropertyDefinition $property) {
454457
return 'mimetype';
455458
case '{DAV:}getlastmodified':
456459
return 'mtime';
460+
case '{DAV:}creationdate':
461+
return 'creation_time';
457462
case '{http://nextcloud.org/ns}upload_time':
458463
return 'upload_time';
459464
case FilesPlugin::SIZE_PROPERTYNAME:

apps/dav/openapi.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,20 @@
2929
"dav": {
3030
"type": "object",
3131
"required": [
32-
"chunking"
32+
"chunking",
33+
"search_supports_creation_time",
34+
"search_supports_upload_time"
3335
],
3436
"properties": {
3537
"chunking": {
3638
"type": "string"
3739
},
40+
"search_supports_creation_time": {
41+
"type": "boolean"
42+
},
43+
"search_supports_upload_time": {
44+
"type": "boolean"
45+
},
3846
"bulkupload": {
3947
"type": "string"
4048
},

apps/dav/tests/unit/CapabilitiesTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public function testGetCapabilities(): void {
2828
$expected = [
2929
'dav' => [
3030
'chunking' => '1.0',
31+
'search_supports_creation_time' => true,
32+
'search_supports_upload_time' => true,
3133
],
3234
];
3335
$this->assertSame($expected, $capabilities->getCapabilities());
@@ -47,6 +49,8 @@ public function testGetCapabilitiesWithBulkUpload(): void {
4749
$expected = [
4850
'dav' => [
4951
'chunking' => '1.0',
52+
'search_supports_creation_time' => true,
53+
'search_supports_upload_time' => true,
5054
'bulkupload' => '1.0',
5155
],
5256
];
@@ -67,6 +71,8 @@ public function testGetCapabilitiesWithAbsence(): void {
6771
$expected = [
6872
'dav' => [
6973
'chunking' => '1.0',
74+
'search_supports_creation_time' => true,
75+
'search_supports_upload_time' => true,
7076
'absence-supported' => true,
7177
'absence-replacement' => true,
7278
],

apps/files/src/components/FileEntry/FileEntryPreview.vue

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
<FavoriteIcon v-once />
4040
</span>
4141

42+
<!-- Recently created icon -->
43+
<span v-else-if="isRecentView && isRecentlyCreated" class="files-list__row-icon-recently-created">
44+
<RecentlyCreatedIcon v-once />
45+
</span>
46+
4247
<OverlayIcon :is="fileOverlay"
4348
v-if="fileOverlay"
4449
class="files-list__row-icon-overlay files-list__row-icon-overlay--file" />
@@ -70,6 +75,7 @@ import PlayCircleIcon from 'vue-material-design-icons/PlayCircle.vue'
7075
7176
import CollectivesIcon from './CollectivesIcon.vue'
7277
import FavoriteIcon from './FavoriteIcon.vue'
78+
import RecentlyCreatedIcon from './RecentlyCreatedIcon.vue'
7379
7480
import { isLivePhoto } from '../../services/LivePhotos'
7581
import { useUserConfigStore } from '../../store/userconfig.ts'
@@ -90,6 +96,7 @@ export default defineComponent({
9096
LinkIcon,
9197
NetworkIcon,
9298
TagIcon,
99+
RecentlyCreatedIcon,
93100
},
94101
95102
props: {
@@ -132,6 +139,21 @@ export default defineComponent({
132139
return this.source.attributes.favorite === 1
133140
},
134141
142+
isRecentlyCreated(): boolean {
143+
if (!this.source.crtime) {
144+
return false
145+
}
146+
147+
const oneDayAgo = new Date()
148+
oneDayAgo.setDate(oneDayAgo.getDate() - 1)
149+
150+
return this.source.crtime > oneDayAgo
151+
},
152+
153+
isRecentView(): boolean {
154+
return this.$route?.params?.view === 'recent'
155+
},
156+
135157
userConfig(): UserConfig {
136158
return this.userConfigStore.userConfig
137159
},
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<!--
2+
- SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
- SPDX-License-Identifier: AGPL-3.0-or-later
4+
-->
5+
<template>
6+
<NcIconSvgWrapper class="recently-created-marker-icon" :name="t('files', 'Recently created')" :path="mdiPlus" />
7+
</template>
8+
9+
<script lang="ts">
10+
import { mdiPlus } from '@mdi/js'
11+
import { t } from '@nextcloud/l10n'
12+
import { defineComponent } from 'vue'
13+
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
14+
15+
/**
16+
* A recently created icon to be used for overlaying recently created entries like the file preview / icon
17+
* It has a stroke around the icon to ensure enough contrast for accessibility.
18+
*
19+
* If the background has a hover state you might want to also apply it to the stroke like this:
20+
* ```scss
21+
* .parent:hover :deep(.recently-created-marker-icon svg path) {
22+
* stroke: var(--color-background-hover);
23+
* }
24+
* ```
25+
*/
26+
export default defineComponent({
27+
name: 'RecentlyCreatedIcon',
28+
components: {
29+
NcIconSvgWrapper,
30+
},
31+
32+
setup() {
33+
return {
34+
mdiPlus,
35+
}
36+
},
37+
38+
methods: {
39+
t,
40+
},
41+
})
42+
</script>
43+
44+
<style lang="scss" scoped>
45+
.recently-created-marker-icon {
46+
color: var(--color-element-success);
47+
// Override NcIconSvgWrapper defaults (clickable area)
48+
min-width: unset !important;
49+
min-height: unset !important;
50+
51+
:deep() {
52+
svg {
53+
// We added a stroke for a11y so we must increase the size to include the stroke
54+
width: 20px !important;
55+
height: 20px !important;
56+
57+
// Override NcIconSvgWrapper defaults of 20px
58+
max-width: unset !important;
59+
max-height: unset !important;
60+
61+
// Show a border around the icon for better contrast
62+
path {
63+
stroke: var(--color-main-background);
64+
stroke-width: 8px;
65+
stroke-linejoin: round;
66+
paint-order: stroke;
67+
}
68+
}
69+
}
70+
}
71+
</style>

apps/files/src/components/FilesListVirtual.vue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ export default defineComponent({
679679
& > span {
680680
justify-content: flex-start;
681681
682-
&:not(.files-list__row-icon-favorite) svg {
682+
&:not(.files-list__row-icon-favorite):not(.files-list__row-icon-recently-created) svg {
683683
width: var(--icon-preview-size);
684684
height: var(--icon-preview-size);
685685
}
@@ -727,7 +727,8 @@ export default defineComponent({
727727
}
728728
}
729729
730-
&-favorite {
730+
&-favorite,
731+
&-recently-created {
731732
position: absolute;
732733
top: 0px;
733734
inset-inline-end: -10px;
@@ -923,8 +924,9 @@ export default defineComponent({
923924
}
924925
}
925926
926-
// Star icon in the top right
927-
.files-list__row-icon-favorite {
927+
// Icon in the top right
928+
.files-list__row-icon-favorite,
929+
.files-list__row-icon-recently-created {
928930
position: absolute;
929931
top: 0;
930932
inset-inline-end: 0;

dist/files-main.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/files-main.js.license

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ This file is generated from multiple sources. Included packages:
5252
- @linusborg/vue-simple-portal
5353
- version: 0.1.5
5454
- license: Apache-2.0
55+
- @mdi/js
56+
- version: 7.4.47
57+
- license: Apache-2.0
5558
- @mdi/svg
5659
- version: 7.4.47
5760
- license: Apache-2.0

dist/files-main.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)