Merge pull request #58692 from nextcloud/backport/58562/stable31
Some checks failed
Integration sqlite / changes (push) Has been cancelled
Psalm static code analysis / static-code-analysis (push) Has been cancelled
Psalm static code analysis / static-code-analysis-security (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ocp (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ncu (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, --tags ~@large files_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, capabilities_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, collaboration_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, comments_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, dav_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, federation_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, file_conversions) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, files_reminders) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, filesdrop_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, ldap_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, openldap_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, openldap_numerical_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, remoteapi_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, setup_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, sharees_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, sharing_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, theming_features) (push) Has been cancelled
Integration sqlite / integration-sqlite (stable31, 8.1, stable31, videoverification_features) (push) Has been cancelled
Integration sqlite / integration-sqlite-summary (push) Has been cancelled

[stable31] feat: set creation_time on file creation and render recently created icon
This commit is contained in:
Stephan Orbaugh 2026-03-04 09:32:29 +01:00 committed by GitHub
commit df1f5c7cde
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 137 additions and 10 deletions

View file

@ -17,12 +17,13 @@ class Capabilities implements ICapability {
}
/**
* @return array{dav: array{chunking: string, bulkupload?: string, absence-supported?: bool, absence-replacement?: bool}}
* @return array{dav: array{chunking: string, search_supports_creation_time: bool, search_supports_upload_time: bool, bulkupload?: string, absence-supported?: bool, absence-replacement?: bool}}
*/
public function getCapabilities() {
$capabilities = [
'dav' => [
'chunking' => '1.0',
'search_supports_creation_time' => true,
'search_supports_upload_time' => true,
]
];

View file

@ -85,6 +85,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('{DAV:}creationdate', 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),
@ -298,6 +299,8 @@ class FileSearchBackend implements ISearchBackend {
return $node->getName();
case '{DAV:}getlastmodified':
return $node->getLastModified();
case '{DAV:}creationdate':
return $node->getNode()->getCreationTime();
case '{http://nextcloud.org/ns}upload_time':
return $node->getNode()->getUploadTime();
case FilesPlugin::SIZE_PROPERTYNAME:
@ -454,6 +457,8 @@ class FileSearchBackend implements ISearchBackend {
return 'mimetype';
case '{DAV:}getlastmodified':
return 'mtime';
case '{DAV:}creationdate':
return 'creation_time';
case '{http://nextcloud.org/ns}upload_time':
return 'upload_time';
case FilesPlugin::SIZE_PROPERTYNAME:

View file

@ -29,12 +29,20 @@
"dav": {
"type": "object",
"required": [
"chunking"
"chunking",
"search_supports_creation_time",
"search_supports_upload_time"
],
"properties": {
"chunking": {
"type": "string"
},
"search_supports_creation_time": {
"type": "boolean"
},
"search_supports_upload_time": {
"type": "boolean"
},
"bulkupload": {
"type": "string"
},

View file

@ -28,6 +28,8 @@ class CapabilitiesTest extends TestCase {
$expected = [
'dav' => [
'chunking' => '1.0',
'search_supports_creation_time' => true,
'search_supports_upload_time' => true,
],
];
$this->assertSame($expected, $capabilities->getCapabilities());
@ -47,6 +49,8 @@ class CapabilitiesTest extends TestCase {
$expected = [
'dav' => [
'chunking' => '1.0',
'search_supports_creation_time' => true,
'search_supports_upload_time' => true,
'bulkupload' => '1.0',
],
];
@ -67,6 +71,8 @@ class CapabilitiesTest extends TestCase {
$expected = [
'dav' => [
'chunking' => '1.0',
'search_supports_creation_time' => true,
'search_supports_upload_time' => true,
'absence-supported' => true,
'absence-replacement' => true,
],

View file

@ -39,6 +39,11 @@
<FavoriteIcon v-once />
</span>
<!-- Recently created icon -->
<span v-else-if="isRecentView && isRecentlyCreated" class="files-list__row-icon-recently-created">
<RecentlyCreatedIcon v-once />
</span>
<OverlayIcon :is="fileOverlay"
v-if="fileOverlay"
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'
import CollectivesIcon from './CollectivesIcon.vue'
import FavoriteIcon from './FavoriteIcon.vue'
import RecentlyCreatedIcon from './RecentlyCreatedIcon.vue'
import { isLivePhoto } from '../../services/LivePhotos'
import { useUserConfigStore } from '../../store/userconfig.ts'
@ -90,6 +96,7 @@ export default defineComponent({
LinkIcon,
NetworkIcon,
TagIcon,
RecentlyCreatedIcon,
},
props: {
@ -132,6 +139,21 @@ export default defineComponent({
return this.source.attributes.favorite === 1
},
isRecentlyCreated(): boolean {
if (!this.source.crtime) {
return false
}
const oneDayAgo = new Date()
oneDayAgo.setDate(oneDayAgo.getDate() - 1)
return this.source.crtime > oneDayAgo
},
isRecentView(): boolean {
return this.$route?.params?.view === 'recent'
},
userConfig(): UserConfig {
return this.userConfigStore.userConfig
},

View file

@ -0,0 +1,71 @@
<!--
- SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcIconSvgWrapper class="recently-created-marker-icon" :name="t('files', 'Recently created')" :path="mdiPlus" />
</template>
<script lang="ts">
import { mdiPlus } from '@mdi/js'
import { t } from '@nextcloud/l10n'
import { defineComponent } from 'vue'
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
/**
* A recently created icon to be used for overlaying recently created entries like the file preview / icon
* It has a stroke around the icon to ensure enough contrast for accessibility.
*
* If the background has a hover state you might want to also apply it to the stroke like this:
* ```scss
* .parent:hover :deep(.recently-created-marker-icon svg path) {
* stroke: var(--color-background-hover);
* }
* ```
*/
export default defineComponent({
name: 'RecentlyCreatedIcon',
components: {
NcIconSvgWrapper,
},
setup() {
return {
mdiPlus,
}
},
methods: {
t,
},
})
</script>
<style lang="scss" scoped>
.recently-created-marker-icon {
color: var(--color-element-success);
// Override NcIconSvgWrapper defaults (clickable area)
min-width: unset !important;
min-height: unset !important;
:deep() {
svg {
// We added a stroke for a11y so we must increase the size to include the stroke
width: 20px !important;
height: 20px !important;
// Override NcIconSvgWrapper defaults of 20px
max-width: unset !important;
max-height: unset !important;
// Show a border around the icon for better contrast
path {
stroke: var(--color-main-background);
stroke-width: 8px;
stroke-linejoin: round;
paint-order: stroke;
}
}
}
}
</style>

View file

@ -679,7 +679,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);
}
@ -727,7 +727,8 @@ export default defineComponent({
}
}
&-favorite {
&-favorite,
&-recently-created {
position: absolute;
top: 0px;
inset-inline-end: -10px;
@ -923,8 +924,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;

4
dist/files-main.js vendored

File diff suppressed because one or more lines are too long

View file

@ -52,6 +52,9 @@ This file is generated from multiple sources. Included packages:
- @linusborg/vue-simple-portal
- version: 0.1.5
- license: Apache-2.0
- @mdi/js
- version: 7.4.47
- license: Apache-2.0
- @mdi/svg
- version: 7.4.47
- license: Apache-2.0

File diff suppressed because one or more lines are too long

View file

@ -232,6 +232,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;

View file

@ -151,7 +151,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);

View file

@ -63,6 +63,7 @@ class SearchBuilder {
'share_with' => 'string',
'share_type' => 'integer',
'owner' => 'string',
'creation_time' => 'integer',
'upload_time' => 'integer',
];
@ -257,6 +258,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'],
];

View file

@ -174,6 +174,7 @@ class Folder extends Node implements \OCP\Files\Folder {
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;
}