mirror of
https://github.com/nextcloud/server.git
synced 2026-06-11 01:30:50 -04:00
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
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:
commit
df1f5c7cde
14 changed files with 137 additions and 10 deletions
|
|
@ -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,
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
71
apps/files/src/components/FileEntry/RecentlyCreatedIcon.vue
Normal file
71
apps/files/src/components/FileEntry/RecentlyCreatedIcon.vue
Normal 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>
|
||||
|
|
@ -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
4
dist/files-main.js
vendored
File diff suppressed because one or more lines are too long
3
dist/files-main.js.license
vendored
3
dist/files-main.js.license
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
dist/files-main.js.map
vendored
2
dist/files-main.js.map
vendored
File diff suppressed because one or more lines are too long
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue