Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 2.11.0-privacyPolicy
  • APIv2
  • Docs/Setup
  • Experiment/fix-debugging
  • Experimental/Heinrichs-cypress
  • Feature/xxxx-turnOffDataPub
  • Fix/xxxx-ToS400Error
  • Fix/xxxx-migrateLogin
  • Fix/xxxx-tokenUploadButton
  • Hotfix/0038-correctDownload
  • Hotfix/1917-PublicFilesVisibility
  • Hotfix/1963-fixOrganizationField
  • Hotfix/2015-PublicFilesVisibility
  • Hotfix/2130-uiv2ContactChange
  • Hotfix/2144-invitationCall
  • Hotfix/2150-fixUpload
  • Hotfix/2160-userOrgsInst
  • Hotfix/2190-requiredFieldsForUserProfile
  • Hotfix/2196-RCVTableTranslation
  • Hotfix/2212-fixFiles
  • Hotfix/2226-userProfileSaveButton
  • Hotfix/2232-dependencyFix
  • Hotfix/2233-fixMe
  • Hotfix/2258-saveButtonWorksAsExpected
  • Hotfix/2296-selectedValuesNotReturned
  • Hotfix/2308-defaultLicense
  • Hotfix/2335-fixingSearchRCV
  • Hotfix/2353-dropShape
  • Hotfix/2370-fixDeleteButton
  • Hotfix/2378-linkedFix
  • Hotfix/2379-filesDragAndDrop
  • Hotfix/2382-guestStillBuggy
  • Hotfix/2384-guestsAndLinked
  • Hotfix/2427-adminTrouble
  • Hotfix/2459-EncodingPath
  • Hotfix/2465-orcidLink
  • Hotfix/2465-orcidLink-v1.25.1
  • Hotfix/2504-formGen
  • Hotfix/2541-resCreate
  • Hotfix/2601-correctMetadataIdentity
  • Hotfix/2611-feedback
  • Hotfix/2618-turtle
  • Hotfix/2681-validationErrors
  • Hotfix/2684-correctEncoding
  • Hotfix/2684-fixSubMetadata
  • Hotfix/2713-validateEntryName
  • Hotfix/2734-allowEmptyLicense
  • Hotfix/2765-encodingAgain
  • Hotfix/2852-adaptTextForToSUi
  • Hotfix/2853-optimizationV4
  • Hotfix/2943-reloadingResources
  • Hotfix/2943-searchHighlighting
  • Hotfix/2957-styleAndUpgrade
  • Hotfix/2971-fixTextInDataPub
  • Hotfix/2989-cookieLength
  • Hotfix/662-keepSidebarExpanded
  • Hotfix/xxxx-correctLinking
  • Hotfix/xxxx-folderRecursive
  • Hotfix/xxxx-fullscreenCss
  • Hotfix/xxxx-homepageDisplay
  • Hotfix/xxxx-liveReleaseFixes
  • Hotfix/xxxx-partnerProjects
  • Hotfix/xxxx-workingFileIndex
  • Issue/1782-structualDataIntegration
  • Issue/1792-newMetadataStructure
  • Issue/1822-coscineUIv2App
  • Issue/1824-componentsUIv2
  • Issue/1824-routerAdditions
  • Issue/1825-codeQualityPipelines
  • Issue/1833-newLogin
  • Issue/1843-multipleFilesValidation
  • Issue/1860-searchScoping
  • Issue/1861-searchMetadata
  • Issue/1862-searchFacets
  • Issue/1863-paginationForSearch
  • Issue/1926-userProfile
  • Issue/1927-projectAppMigration
  • Issue/1928-sidebarmenuAddition
  • Issue/1929-vuexToPinia
  • Issue/1938-internalHandling
  • Issue/1951-quotaImplementation
  • Issue/1953-owlImports
  • Issue/1957-resourceAppMigration
  • Issue/1957-resourceAppMigrationNew
  • Issue/1962-SearchAppUI2
  • Issue/1964-tokenExpiryUIv2
  • Issue/1965-userListMigration
  • Issue/1970-breadcrumbs
  • Issue/1971-projectEditCreateMigration
  • Issue/1972-homeDepot
  • Issue/1974-shibbolethLogout
  • Issue/1976-resouceCreationVaildEmail
  • Issue/1979-supportAdminUIv2Migration
  • Issue/1980-userManagement
  • Issue/1985-adaptSidebar
  • Issue/2002-migrateResourceCreate
  • Issue/2003-resourceSettings
  • Issue/2008-quotaManagement
  • Issue/2011-pathConfig
  • Issue/2016-BannerMigration
  • 1.28.0-pilot
  • v1.0.0
  • v1.1.0
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.10.3
  • v1.11.0
  • v1.11.1
  • v1.11.2
  • v1.11.3
  • v1.11.4
  • v1.11.5
  • v1.11.6
  • v1.11.7
  • v1.12.0
  • v1.13.0
  • v1.14.0
  • v1.14.1
  • v1.14.2
  • v1.14.3
  • v1.15.0
  • v1.15.1
  • v1.16.0
  • v1.16.1
  • v1.16.2
  • v1.16.3
  • v1.17.0
  • v1.17.1
  • v1.17.2
  • v1.18.0
  • v1.18.1
  • v1.19.0
  • v1.2.0
  • v1.20.0
  • v1.20.1
  • v1.20.2
  • v1.20.3
  • v1.20.4
  • v1.20.5
  • v1.21.0
  • v1.22.0
  • v1.22.1
  • v1.22.2
  • v1.23.0
  • v1.23.1
  • v1.23.2
  • v1.23.3
  • v1.23.4
  • v1.23.5
  • v1.23.6
  • v1.23.6-patch-2417-2427
  • v1.24.0
  • v1.24.1
  • v1.25.0
  • v1.25.1
  • v1.26.0
  • v1.26.1
  • v1.27.0
  • v1.27.1
  • v1.27.1-pilot
  • v1.28.0
  • v1.29.0
  • v1.29.1
  • v1.29.2
  • v1.3.0
  • v1.30.0
  • v1.30.1
  • v1.30.2
  • v1.31.0
  • v1.32.0
  • v1.4.0
  • v1.4.1
  • v1.5.0
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.7.0
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.9.0
  • v2.0.0
  • v2.1.0
  • v2.10.0
  • v2.10.1
  • v2.11.0
  • v2.12.0
  • v2.12.1
  • v2.12.2
  • v2.12.3
  • v2.12.4
  • v2.12.5
  • v2.13.0
  • v2.13.1
  • v2.13.2
  • v2.13.3
  • v2.13.4
  • v2.14.0
  • v2.15.0
200 results

Target

Select target project
  • coscine/frontend/apps/ui
1 result
Select Git revision
  • 2.11.0-privacyPolicy
  • APIv2
  • Docs/Setup
  • Experiment/fix-debugging
  • Experimental/Heinrichs-cypress
  • Feature/xxxx-turnOffDataPub
  • Fix/xxxx-ToS400Error
  • Fix/xxxx-migrateLogin
  • Fix/xxxx-tokenUploadButton
  • Hotfix/0038-correctDownload
  • Hotfix/1917-PublicFilesVisibility
  • Hotfix/1963-fixOrganizationField
  • Hotfix/2015-PublicFilesVisibility
  • Hotfix/2130-uiv2ContactChange
  • Hotfix/2144-invitationCall
  • Hotfix/2150-fixUpload
  • Hotfix/2160-userOrgsInst
  • Hotfix/2190-requiredFieldsForUserProfile
  • Hotfix/2196-RCVTableTranslation
  • Hotfix/2212-fixFiles
  • Hotfix/2226-userProfileSaveButton
  • Hotfix/2232-dependencyFix
  • Hotfix/2233-fixMe
  • Hotfix/2258-saveButtonWorksAsExpected
  • Hotfix/2296-selectedValuesNotReturned
  • Hotfix/2308-defaultLicense
  • Hotfix/2335-fixingSearchRCV
  • Hotfix/2353-dropShape
  • Hotfix/2370-fixDeleteButton
  • Hotfix/2378-linkedFix
  • Hotfix/2379-filesDragAndDrop
  • Hotfix/2382-guestStillBuggy
  • Hotfix/2384-guestsAndLinked
  • Hotfix/2427-adminTrouble
  • Hotfix/2459-EncodingPath
  • Hotfix/2465-orcidLink
  • Hotfix/2465-orcidLink-v1.25.1
  • Hotfix/2504-formGen
  • Hotfix/2541-resCreate
  • Hotfix/2601-correctMetadataIdentity
  • Hotfix/2611-feedback
  • Hotfix/2618-turtle
  • Hotfix/2681-validationErrors
  • Hotfix/2684-correctEncoding
  • Hotfix/2684-fixSubMetadata
  • Hotfix/2713-validateEntryName
  • Hotfix/2734-allowEmptyLicense
  • Hotfix/2765-encodingAgain
  • Hotfix/2852-adaptTextForToSUi
  • Hotfix/2853-optimizationV4
  • Hotfix/2943-reloadingResources
  • Hotfix/2943-searchHighlighting
  • Hotfix/2957-styleAndUpgrade
  • Hotfix/2971-fixTextInDataPub
  • Hotfix/2989-cookieLength
  • Hotfix/662-keepSidebarExpanded
  • Hotfix/xxxx-correctLinking
  • Hotfix/xxxx-folderRecursive
  • Hotfix/xxxx-fullscreenCss
  • Hotfix/xxxx-homepageDisplay
  • Hotfix/xxxx-liveReleaseFixes
  • Hotfix/xxxx-partnerProjects
  • Hotfix/xxxx-workingFileIndex
  • Issue/1782-structualDataIntegration
  • Issue/1792-newMetadataStructure
  • Issue/1822-coscineUIv2App
  • Issue/1824-componentsUIv2
  • Issue/1824-routerAdditions
  • Issue/1825-codeQualityPipelines
  • Issue/1833-newLogin
  • Issue/1843-multipleFilesValidation
  • Issue/1860-searchScoping
  • Issue/1861-searchMetadata
  • Issue/1862-searchFacets
  • Issue/1863-paginationForSearch
  • Issue/1926-userProfile
  • Issue/1927-projectAppMigration
  • Issue/1928-sidebarmenuAddition
  • Issue/1929-vuexToPinia
  • Issue/1938-internalHandling
  • Issue/1951-quotaImplementation
  • Issue/1953-owlImports
  • Issue/1957-resourceAppMigration
  • Issue/1957-resourceAppMigrationNew
  • Issue/1962-SearchAppUI2
  • Issue/1964-tokenExpiryUIv2
  • Issue/1965-userListMigration
  • Issue/1970-breadcrumbs
  • Issue/1971-projectEditCreateMigration
  • Issue/1972-homeDepot
  • Issue/1974-shibbolethLogout
  • Issue/1976-resouceCreationVaildEmail
  • Issue/1979-supportAdminUIv2Migration
  • Issue/1980-userManagement
  • Issue/1985-adaptSidebar
  • Issue/2002-migrateResourceCreate
  • Issue/2003-resourceSettings
  • Issue/2008-quotaManagement
  • Issue/2011-pathConfig
  • Issue/2016-BannerMigration
  • 1.28.0-pilot
  • v1.0.0
  • v1.1.0
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.10.3
  • v1.11.0
  • v1.11.1
  • v1.11.2
  • v1.11.3
  • v1.11.4
  • v1.11.5
  • v1.11.6
  • v1.11.7
  • v1.12.0
  • v1.13.0
  • v1.14.0
  • v1.14.1
  • v1.14.2
  • v1.14.3
  • v1.15.0
  • v1.15.1
  • v1.16.0
  • v1.16.1
  • v1.16.2
  • v1.16.3
  • v1.17.0
  • v1.17.1
  • v1.17.2
  • v1.18.0
  • v1.18.1
  • v1.19.0
  • v1.2.0
  • v1.20.0
  • v1.20.1
  • v1.20.2
  • v1.20.3
  • v1.20.4
  • v1.20.5
  • v1.21.0
  • v1.22.0
  • v1.22.1
  • v1.22.2
  • v1.23.0
  • v1.23.1
  • v1.23.2
  • v1.23.3
  • v1.23.4
  • v1.23.5
  • v1.23.6
  • v1.23.6-patch-2417-2427
  • v1.24.0
  • v1.24.1
  • v1.25.0
  • v1.25.1
  • v1.26.0
  • v1.26.1
  • v1.27.0
  • v1.27.1
  • v1.27.1-pilot
  • v1.28.0
  • v1.29.0
  • v1.29.1
  • v1.29.2
  • v1.3.0
  • v1.30.0
  • v1.30.1
  • v1.30.2
  • v1.31.0
  • v1.32.0
  • v1.4.0
  • v1.4.1
  • v1.5.0
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.7.0
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.9.0
  • v2.0.0
  • v2.1.0
  • v2.10.0
  • v2.10.1
  • v2.11.0
  • v2.12.0
  • v2.12.1
  • v2.12.2
  • v2.12.3
  • v2.12.4
  • v2.12.5
  • v2.13.0
  • v2.13.1
  • v2.13.2
  • v2.13.3
  • v2.13.4
  • v2.14.0
  • v2.15.0
200 results
Show changes
Commits on Source (11)
Showing
with 235 additions and 59 deletions
{
"name": "ui",
"version": "2.13.4",
"version": "2.14.0",
"scripts": {
"dev": "vite",
"build": "vite build",
......
......@@ -240,6 +240,7 @@
<b-form-input
:id="'resourceQuotaSlider-' + data.item.resource.id"
v-model="data.item.reserved.value"
class="resourceQuotaSlider"
type="range"
:min="minGiB(data.item.resource.id)"
:max="maxGiB(data.item.resource.id)"
......@@ -627,7 +628,7 @@ export default defineComponent({
.list-group {
width: 20%;
}
#resourceQuotaSlider {
.resourceQuotaSlider {
width: calc(100% - 8em);
padding-top: 0.6rem;
}
......
......@@ -187,8 +187,18 @@
track-by="displayName"
:show-labels="false"
:placeholder="$t('form.project.projectOrganization')"
@search-change="retrieveOrganizations"
@search-change="retrieveMoreOrganizations"
>
<template slot="afterList">
<div
v-if="organizations.length && !organizationsComplete"
v-observe-visibility="retrieveMoreOrganizations"
>
<div class="d-flex justify-content-left my-3 pl-3">
<b-spinner v-if="isLoadingOrganizations" small />
</div>
</div>
</template>
<template #noOptions>
{{ $t("form.project.projectOrganizationNoOptions") }}
</template>
......@@ -320,6 +330,7 @@ import {
} from "@/mapping/project";
import moment from "moment";
import "@/plugins/deprecated/vue-multiselect";
import "@/plugins/vue-observe-visibility";
// import the store for current module
import useProjectStore from "../../store";
......@@ -407,6 +418,9 @@ export default defineComponent({
this.projectStore.defaultOrganizations
);
},
organizationsComplete(): boolean {
return this.projectStore.organizationsComplete;
},
visibilities(): VisibilityDto[] | null {
return this.projectStore.visibilities;
},
......@@ -498,23 +512,14 @@ export default defineComponent({
this.v$.projectForManipulation.keywords?.$touch();
},
async retrieveOrganizations(search: string) {
async retrieveMoreOrganizations(search: string | boolean) {
clearTimeout(this.queryTimer);
// Replace the list of available organizations
// with the default one after the search field was cleared
if (search.trim() === "") {
// See computed property organizations' definition
this.projectStore.organizations = null;
}
// Fetch organizations based on the search query
else {
// Add delay to API organization retrieval
this.queryTimer = window.setTimeout(async () => {
this.isLoadingOrganizations = true;
await this.projectStore.retrieveOrganizations(search);
this.isLoadingOrganizations = false;
}, 1000);
}
// Add delay to API organization retrieval
this.queryTimer = window.setTimeout(async () => {
this.isLoadingOrganizations = true;
await this.projectStore.retrieveMoreOrganizations(search);
this.isLoadingOrganizations = false;
}, 300);
},
copyMetadataFromParent() {
......
......@@ -69,6 +69,8 @@ export const useProjectStore = defineStore({
disciplines: null,
licenses: null,
organizations: null,
organizationsComplete: false,
organizationsFilter: "",
roles: null,
visibilities: null,
}),
......@@ -512,17 +514,32 @@ export const useProjectStore = defineStore({
}
},
async retrieveOrganizations(filter = "") {
async retrieveMoreOrganizations(filter: string | boolean) {
const notificationStore = useNotificationStore();
try {
const organizationDtos = await wrapListRequest((pageNumber: number) =>
OrganizationApi.getOrganizations({
searchTerm: filter,
pageNumber,
pageSize: 50,
}),
);
this.organizations = organizationDtos;
if (typeof filter === "boolean") {
filter = this.organizationsFilter;
}
if (this.organizationsComplete && this.organizationsFilter === filter) {
return;
}
if (this.organizationsFilter !== filter) {
this.organizations = [];
this.organizationsFilter = filter;
}
if (this.organizations === null) {
this.organizations = [];
}
const organizationResponse = await OrganizationApi.getOrganizations({
searchTerm: filter,
pageSize: 10,
pageNumber: Math.trunc(this.organizations.length / 10) + 1,
});
if (organizationResponse.data.data) {
this.organizationsComplete =
organizationResponse.data.pagination?.hasNext === false;
this.organizations.push(...organizationResponse.data.data);
}
} catch (error) {
// Handle other Status Codes
notificationStore.postApiErrorNotification(error as AxiosError);
......
......@@ -40,6 +40,8 @@ export interface ProjectState {
disciplines: DisciplineDto[] | null;
licenses: LicenseDto[] | null;
organizations: OrganizationDto[] | null;
organizationsComplete: boolean;
organizationsFilter: string;
roles: RoleDto[] | null;
visibilities: VisibilityDto[] | null;
}
......@@ -109,12 +109,12 @@
/>
<!-- Icon -->
<b-icon
:icon="row.item.isFolder ? 'folder' : 'file-earmark'"
:icon="row.item.type === 'Tree' ? 'folder' : 'file-earmark'"
class="ml-1"
/>
</span>
<a
v-if="!editableDataUrl || row.item.isFolder"
v-if="!editableDataUrl || row.item.type === 'Tree'"
class="fileViewEntry"
@click="triggerNavigation(row.item)"
>
......@@ -139,7 +139,7 @@
<!-- Option "Download" -->
<b-dropdown-item
v-if="!editableDataUrl && !row.item.isFolder"
v-if="!editableDataUrl && row.item.type === 'Leaf'"
@click="openFile(row.item)"
>
{{ $t("page.resource.metadataManagerBtnDownload") }}
......
......@@ -13,6 +13,7 @@
@downloadExtractedMetadata="downloadExtractedMetadata"
@downloadMetadata="downloadMetadata"
@selectFiles="selectFiles"
@selectFolders="selectFolders"
@showModalDeleteFolderContents="showModalDeleteFolderContents"
/>
......@@ -248,6 +249,7 @@ export default defineComponent({
emits: {
clickFileSelect: () => true,
clickFolderSelect: () => true,
emptyFileLists: (_: boolean) => true,
isUploading: (_: boolean) => true,
navigateTree: () => true,
......@@ -399,7 +401,8 @@ export default defineComponent({
return (
this.isGuest ||
this.resource?.archived ||
this.currentVersion !== this.currentFolderContent?.latestVersion ||
(this.currentFileId !== -1 &&
this.currentVersion !== this.currentFolderContent?.latestVersion) ||
this.loadingVersionMetadata
);
},
......@@ -441,7 +444,8 @@ export default defineComponent({
!this.valid ||
this.isUploading ||
this.currentView === "Extracted" ||
this.currentFolderContent?.latestVersion !== this.currentVersion ||
(this.currentFileId !== -1 &&
this.currentVersion !== this.currentFolderContent?.latestVersion) ||
(this.currentFolderContent?.type === "Leaf" &&
((this.editableDataUrl && !this.currentFolderContent.dataUrl) ||
(this.editableKey && !this.currentFolderContent.name)))
......@@ -472,17 +476,20 @@ export default defineComponent({
watch: {
currentVersion() {
this.setExtractedMetadata();
if (this.fileListUpload.length === 0) {
this.setExtractedMetadata();
}
},
currentViewedMetadata() {
this.currentUsedMetadata = this.currentViewedMetadata;
},
fileListUpload() {
this.currentView = "Metadata";
this.getOptions();
},
fileListEdit(newVal, oldVal) {
fileListEdit(newVal: FolderContent[], oldVal: FolderContent[]) {
// Trigger only the value really changed. Otherwise double API calls are triggered.
if (newVal !== oldVal) {
if (newVal !== oldVal && newVal.length !== 0) {
this.getOptions();
this.setExtractedMetadata();
}
......@@ -737,7 +744,7 @@ export default defineComponent({
for (const currentFile of this.shownFiles) {
if (
(this.uploadDuplicates ||
this.folderContents.find((x) => x.name === currentFile.name) ===
this.folderContents.find((x) => x.path === currentFile.path) ===
undefined) &&
currentFile.type === "Leaf"
) {
......@@ -777,7 +784,7 @@ export default defineComponent({
this.uploadFileListReplaceFiles = [];
for (const fileToUpload of this.fileListUpload) {
if (
this.folderContents.find((x) => x.name === fileToUpload.name) ===
this.folderContents.find((x) => x.path === fileToUpload.path) ===
undefined
) {
this.uploadFileListNewFiles.push(fileToUpload);
......@@ -901,7 +908,7 @@ export default defineComponent({
async handleUploadContent(contents: File, file: FileInformation) {
if (this.project?.id && this.resource?.id) {
const fileExists = this.folderContents.some(
(x) => x.name === file.name,
(x) => x.path === file.path,
);
this.progressStatus = 0;
......@@ -936,7 +943,7 @@ export default defineComponent({
}
// Update or emit the updated folder contents
const entry = this.folderContents.find((x) => x.name === file.name);
const entry = this.folderContents.find((x) => x.path === file.path);
if (entry === undefined) {
const identifier = uuidv4();
const version = +new Date();
......@@ -1036,6 +1043,13 @@ export default defineComponent({
this.$emit("clickFileSelect");
},
/**
* Emits an event signaling the selection of folder.
*/
selectFolders() {
this.$emit("clickFolderSelect");
},
/**
* Retrieves the current extracted metadata.
*
......@@ -1139,7 +1153,7 @@ export default defineComponent({
}
// Find the corresponding file entry from folder contents
const tmp = this.folderContents.find((x) => x.name === file.name);
const tmp = this.folderContents.find((x) => x.path === file.path);
// If the file has a data URL and is editable, handle its content upload
if (
......
<template>
<b-row id="metadataManagerButtonRowTop" align-h="between">
<b-col>
<!-- Select Files Button -->
<span id="buttonSelectFilesWrapper">
<!-- Input Group with Upload button -->
<b-input-group id="metadataManagerUploadMenu">
<b-dropdown
id="buttonSelectFoldersWrapper"
size="sm"
left
:disabled="uploadDisabled || editableDataUrl"
>
<b-dropdown-item
id="buttonSelectFolder"
@click="$emit('selectFolders')"
>
{{ $t("page.resource.metadataManagerBtnSelectFolders") }}
</b-dropdown-item>
</b-dropdown>
<!-- Select Files Button -->
<b-button
v-if="!isGuest"
id="buttonSelectFiles"
variant="secondary"
:placeholder="$t('page.resource.metadataManagerBtnSelectFiles')"
:disabled="uploadDisabled"
autofocus
@click="$emit('selectFiles')"
>
{{ $t("page.resource.metadataManagerBtnSelectFiles") }}
</b-button>
</span>
</b-input-group>
<b-tooltip
v-if="uploadDisabled && uploadDisabledReason"
target="metadataManagerUploadMenu"
triggers="hover"
placement="bottom"
boundary="viewport"
>
{{ uploadDisabledReason }}
</b-tooltip>
<b-tooltip
v-if="uploadDisabled && uploadDisabledReason"
target="buttonSelectFilesWrapper"
target="buttonSelectFoldersWrapper"
triggers="hover"
placement="bottom"
boundary="viewport"
......@@ -201,6 +223,13 @@ export default defineComponent({
#metadataManagerButtonRowTop {
margin-bottom: 5px;
}
#metadataManagerUploadMenu {
margin-right: 5px;
width: auto;
}
#metadataManagerUploadMenu button {
border-radius: 0px;
}
#metadataManagerDropDownMenu {
margin-right: 5px;
width: auto;
......
......@@ -17,7 +17,7 @@
:pressed="false"
variant="outline-secondary"
@click="$emit('changeMetadata', index)"
>{{ item.name
>{{ item.path
}}<b-spinner
v-show="item.type === 'Leaf' && item.uploading"
label="Spinning"
......
......@@ -127,6 +127,7 @@ export default {
metadataManagerBtnUpload: "Hochladen",
metadataManagerBtnSelectFiles: "Dateien auswählen",
metadataManagerBtnSelectFolders: "Ordner auswählen",
metadataManagerBtnCantUploadArchived:
"Uploads sind deaktiviert, weil die aktuelle Ressource archiviert ist.",
......@@ -441,6 +442,7 @@ export default {
metadataManagerBtnDownload: "Öffnen",
metadataManagerBtnUpload: "Speichern",
metadataManagerBtnSelectFiles: "Neuer Eintrag",
metadataManagerBtnSelectFolders: "Neuer Ordner",
infoFileType: "Eintrag",
infoFileTypeFolder: "Ordner",
......
......@@ -125,6 +125,7 @@ export default {
metadataManagerBtnUpload: "Upload",
metadataManagerBtnSelectFiles: "Select Files",
metadataManagerBtnSelectFolders: "Select Folders",
metadataManagerBtnCantUploadArchived:
"Upload disabled because the current resource is archived.",
......@@ -431,6 +432,7 @@ export default {
metadataManagerBtnDownload: "Open",
metadataManagerBtnUpload: "Save",
metadataManagerBtnSelectFiles: "New Entry",
metadataManagerBtnSelectFolders: "New folder",
infoFileType: "Entry Type",
infoFileTypeFolder: "Folder",
......
......@@ -29,6 +29,17 @@
@input="fileListUploadSelected"
/>
<!-- Form File Window -->
<b-form-file
ref="folderTrigger"
multiple
class="mt-3"
plain
directory
no-traverse
@input="fileListUploadSelected"
/>
<!-- Files View Column -->
<span id="filesViewSpan">
<div
......@@ -90,6 +101,7 @@
@isUploading="setIsUploading"
@showModalDelete="showModalDelete"
@clickFileSelect="clickFileSelect"
@clickFolderSelect="clickFolderSelect"
/>
<!-- Toggle Fullscreen Button -->
......@@ -141,6 +153,7 @@ import MetadataManager from "../components/resource-page/MetadataManager.vue";
import OversizedFilesModal from "../components/resource-page/modals/OversizedFilesModal.vue";
import DeleteFolderContentsModal from "../components/resource-page/modals/DeleteFolderContentsModal.vue";
import { v4 as uuidv4 } from "uuid";
import { type ExtendedFile, FileUtil } from "@/modules/resource/utils/FileUtil";
import factory from "rdf-ext";
import type { BFormFile, BTable } from "bootstrap-vue";
import type { Dataset } from "@rdfjs/types";
......@@ -277,6 +290,29 @@ export default defineComponent({
}
},
/**
* Trigger folder selection, if not editable data URL is provided.
*/
clickFolderSelect() {
this.showDetail = false;
// Check if editableDataUrl is present
if (
this.resourceTypeInformation?.resourceContent?.metadataView
?.editableDataUrl
) {
this.emptyFileLists();
} else {
// Trigger a click event on fileTrigger element
(this.$refs.folderTrigger as BFormFile).$el.dispatchEvent(
new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: true,
}),
);
}
},
/**
* Display the modal for file deletion and set the files to be deleted.
* @param {FolderContent[]} files - Files to be deleted.
......@@ -356,9 +392,9 @@ export default defineComponent({
/**
* Add selected files to the fileListUpload list.
* @param {File[] | File} selectedFiles - Selected files.
* @param {ExtendedFile[] | ExtendedFile} selectedFiles - Selected files.
*/
fileListUploadSelected(selectedFiles: File[] | File) {
fileListUploadSelected(selectedFiles: ExtendedFile[] | ExtendedFile) {
if (!Array.isArray(selectedFiles)) {
selectedFiles = [selectedFiles];
}
......@@ -369,10 +405,23 @@ export default defineComponent({
const identifier = uuidv4();
const version = +new Date();
const $path = file.$path ? file.$path : null;
// Correctly determine the parentDirectory based on multiple inputs
const parentDirectory = $path
? $path.substring(0, $path.lastIndexOf("/"))
: file.path ?? "";
// Correctly determine the path based on multiple inputs (check for .path or $path if .name still needs to be appended)
let path = file.path ?? $path ?? "";
if (!path.endsWith(file.name)) {
path += file.name;
}
const fileInformation: FileInformation = {
id: identifier,
path: this.dirTrail + file.name,
parentDirectory: this.dirTrail,
path: this.dirTrail + path,
parentDirectory: this.dirTrail + parentDirectory,
type: "Leaf",
uploading: false,
info: file,
......@@ -437,19 +486,17 @@ export default defineComponent({
* Handle file drop action for upload.
* @param {DragEvent} ev - Drag event.
*/
uploadDrop(ev: DragEvent) {
async uploadDrop(ev: DragEvent) {
if (this.fileAddable) {
this.dragCounter = 0;
// Handling file drops
if (ev?.dataTransfer?.items) {
for (const item of ev.dataTransfer.items) {
if (item.kind === "file") {
const file = item.getAsFile();
if (file !== null) {
this.fileListUploadSelected(file);
}
}
const files = await FileUtil.getFilesDataTransferItems(
ev.dataTransfer.items,
);
for (const file of files) {
this.fileListUploadSelected(file);
}
}
}
......
......@@ -4,6 +4,17 @@ import type {
QuotaUnit,
} from "@coscine/api-client/dist/types/Coscine.Api";
export interface ExtendedFile extends File {
/**
* From Bootstrap-Vue (is the full path)
*/
$path?: string;
/**
* Custom (is the folder path)
*/
path?: string;
}
export class FileUtil {
public static formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) {
......@@ -42,6 +53,52 @@ export class FileUtil {
return input.value * Math.pow(k, input_exponent - output_exponent);
}
public static getFilesDataTransferItems(
dataTransferItems: DataTransferItemList,
): Promise<ExtendedFile[]> {
function traverseFileTreePromise(
item: FileSystemEntry | null,
path = "",
folder: ExtendedFile[],
) {
return new Promise((resolve) => {
if (item?.isFile) {
(item as FileSystemFileEntry).file((file) => {
(file as ExtendedFile).path = path || "" + file.name; //save full path
folder.push(file as ExtendedFile);
resolve(file);
});
} else if (item?.isDirectory) {
const dirReader = (item as FileSystemDirectoryEntry).createReader();
dirReader.readEntries((entries) => {
const entriesPromises = [];
for (const entr of entries)
entriesPromises.push(
traverseFileTreePromise(
entr,
path || "" + item.name + "/",
folder,
),
);
resolve(Promise.all(entriesPromises));
});
}
});
}
const files: ExtendedFile[] = [];
return new Promise((resolve, _) => {
const entriesPromises = [];
for (const it of dataTransferItems)
entriesPromises.push(
traverseFileTreePromise(it.webkitGetAsEntry(), "", files),
);
Promise.all(entriesPromises).then((_) => {
resolve(files);
});
});
}
}
/**@deprecated Unfortunately that is a workaround, since we can't directly use the QuotaUnit Enum */
......