mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-30 10:29:42 +00:00
eslint fixes
This commit is contained in:
@@ -1,28 +1,28 @@
|
||||
<template>
|
||||
<a :class="props.classes" @click="download_and_open($event)" ref="btn">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
Télécharger en pdf
|
||||
</a>
|
||||
<a :class="props.classes" @click="download_and_open($event)" ref="btn">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
Télécharger en pdf
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
build_convert_link,
|
||||
download_and_decrypt_doc,
|
||||
download_doc,
|
||||
build_convert_link,
|
||||
download_and_decrypt_doc,
|
||||
download_doc,
|
||||
} from "./helpers";
|
||||
import mime from "mime";
|
||||
import { reactive, ref } from "vue";
|
||||
import { StoredObject } from "../../types";
|
||||
|
||||
interface ConvertButtonConfig {
|
||||
storedObject: StoredObject;
|
||||
classes: Record<string, boolean>;
|
||||
filename?: string;
|
||||
storedObject: StoredObject;
|
||||
classes: Record<string, boolean>;
|
||||
filename?: string;
|
||||
}
|
||||
|
||||
interface DownloadButtonState {
|
||||
content: null | string;
|
||||
content: null | string;
|
||||
}
|
||||
|
||||
const props = defineProps<ConvertButtonConfig>();
|
||||
@@ -30,36 +30,34 @@ const state: DownloadButtonState = reactive({ content: null });
|
||||
const btn = ref<HTMLAnchorElement | null>(null);
|
||||
|
||||
async function download_and_open(event: Event): Promise<void> {
|
||||
const button = event.target as HTMLAnchorElement;
|
||||
const button = event.target as HTMLAnchorElement;
|
||||
|
||||
if (null === state.content) {
|
||||
event.preventDefault();
|
||||
if (null === state.content) {
|
||||
event.preventDefault();
|
||||
|
||||
const raw = await download_doc(
|
||||
build_convert_link(props.storedObject.uuid),
|
||||
);
|
||||
state.content = window.URL.createObjectURL(raw);
|
||||
const raw = await download_doc(build_convert_link(props.storedObject.uuid));
|
||||
state.content = window.URL.createObjectURL(raw);
|
||||
|
||||
button.href = window.URL.createObjectURL(raw);
|
||||
button.type = "application/pdf";
|
||||
button.href = window.URL.createObjectURL(raw);
|
||||
button.type = "application/pdf";
|
||||
|
||||
button.download = props.filename + ".pdf" || "document.pdf";
|
||||
}
|
||||
button.download = props.filename + ".pdf" || "document.pdf";
|
||||
}
|
||||
|
||||
button.click();
|
||||
const reset_pending = setTimeout(reset_state, 45000);
|
||||
button.click();
|
||||
const reset_pending = setTimeout(reset_state, 45000);
|
||||
}
|
||||
|
||||
function reset_state(): void {
|
||||
state.content = null;
|
||||
btn.value?.removeAttribute("download");
|
||||
btn.value?.removeAttribute("href");
|
||||
btn.value?.removeAttribute("type");
|
||||
state.content = null;
|
||||
btn.value?.removeAttribute("download");
|
||||
btn.value?.removeAttribute("href");
|
||||
btn.value?.removeAttribute("type");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
@@ -3,13 +3,13 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import { computed, reactive } from "vue";
|
||||
|
||||
export interface DesktopEditButtonConfig {
|
||||
editLink: null;
|
||||
classes: Record<string, boolean>;
|
||||
expirationLink: number | Date;
|
||||
editLink: null;
|
||||
classes: Record<string, boolean>;
|
||||
expirationLink: number | Date;
|
||||
}
|
||||
|
||||
interface DesktopEditButtonState {
|
||||
modalOpened: boolean;
|
||||
modalOpened: boolean;
|
||||
}
|
||||
|
||||
const state: DesktopEditButtonState = reactive({ modalOpened: false });
|
||||
@@ -17,80 +17,76 @@ const state: DesktopEditButtonState = reactive({ modalOpened: false });
|
||||
const props = defineProps<DesktopEditButtonConfig>();
|
||||
|
||||
const buildCommand = computed<string>(
|
||||
() => "vnd.libreoffice.command:ofe|u|" + props.editLink,
|
||||
() => "vnd.libreoffice.command:ofe|u|" + props.editLink,
|
||||
);
|
||||
|
||||
const editionUntilFormatted = computed<string>(() => {
|
||||
let d;
|
||||
let d;
|
||||
|
||||
if (props.expirationLink instanceof Date) {
|
||||
d = props.expirationLink;
|
||||
} else {
|
||||
d = new Date(props.expirationLink * 1000);
|
||||
}
|
||||
console.log(props.expirationLink);
|
||||
if (props.expirationLink instanceof Date) {
|
||||
d = props.expirationLink;
|
||||
} else {
|
||||
d = new Date(props.expirationLink * 1000);
|
||||
}
|
||||
console.log(props.expirationLink);
|
||||
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: "long",
|
||||
timeStyle: "medium",
|
||||
}).format(d);
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: "long",
|
||||
timeStyle: "medium",
|
||||
}).format(d);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<teleport to="body">
|
||||
<modal v-if="state.modalOpened" @close="state.modalOpened = false">
|
||||
<template v-slot:body>
|
||||
<div class="desktop-edit">
|
||||
<p class="center">
|
||||
Veuillez enregistrer vos modifications avant le
|
||||
</p>
|
||||
<p>
|
||||
<strong>{{ editionUntilFormatted }}</strong>
|
||||
</p>
|
||||
<teleport to="body">
|
||||
<modal v-if="state.modalOpened" @close="state.modalOpened = false">
|
||||
<template v-slot:body>
|
||||
<div class="desktop-edit">
|
||||
<p class="center">Veuillez enregistrer vos modifications avant le</p>
|
||||
<p>
|
||||
<strong>{{ editionUntilFormatted }}</strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a class="btn btn-primary" :href="buildCommand"
|
||||
>Ouvrir le document pour édition</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-primary" :href="buildCommand"
|
||||
>Ouvrir le document pour édition</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<small
|
||||
>Le document peut être édité uniquement en utilisant
|
||||
Libre Office.</small
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<small
|
||||
>Le document peut être édité uniquement en utilisant Libre
|
||||
Office.</small
|
||||
>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<small
|
||||
>En cas d'échec lors de l'enregistrement, sauver le
|
||||
document sur le poste de travail avant de le déposer
|
||||
à nouveau ici.</small
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<small
|
||||
>En cas d'échec lors de l'enregistrement, sauver le document sur
|
||||
le poste de travail avant de le déposer à nouveau ici.</small
|
||||
>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<small
|
||||
>Vous pouvez naviguez sur d'autres pages pendant
|
||||
l'édition.</small
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</modal>
|
||||
</teleport>
|
||||
<a :class="props.classes" @click="state.modalOpened = true">
|
||||
<i class="fa fa-desktop"></i>
|
||||
Éditer sur le bureau
|
||||
</a>
|
||||
<p>
|
||||
<small
|
||||
>Vous pouvez naviguez sur d'autres pages pendant l'édition.</small
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</modal>
|
||||
</teleport>
|
||||
<a :class="props.classes" @click="state.modalOpened = true">
|
||||
<i class="fa fa-desktop"></i>
|
||||
Éditer sur le bureau
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.desktop-edit {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,26 +1,26 @@
|
||||
<template>
|
||||
<a
|
||||
v-if="!state.is_ready"
|
||||
:class="props.classes"
|
||||
@click="download_and_open()"
|
||||
title="Télécharger"
|
||||
>
|
||||
<i class="fa fa-download"></i>
|
||||
<template v-if="displayActionStringInButton">Télécharger</template>
|
||||
</a>
|
||||
<a
|
||||
v-else
|
||||
:class="props.classes"
|
||||
target="_blank"
|
||||
:type="props.atVersion.type"
|
||||
:download="buildDocumentName()"
|
||||
:href="state.href_url"
|
||||
ref="open_button"
|
||||
title="Ouvrir"
|
||||
>
|
||||
<i class="fa fa-external-link"></i>
|
||||
<template v-if="displayActionStringInButton">Ouvrir</template>
|
||||
</a>
|
||||
<a
|
||||
v-if="!state.is_ready"
|
||||
:class="props.classes"
|
||||
@click="download_and_open()"
|
||||
title="Télécharger"
|
||||
>
|
||||
<i class="fa fa-download"></i>
|
||||
<template v-if="displayActionStringInButton">Télécharger</template>
|
||||
</a>
|
||||
<a
|
||||
v-else
|
||||
:class="props.classes"
|
||||
target="_blank"
|
||||
:type="props.atVersion.type"
|
||||
:download="buildDocumentName()"
|
||||
:href="state.href_url"
|
||||
ref="open_button"
|
||||
title="Ouvrir"
|
||||
>
|
||||
<i class="fa fa-external-link"></i>
|
||||
<template v-if="displayActionStringInButton">Ouvrir</template>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -30,112 +30,109 @@ import mime from "mime";
|
||||
import { StoredObject, StoredObjectVersion } from "../../types";
|
||||
|
||||
interface DownloadButtonConfig {
|
||||
storedObject: StoredObject;
|
||||
atVersion: StoredObjectVersion;
|
||||
classes: Record<string, boolean>;
|
||||
filename?: string;
|
||||
/**
|
||||
* if true, display the action string into the button. If false, displays only
|
||||
* the icon
|
||||
*/
|
||||
displayActionStringInButton?: boolean;
|
||||
/**
|
||||
* if true, will download directly the file on load
|
||||
*/
|
||||
directDownload?: boolean;
|
||||
storedObject: StoredObject;
|
||||
atVersion: StoredObjectVersion;
|
||||
classes: Record<string, boolean>;
|
||||
filename?: string;
|
||||
/**
|
||||
* if true, display the action string into the button. If false, displays only
|
||||
* the icon
|
||||
*/
|
||||
displayActionStringInButton?: boolean;
|
||||
/**
|
||||
* if true, will download directly the file on load
|
||||
*/
|
||||
directDownload?: boolean;
|
||||
}
|
||||
|
||||
interface DownloadButtonState {
|
||||
is_ready: boolean;
|
||||
is_running: boolean;
|
||||
href_url: string;
|
||||
is_ready: boolean;
|
||||
is_running: boolean;
|
||||
href_url: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<DownloadButtonConfig>(), {
|
||||
displayActionStringInButton: true,
|
||||
directDownload: false,
|
||||
displayActionStringInButton: true,
|
||||
directDownload: false,
|
||||
});
|
||||
const state: DownloadButtonState = reactive({
|
||||
is_ready: false,
|
||||
is_running: false,
|
||||
href_url: "#",
|
||||
is_ready: false,
|
||||
is_running: false,
|
||||
href_url: "#",
|
||||
});
|
||||
|
||||
const open_button = ref<HTMLAnchorElement | null>(null);
|
||||
|
||||
function buildDocumentName(): string {
|
||||
let document_name = props.filename ?? props.storedObject.title;
|
||||
let document_name = props.filename ?? props.storedObject.title;
|
||||
|
||||
if ("" === document_name || null === document_name) {
|
||||
document_name = "document";
|
||||
}
|
||||
if ("" === document_name || null === document_name) {
|
||||
document_name = "document";
|
||||
}
|
||||
|
||||
const ext = mime.getExtension(props.atVersion.type);
|
||||
const ext = mime.getExtension(props.atVersion.type);
|
||||
|
||||
if (null !== ext) {
|
||||
return document_name + "." + ext;
|
||||
}
|
||||
if (null !== ext) {
|
||||
return document_name + "." + ext;
|
||||
}
|
||||
|
||||
return document_name;
|
||||
return document_name;
|
||||
}
|
||||
|
||||
async function download_and_open(): Promise<void> {
|
||||
if (state.is_running) {
|
||||
console.log("state is running, aborting");
|
||||
return;
|
||||
}
|
||||
if (state.is_running) {
|
||||
console.log("state is running, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
state.is_running = true;
|
||||
state.is_running = true;
|
||||
|
||||
if (state.is_ready) {
|
||||
console.log("state is ready. This should not happens");
|
||||
return;
|
||||
}
|
||||
if (state.is_ready) {
|
||||
console.log("state is ready. This should not happens");
|
||||
return;
|
||||
}
|
||||
|
||||
let raw;
|
||||
let raw;
|
||||
|
||||
try {
|
||||
raw = await download_and_decrypt_doc(
|
||||
props.storedObject,
|
||||
props.atVersion,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("error while downloading and decrypting document");
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
raw = await download_and_decrypt_doc(props.storedObject, props.atVersion);
|
||||
} catch (e) {
|
||||
console.error("error while downloading and decrypting document");
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
state.href_url = window.URL.createObjectURL(raw);
|
||||
state.is_running = false;
|
||||
state.is_ready = true;
|
||||
state.href_url = window.URL.createObjectURL(raw);
|
||||
state.is_running = false;
|
||||
state.is_ready = true;
|
||||
|
||||
if (!props.directDownload) {
|
||||
await nextTick();
|
||||
open_button.value?.click();
|
||||
if (!props.directDownload) {
|
||||
await nextTick();
|
||||
open_button.value?.click();
|
||||
|
||||
console.log("open button should have been clicked");
|
||||
setTimeout(reset_state, 45000);
|
||||
}
|
||||
console.log("open button should have been clicked");
|
||||
setTimeout(reset_state, 45000);
|
||||
}
|
||||
}
|
||||
|
||||
function reset_state(): void {
|
||||
state.href_url = "#";
|
||||
state.is_ready = false;
|
||||
state.is_running = false;
|
||||
state.href_url = "#";
|
||||
state.is_ready = false;
|
||||
state.is_running = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.directDownload) {
|
||||
download_and_open();
|
||||
}
|
||||
if (props.directDownload) {
|
||||
download_and_open();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
i.fa {
|
||||
margin-right: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,20 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import HistoryButtonModal from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue";
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "./../../types";
|
||||
import { computed, reactive, ref, useTemplateRef } from "vue";
|
||||
import { get_versions } from "./HistoryButton/api";
|
||||
|
||||
interface HistoryButtonConfig {
|
||||
storedObject: StoredObject;
|
||||
canEdit: boolean;
|
||||
storedObject: StoredObject;
|
||||
canEdit: boolean;
|
||||
}
|
||||
|
||||
interface HistoryButtonState {
|
||||
versions: StoredObjectVersionWithPointInTime[];
|
||||
loaded: boolean;
|
||||
versions: StoredObjectVersionWithPointInTime[];
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<HistoryButtonConfig>();
|
||||
@@ -22,47 +22,47 @@ const state = reactive<HistoryButtonState>({ versions: [], loaded: false });
|
||||
const modal = useTemplateRef<typeof HistoryButtonModal>("modal");
|
||||
|
||||
const download_version_and_open_modal = async function (): Promise<void> {
|
||||
if (null !== modal.value) {
|
||||
modal.value.open();
|
||||
} else {
|
||||
console.log("modal is null");
|
||||
}
|
||||
if (null !== modal.value) {
|
||||
modal.value.open();
|
||||
} else {
|
||||
console.log("modal is null");
|
||||
}
|
||||
|
||||
if (!state.loaded) {
|
||||
const versions = await get_versions(props.storedObject);
|
||||
if (!state.loaded) {
|
||||
const versions = await get_versions(props.storedObject);
|
||||
|
||||
for (const version of versions) {
|
||||
state.versions.push(version);
|
||||
}
|
||||
state.loaded = true;
|
||||
for (const version of versions) {
|
||||
state.versions.push(version);
|
||||
}
|
||||
state.loaded = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onRestoreVersion = ({
|
||||
newVersion,
|
||||
newVersion,
|
||||
}: {
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
}) => {
|
||||
state.versions.unshift(newVersion);
|
||||
state.versions.unshift(newVersion);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a @click="download_version_and_open_modal" class="dropdown-item">
|
||||
<history-button-modal
|
||||
ref="modal"
|
||||
:versions="state.versions"
|
||||
:stored-object="storedObject"
|
||||
:can-edit="canEdit"
|
||||
@restore-version="onRestoreVersion"
|
||||
></history-button-modal>
|
||||
<i class="fa fa-history"></i>
|
||||
Historique
|
||||
</a>
|
||||
<a @click="download_version_and_open_modal" class="dropdown-item">
|
||||
<history-button-modal
|
||||
ref="modal"
|
||||
:versions="state.versions"
|
||||
:stored-object="storedObject"
|
||||
:can-edit="canEdit"
|
||||
@restore-version="onRestoreVersion"
|
||||
></history-button-modal>
|
||||
<i class="fa fa-history"></i>
|
||||
Historique
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,26 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "./../../../types";
|
||||
import HistoryButtonListItem from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue";
|
||||
import { computed, reactive } from "vue";
|
||||
|
||||
interface HistoryButtonListConfig {
|
||||
versions: StoredObjectVersionWithPointInTime[];
|
||||
storedObject: StoredObject;
|
||||
canEdit: boolean;
|
||||
versions: StoredObjectVersionWithPointInTime[];
|
||||
storedObject: StoredObject;
|
||||
canEdit: boolean;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
}>();
|
||||
|
||||
interface HistoryButtonListState {
|
||||
/**
|
||||
* Contains the number of the newly created version when a version is restored.
|
||||
*/
|
||||
restored: number;
|
||||
/**
|
||||
* Contains the number of the newly created version when a version is restored.
|
||||
*/
|
||||
restored: number;
|
||||
}
|
||||
|
||||
const props = defineProps<HistoryButtonListConfig>();
|
||||
@@ -28,11 +28,11 @@ const props = defineProps<HistoryButtonListConfig>();
|
||||
const state = reactive<HistoryButtonListState>({ restored: -1 });
|
||||
|
||||
const higher_version = computed<number>(() =>
|
||||
props.versions.reduce(
|
||||
(accumulator: number, version: StoredObjectVersionWithPointInTime) =>
|
||||
Math.max(accumulator, version.version),
|
||||
-1,
|
||||
),
|
||||
props.versions.reduce(
|
||||
(accumulator: number, version: StoredObjectVersionWithPointInTime) =>
|
||||
Math.max(accumulator, version.version),
|
||||
-1,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -41,32 +41,32 @@ const higher_version = computed<number>(() =>
|
||||
* internally, keep track of the newly restored version
|
||||
*/
|
||||
const onRestored = ({
|
||||
newVersion,
|
||||
newVersion,
|
||||
}: {
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
}) => {
|
||||
state.restored = newVersion.version;
|
||||
emit("restoreVersion", { newVersion });
|
||||
state.restored = newVersion.version;
|
||||
emit("restoreVersion", { newVersion });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="props.versions.length > 0">
|
||||
<div class="container">
|
||||
<template v-for="v in props.versions" :key="v.id">
|
||||
<history-button-list-item
|
||||
:version="v"
|
||||
:can-edit="canEdit"
|
||||
:is-current="higher_version === v.version"
|
||||
:stored-object="storedObject"
|
||||
@restore-version="onRestored"
|
||||
></history-button-list-item>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>Chargement des versions</p>
|
||||
</template>
|
||||
<template v-if="props.versions.length > 0">
|
||||
<div class="container">
|
||||
<template v-for="v in props.versions" :key="v.id">
|
||||
<history-button-list-item
|
||||
:version="v"
|
||||
:can-edit="canEdit"
|
||||
:is-current="higher_version === v.version"
|
||||
:stored-object="storedObject"
|
||||
@restore-version="onRestored"
|
||||
></history-button-list-item>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>Chargement des versions</p>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectPointInTime,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
StoredObject,
|
||||
StoredObjectPointInTime,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "./../../../types";
|
||||
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
|
||||
import { ISOToDatetime } from "./../../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
||||
@@ -12,185 +12,173 @@ import DownloadButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/Downloa
|
||||
import { computed } from "vue";
|
||||
|
||||
interface HistoryButtonListItemConfig {
|
||||
version: StoredObjectVersionWithPointInTime;
|
||||
storedObject: StoredObject;
|
||||
canEdit: boolean;
|
||||
isCurrent: boolean;
|
||||
version: StoredObjectVersionWithPointInTime;
|
||||
storedObject: StoredObject;
|
||||
canEdit: boolean;
|
||||
isCurrent: boolean;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
}>();
|
||||
|
||||
const props = defineProps<HistoryButtonListItemConfig>();
|
||||
|
||||
const onRestore = ({
|
||||
newVersion,
|
||||
newVersion,
|
||||
}: {
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
}) => {
|
||||
emit("restoreVersion", { newVersion });
|
||||
emit("restoreVersion", { newVersion });
|
||||
};
|
||||
|
||||
const isKeptBeforeConversion = computed<boolean>(() => {
|
||||
if ("point-in-times" in props.version) {
|
||||
return props.version["point-in-times"].reduce(
|
||||
(accumulator: boolean, pit: StoredObjectPointInTime) =>
|
||||
accumulator || "keep-before-conversion" === pit.reason,
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if ("point-in-times" in props.version) {
|
||||
return props.version["point-in-times"].reduce(
|
||||
(accumulator: boolean, pit: StoredObjectPointInTime) =>
|
||||
accumulator || "keep-before-conversion" === pit.reason,
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const isRestored = computed<boolean>(
|
||||
() => props.version.version > 0 && null !== props.version["from-restored"],
|
||||
() => props.version.version > 0 && null !== props.version["from-restored"],
|
||||
);
|
||||
|
||||
const isDuplicated = computed<boolean>(
|
||||
() =>
|
||||
props.version.version === 0 && null !== props.version["from-restored"],
|
||||
() => props.version.version === 0 && null !== props.version["from-restored"],
|
||||
);
|
||||
|
||||
const classes = computed<{
|
||||
row: true;
|
||||
"row-hover": true;
|
||||
"blinking-1": boolean;
|
||||
"blinking-2": boolean;
|
||||
row: true;
|
||||
"row-hover": true;
|
||||
"blinking-1": boolean;
|
||||
"blinking-2": boolean;
|
||||
}>(() => ({
|
||||
row: true,
|
||||
"row-hover": true,
|
||||
"blinking-1": props.isRestored && 0 === props.version.version % 2,
|
||||
"blinking-2": props.isRestored && 1 === props.version.version % 2,
|
||||
row: true,
|
||||
"row-hover": true,
|
||||
"blinking-1": props.isRestored && 0 === props.version.version % 2,
|
||||
"blinking-2": props.isRestored && 1 === props.version.version % 2,
|
||||
}));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="classes">
|
||||
<div
|
||||
class="col-12 tags"
|
||||
v-if="
|
||||
isCurrent ||
|
||||
isKeptBeforeConversion ||
|
||||
isRestored ||
|
||||
isDuplicated
|
||||
"
|
||||
>
|
||||
<span class="badge bg-success" v-if="isCurrent"
|
||||
>Version actuelle</span
|
||||
>
|
||||
<span class="badge bg-info" v-if="isKeptBeforeConversion"
|
||||
>Conservée avant conversion dans un autre format</span
|
||||
>
|
||||
<span class="badge bg-info" v-if="isRestored"
|
||||
>Restaurée depuis la version
|
||||
{{ version["from-restored"]?.version + 1 }}</span
|
||||
>
|
||||
<span class="badge bg-info" v-if="isDuplicated"
|
||||
>Dupliqué depuis un autre document</span
|
||||
>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<file-icon :type="version.type"></file-icon>
|
||||
<span
|
||||
><strong> #{{ version.version + 1 }} </strong></span
|
||||
>
|
||||
<template
|
||||
v-if="version.createdBy !== null && version.createdAt !== null"
|
||||
><strong v-if="version.version == 0">créé par</strong
|
||||
><strong v-else>modifié par</strong>
|
||||
<span class="badge-user"
|
||||
><UserRenderBoxBadge
|
||||
:user="version.createdBy"
|
||||
></UserRenderBoxBadge
|
||||
></span>
|
||||
<strong>à</strong>
|
||||
{{
|
||||
$d(ISOToDatetime(version.createdAt.datetime8601), "long")
|
||||
}}</template
|
||||
><template
|
||||
v-if="version.createdBy === null && version.createdAt !== null"
|
||||
><strong v-if="version.version == 0">Créé le</strong
|
||||
><strong v-else>modifié le</strong>
|
||||
{{
|
||||
$d(ISOToDatetime(version.createdAt.datetime8601), "long")
|
||||
}}</template
|
||||
>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<ul class="record_actions small slim on-version-actions">
|
||||
<li v-if="canEdit && !isCurrent">
|
||||
<restore-version-button
|
||||
:stored-object-version="props.version"
|
||||
@restore-version="onRestore"
|
||||
></restore-version-button>
|
||||
</li>
|
||||
<li>
|
||||
<download-button
|
||||
:stored-object="storedObject"
|
||||
:at-version="version"
|
||||
:classes="{
|
||||
btn: true,
|
||||
'btn-outline-primary': true,
|
||||
'btn-sm': true,
|
||||
}"
|
||||
:display-action-string-in-button="false"
|
||||
></download-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div :class="classes">
|
||||
<div
|
||||
class="col-12 tags"
|
||||
v-if="isCurrent || isKeptBeforeConversion || isRestored || isDuplicated"
|
||||
>
|
||||
<span class="badge bg-success" v-if="isCurrent">Version actuelle</span>
|
||||
<span class="badge bg-info" v-if="isKeptBeforeConversion"
|
||||
>Conservée avant conversion dans un autre format</span
|
||||
>
|
||||
<span class="badge bg-info" v-if="isRestored"
|
||||
>Restaurée depuis la version
|
||||
{{ version["from-restored"]?.version + 1 }}</span
|
||||
>
|
||||
<span class="badge bg-info" v-if="isDuplicated"
|
||||
>Dupliqué depuis un autre document</span
|
||||
>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<file-icon :type="version.type"></file-icon>
|
||||
<span
|
||||
><strong> #{{ version.version + 1 }} </strong></span
|
||||
>
|
||||
<template v-if="version.createdBy !== null && version.createdAt !== null"
|
||||
><strong v-if="version.version == 0">créé par</strong
|
||||
><strong v-else>modifié par</strong>
|
||||
<span class="badge-user"
|
||||
><UserRenderBoxBadge :user="version.createdBy"></UserRenderBoxBadge
|
||||
></span>
|
||||
<strong>à</strong>
|
||||
{{
|
||||
$d(ISOToDatetime(version.createdAt.datetime8601), "long")
|
||||
}}</template
|
||||
><template v-if="version.createdBy === null && version.createdAt !== null"
|
||||
><strong v-if="version.version == 0">Créé le</strong
|
||||
><strong v-else>modifié le</strong>
|
||||
{{
|
||||
$d(ISOToDatetime(version.createdAt.datetime8601), "long")
|
||||
}}</template
|
||||
>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<ul class="record_actions small slim on-version-actions">
|
||||
<li v-if="canEdit && !isCurrent">
|
||||
<restore-version-button
|
||||
:stored-object-version="props.version"
|
||||
@restore-version="onRestore"
|
||||
></restore-version-button>
|
||||
</li>
|
||||
<li>
|
||||
<download-button
|
||||
:stored-object="storedObject"
|
||||
:at-version="version"
|
||||
:classes="{
|
||||
btn: true,
|
||||
'btn-outline-primary': true,
|
||||
'btn-sm': true,
|
||||
}"
|
||||
:display-action-string-in-button="false"
|
||||
></download-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
div.tags {
|
||||
span.badge:not(:last-child) {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
span.badge:not(:last-child) {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
// to make the animation restart, we have the same animation twice,
|
||||
// and alternate between both
|
||||
.blinking-1 {
|
||||
animation-name: backgroundColorPalette-1;
|
||||
animation-duration: 8s;
|
||||
animation-iteration-count: 1;
|
||||
animation-direction: normal;
|
||||
animation-timing-function: linear;
|
||||
animation-name: backgroundColorPalette-1;
|
||||
animation-duration: 8s;
|
||||
animation-iteration-count: 1;
|
||||
animation-direction: normal;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
@keyframes backgroundColorPalette-1 {
|
||||
0% {
|
||||
background: var(--bs-chill-green-dark);
|
||||
}
|
||||
25% {
|
||||
background: var(--bs-chill-green);
|
||||
}
|
||||
65% {
|
||||
background: var(--bs-chill-beige);
|
||||
}
|
||||
100% {
|
||||
background: unset;
|
||||
}
|
||||
0% {
|
||||
background: var(--bs-chill-green-dark);
|
||||
}
|
||||
25% {
|
||||
background: var(--bs-chill-green);
|
||||
}
|
||||
65% {
|
||||
background: var(--bs-chill-beige);
|
||||
}
|
||||
100% {
|
||||
background: unset;
|
||||
}
|
||||
}
|
||||
.blinking-2 {
|
||||
animation-name: backgroundColorPalette-2;
|
||||
animation-duration: 8s;
|
||||
animation-iteration-count: 1;
|
||||
animation-direction: normal;
|
||||
animation-timing-function: linear;
|
||||
animation-name: backgroundColorPalette-2;
|
||||
animation-duration: 8s;
|
||||
animation-iteration-count: 1;
|
||||
animation-direction: normal;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
@keyframes backgroundColorPalette-2 {
|
||||
0% {
|
||||
background: var(--bs-chill-green-dark);
|
||||
}
|
||||
25% {
|
||||
background: var(--bs-chill-green);
|
||||
}
|
||||
65% {
|
||||
background: var(--bs-chill-beige);
|
||||
}
|
||||
100% {
|
||||
background: unset;
|
||||
}
|
||||
0% {
|
||||
background: var(--bs-chill-green-dark);
|
||||
}
|
||||
25% {
|
||||
background: var(--bs-chill-green);
|
||||
}
|
||||
65% {
|
||||
background: var(--bs-chill-beige);
|
||||
}
|
||||
100% {
|
||||
background: unset;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -3,54 +3,54 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import { reactive } from "vue";
|
||||
import HistoryButtonList from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue";
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "./../../../types";
|
||||
|
||||
interface HistoryButtonListConfig {
|
||||
versions: StoredObjectVersionWithPointInTime[];
|
||||
storedObject: StoredObject;
|
||||
canEdit: boolean;
|
||||
versions: StoredObjectVersionWithPointInTime[];
|
||||
storedObject: StoredObject;
|
||||
canEdit: boolean;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
}>();
|
||||
|
||||
interface HistoryButtonModalState {
|
||||
opened: boolean;
|
||||
opened: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<HistoryButtonListConfig>();
|
||||
const state = reactive<HistoryButtonModalState>({ opened: false });
|
||||
|
||||
const open = () => {
|
||||
state.opened = true;
|
||||
state.opened = true;
|
||||
};
|
||||
|
||||
const onRestoreVersion = (payload: {
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
}) => emit("restoreVersion", payload);
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<modal v-if="state.opened" @close="state.opened = false">
|
||||
<template v-slot:header>
|
||||
<h3>Historique des versions du document</h3>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<p>Les versions sont conservées pendant 90 jours.</p>
|
||||
<history-button-list
|
||||
:versions="props.versions"
|
||||
:can-edit="canEdit"
|
||||
:stored-object="storedObject"
|
||||
@restore-version="onRestoreVersion"
|
||||
></history-button-list>
|
||||
</template>
|
||||
</modal>
|
||||
</Teleport>
|
||||
<Teleport to="body">
|
||||
<modal v-if="state.opened" @close="state.opened = false">
|
||||
<template v-slot:header>
|
||||
<h3>Historique des versions du document</h3>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<p>Les versions sont conservées pendant 90 jours.</p>
|
||||
<history-button-list
|
||||
:versions="props.versions"
|
||||
:can-edit="canEdit"
|
||||
:stored-object="storedObject"
|
||||
@restore-version="onRestoreVersion"
|
||||
></history-button-list>
|
||||
</template>
|
||||
</modal>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
@@ -1,17 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
StoredObjectVersionPersisted,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
StoredObjectVersionPersisted,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "../../../types";
|
||||
import { useToast } from "vue-toast-notification";
|
||||
import { restore_version } from "./api";
|
||||
|
||||
interface RestoreVersionButtonProps {
|
||||
storedObjectVersion: StoredObjectVersionPersisted;
|
||||
storedObjectVersion: StoredObjectVersionPersisted;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
}>();
|
||||
|
||||
const props = defineProps<RestoreVersionButtonProps>();
|
||||
@@ -19,21 +19,21 @@ const props = defineProps<RestoreVersionButtonProps>();
|
||||
const $toast = useToast();
|
||||
|
||||
const restore_version_fn = async () => {
|
||||
const newVersion = await restore_version(props.storedObjectVersion);
|
||||
const newVersion = await restore_version(props.storedObjectVersion);
|
||||
|
||||
$toast.success("Version restaurée");
|
||||
emit("restoreVersion", { newVersion });
|
||||
$toast.success("Version restaurée");
|
||||
emit("restoreVersion", { newVersion });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="btn btn-outline-action"
|
||||
@click="restore_version_fn"
|
||||
title="Restaurer"
|
||||
>
|
||||
<i class="fa fa-rotate-left"></i> Restaurer
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-outline-action"
|
||||
@click="restore_version_fn"
|
||||
title="Restaurer"
|
||||
>
|
||||
<i class="fa fa-rotate-left"></i> Restaurer
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
@@ -1,33 +1,33 @@
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectVersionPersisted,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
StoredObject,
|
||||
StoredObjectVersionPersisted,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "../../../types";
|
||||
import {
|
||||
fetchResults,
|
||||
makeFetch,
|
||||
fetchResults,
|
||||
makeFetch,
|
||||
} from "../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
|
||||
export const get_versions = async (
|
||||
storedObject: StoredObject,
|
||||
storedObject: StoredObject,
|
||||
): Promise<StoredObjectVersionWithPointInTime[]> => {
|
||||
const versions = await fetchResults<StoredObjectVersionWithPointInTime>(
|
||||
`/api/1.0/doc-store/stored-object/${storedObject.uuid}/versions`,
|
||||
);
|
||||
const versions = await fetchResults<StoredObjectVersionWithPointInTime>(
|
||||
`/api/1.0/doc-store/stored-object/${storedObject.uuid}/versions`,
|
||||
);
|
||||
|
||||
return versions.sort(
|
||||
(
|
||||
a: StoredObjectVersionWithPointInTime,
|
||||
b: StoredObjectVersionWithPointInTime,
|
||||
) => b.version - a.version,
|
||||
);
|
||||
return versions.sort(
|
||||
(
|
||||
a: StoredObjectVersionWithPointInTime,
|
||||
b: StoredObjectVersionWithPointInTime,
|
||||
) => b.version - a.version,
|
||||
);
|
||||
};
|
||||
|
||||
export const restore_version = async (
|
||||
version: StoredObjectVersionPersisted,
|
||||
version: StoredObjectVersionPersisted,
|
||||
): Promise<StoredObjectVersionWithPointInTime> => {
|
||||
return await makeFetch<null, StoredObjectVersionWithPointInTime>(
|
||||
"POST",
|
||||
`/api/1.0/doc-store/stored-object/restore-from-version/${version.id}`,
|
||||
);
|
||||
return await makeFetch<null, StoredObjectVersionWithPointInTime>(
|
||||
"POST",
|
||||
`/api/1.0/doc-store/stored-object/restore-from-version/${version.id}`,
|
||||
);
|
||||
};
|
||||
|
@@ -1,29 +1,27 @@
|
||||
<template>
|
||||
<a
|
||||
:class="Object.assign(props.classes, { btn: true })"
|
||||
@click="beforeLeave($event)"
|
||||
:href="
|
||||
build_wopi_editor_link(props.storedObject.uuid, props.returnPath)
|
||||
"
|
||||
>
|
||||
<i class="fa fa-paragraph"></i>
|
||||
Editer en ligne
|
||||
</a>
|
||||
<a
|
||||
:class="Object.assign(props.classes, { btn: true })"
|
||||
@click="beforeLeave($event)"
|
||||
:href="build_wopi_editor_link(props.storedObject.uuid, props.returnPath)"
|
||||
>
|
||||
<i class="fa fa-paragraph"></i>
|
||||
Editer en ligne
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import WopiEditButton from "./WopiEditButton.vue";
|
||||
import { build_wopi_editor_link } from "./helpers";
|
||||
import {
|
||||
StoredObject,
|
||||
WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
StoredObject,
|
||||
WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
} from "../../types";
|
||||
|
||||
interface WopiEditButtonConfig {
|
||||
storedObject: StoredObject;
|
||||
returnPath?: string;
|
||||
classes: Record<string, boolean>;
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
|
||||
storedObject: StoredObject;
|
||||
returnPath?: string;
|
||||
classes: Record<string, boolean>;
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
|
||||
}
|
||||
|
||||
const props = defineProps<WopiEditButtonConfig>();
|
||||
@@ -31,24 +29,24 @@ const props = defineProps<WopiEditButtonConfig>();
|
||||
let executed = false;
|
||||
|
||||
async function beforeLeave(event: Event): Promise<true> {
|
||||
if (props.executeBeforeLeave === undefined || executed === true) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
await props.executeBeforeLeave();
|
||||
executed = true;
|
||||
|
||||
const link = event.target as HTMLAnchorElement;
|
||||
link.click();
|
||||
|
||||
if (props.executeBeforeLeave === undefined || executed === true) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
await props.executeBeforeLeave();
|
||||
executed = true;
|
||||
|
||||
const link = event.target as HTMLAnchorElement;
|
||||
link.click();
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,235 +1,230 @@
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectStatus,
|
||||
StoredObjectStatusChange,
|
||||
StoredObjectVersion,
|
||||
StoredObject,
|
||||
StoredObjectStatus,
|
||||
StoredObjectStatusChange,
|
||||
StoredObjectVersion,
|
||||
} from "../../types";
|
||||
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
|
||||
const MIMES_EDIT = new Set([
|
||||
"application/vnd.ms-powerpoint",
|
||||
"application/vnd.ms-excel",
|
||||
"application/vnd.oasis.opendocument.text",
|
||||
"application/vnd.oasis.opendocument.text-flat-xml",
|
||||
"application/vnd.oasis.opendocument.spreadsheet",
|
||||
"application/vnd.oasis.opendocument.spreadsheet-flat-xml",
|
||||
"application/vnd.oasis.opendocument.presentation",
|
||||
"application/vnd.oasis.opendocument.presentation-flat-xml",
|
||||
"application/vnd.oasis.opendocument.graphics",
|
||||
"application/vnd.oasis.opendocument.graphics-flat-xml",
|
||||
"application/vnd.oasis.opendocument.chart",
|
||||
"application/msword",
|
||||
"application/vnd.ms-excel",
|
||||
"application/vnd.ms-powerpoint",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"application/vnd.ms-word.document.macroEnabled.12",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
||||
"application/vnd.ms-excel.sheet.macroEnabled.12",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||
"application/x-dif-document",
|
||||
"text/spreadsheet",
|
||||
"text/csv",
|
||||
"application/x-dbase",
|
||||
"text/rtf",
|
||||
"text/plain",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
||||
"application/vnd.ms-powerpoint",
|
||||
"application/vnd.ms-excel",
|
||||
"application/vnd.oasis.opendocument.text",
|
||||
"application/vnd.oasis.opendocument.text-flat-xml",
|
||||
"application/vnd.oasis.opendocument.spreadsheet",
|
||||
"application/vnd.oasis.opendocument.spreadsheet-flat-xml",
|
||||
"application/vnd.oasis.opendocument.presentation",
|
||||
"application/vnd.oasis.opendocument.presentation-flat-xml",
|
||||
"application/vnd.oasis.opendocument.graphics",
|
||||
"application/vnd.oasis.opendocument.graphics-flat-xml",
|
||||
"application/vnd.oasis.opendocument.chart",
|
||||
"application/msword",
|
||||
"application/vnd.ms-excel",
|
||||
"application/vnd.ms-powerpoint",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"application/vnd.ms-word.document.macroEnabled.12",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
||||
"application/vnd.ms-excel.sheet.macroEnabled.12",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||
"application/x-dif-document",
|
||||
"text/spreadsheet",
|
||||
"text/csv",
|
||||
"application/x-dbase",
|
||||
"text/rtf",
|
||||
"text/plain",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
||||
]);
|
||||
|
||||
const MIMES_VIEW = new Set([
|
||||
...MIMES_EDIT,
|
||||
[
|
||||
"image/svg+xml",
|
||||
"application/vnd.sun.xml.writer",
|
||||
"application/vnd.sun.xml.calc",
|
||||
"application/vnd.sun.xml.impress",
|
||||
"application/vnd.sun.xml.draw",
|
||||
"application/vnd.sun.xml.writer.global",
|
||||
"application/vnd.sun.xml.writer.template",
|
||||
"application/vnd.sun.xml.calc.template",
|
||||
"application/vnd.sun.xml.impress.template",
|
||||
"application/vnd.sun.xml.draw.template",
|
||||
"application/vnd.oasis.opendocument.text-master",
|
||||
"application/vnd.oasis.opendocument.text-template",
|
||||
"application/vnd.oasis.opendocument.text-master-template",
|
||||
"application/vnd.oasis.opendocument.spreadsheet-template",
|
||||
"application/vnd.oasis.opendocument.presentation-template",
|
||||
"application/vnd.oasis.opendocument.graphics-template",
|
||||
"application/vnd.ms-word.template.macroEnabled.12",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||
"application/vnd.ms-excel.template.macroEnabled.12",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.template",
|
||||
"application/vnd.ms-powerpoint.template.macroEnabled.12",
|
||||
"application/vnd.wordperfect",
|
||||
"application/x-aportisdoc",
|
||||
"application/x-hwp",
|
||||
"application/vnd.ms-works",
|
||||
"application/x-mswrite",
|
||||
"application/vnd.lotus-1-2-3",
|
||||
"image/cgm",
|
||||
"image/vnd.dxf",
|
||||
"image/x-emf",
|
||||
"image/x-wmf",
|
||||
"application/coreldraw",
|
||||
"application/vnd.visio2013",
|
||||
"application/vnd.visio",
|
||||
"application/vnd.ms-visio.drawing",
|
||||
"application/x-mspublisher",
|
||||
"application/x-sony-bbeb",
|
||||
"application/x-gnumeric",
|
||||
"application/macwriteii",
|
||||
"application/x-iwork-numbers-sffnumbers",
|
||||
"application/vnd.oasis.opendocument.text-web",
|
||||
"application/x-pagemaker",
|
||||
"application/x-fictionbook+xml",
|
||||
"application/clarisworks",
|
||||
"image/x-wpg",
|
||||
"application/x-iwork-pages-sffpages",
|
||||
"application/x-iwork-keynote-sffkey",
|
||||
"application/x-abiword",
|
||||
"image/x-freehand",
|
||||
"application/vnd.sun.xml.chart",
|
||||
"application/x-t602",
|
||||
"image/bmp",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/tiff",
|
||||
"image/jpg",
|
||||
"image/jpeg",
|
||||
"application/pdf",
|
||||
],
|
||||
...MIMES_EDIT,
|
||||
[
|
||||
"image/svg+xml",
|
||||
"application/vnd.sun.xml.writer",
|
||||
"application/vnd.sun.xml.calc",
|
||||
"application/vnd.sun.xml.impress",
|
||||
"application/vnd.sun.xml.draw",
|
||||
"application/vnd.sun.xml.writer.global",
|
||||
"application/vnd.sun.xml.writer.template",
|
||||
"application/vnd.sun.xml.calc.template",
|
||||
"application/vnd.sun.xml.impress.template",
|
||||
"application/vnd.sun.xml.draw.template",
|
||||
"application/vnd.oasis.opendocument.text-master",
|
||||
"application/vnd.oasis.opendocument.text-template",
|
||||
"application/vnd.oasis.opendocument.text-master-template",
|
||||
"application/vnd.oasis.opendocument.spreadsheet-template",
|
||||
"application/vnd.oasis.opendocument.presentation-template",
|
||||
"application/vnd.oasis.opendocument.graphics-template",
|
||||
"application/vnd.ms-word.template.macroEnabled.12",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||
"application/vnd.ms-excel.template.macroEnabled.12",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.template",
|
||||
"application/vnd.ms-powerpoint.template.macroEnabled.12",
|
||||
"application/vnd.wordperfect",
|
||||
"application/x-aportisdoc",
|
||||
"application/x-hwp",
|
||||
"application/vnd.ms-works",
|
||||
"application/x-mswrite",
|
||||
"application/vnd.lotus-1-2-3",
|
||||
"image/cgm",
|
||||
"image/vnd.dxf",
|
||||
"image/x-emf",
|
||||
"image/x-wmf",
|
||||
"application/coreldraw",
|
||||
"application/vnd.visio2013",
|
||||
"application/vnd.visio",
|
||||
"application/vnd.ms-visio.drawing",
|
||||
"application/x-mspublisher",
|
||||
"application/x-sony-bbeb",
|
||||
"application/x-gnumeric",
|
||||
"application/macwriteii",
|
||||
"application/x-iwork-numbers-sffnumbers",
|
||||
"application/vnd.oasis.opendocument.text-web",
|
||||
"application/x-pagemaker",
|
||||
"application/x-fictionbook+xml",
|
||||
"application/clarisworks",
|
||||
"image/x-wpg",
|
||||
"application/x-iwork-pages-sffpages",
|
||||
"application/x-iwork-keynote-sffkey",
|
||||
"application/x-abiword",
|
||||
"image/x-freehand",
|
||||
"application/vnd.sun.xml.chart",
|
||||
"application/x-t602",
|
||||
"image/bmp",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/tiff",
|
||||
"image/jpg",
|
||||
"image/jpeg",
|
||||
"application/pdf",
|
||||
],
|
||||
]);
|
||||
|
||||
export interface SignedUrlGet {
|
||||
method: "GET" | "HEAD";
|
||||
url: string;
|
||||
expires: number;
|
||||
object_name: string;
|
||||
method: "GET" | "HEAD";
|
||||
url: string;
|
||||
expires: number;
|
||||
object_name: string;
|
||||
}
|
||||
|
||||
function is_extension_editable(mimeType: string): boolean {
|
||||
return MIMES_EDIT.has(mimeType);
|
||||
return MIMES_EDIT.has(mimeType);
|
||||
}
|
||||
|
||||
function is_extension_viewable(mimeType: string): boolean {
|
||||
return MIMES_VIEW.has(mimeType);
|
||||
return MIMES_VIEW.has(mimeType);
|
||||
}
|
||||
|
||||
function build_convert_link(uuid: string) {
|
||||
return `/chill/wopi/convert/${uuid}`;
|
||||
return `/chill/wopi/convert/${uuid}`;
|
||||
}
|
||||
|
||||
function build_download_info_link(
|
||||
storedObject: StoredObject,
|
||||
atVersion: null | StoredObjectVersion,
|
||||
storedObject: StoredObject,
|
||||
atVersion: null | StoredObjectVersion,
|
||||
): string {
|
||||
const url = `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/get`;
|
||||
const url = `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/get`;
|
||||
|
||||
if (null !== atVersion) {
|
||||
const params = new URLSearchParams({ version: atVersion.filename });
|
||||
if (null !== atVersion) {
|
||||
const params = new URLSearchParams({ version: atVersion.filename });
|
||||
|
||||
return url + "?" + params.toString();
|
||||
}
|
||||
return url + "?" + params.toString();
|
||||
}
|
||||
|
||||
return url;
|
||||
return url;
|
||||
}
|
||||
|
||||
async function download_info_link(
|
||||
storedObject: StoredObject,
|
||||
atVersion: null | StoredObjectVersion,
|
||||
storedObject: StoredObject,
|
||||
atVersion: null | StoredObjectVersion,
|
||||
): Promise<SignedUrlGet> {
|
||||
return makeFetch("GET", build_download_info_link(storedObject, atVersion));
|
||||
return makeFetch("GET", build_download_info_link(storedObject, atVersion));
|
||||
}
|
||||
|
||||
function build_wopi_editor_link(uuid: string, returnPath?: string) {
|
||||
if (returnPath === undefined) {
|
||||
returnPath =
|
||||
window.location.pathname +
|
||||
window.location.search +
|
||||
window.location.hash;
|
||||
}
|
||||
if (returnPath === undefined) {
|
||||
returnPath =
|
||||
window.location.pathname + window.location.search + window.location.hash;
|
||||
}
|
||||
|
||||
return (
|
||||
`/chill/wopi/edit/${uuid}?returnPath=` + encodeURIComponent(returnPath)
|
||||
);
|
||||
return (
|
||||
`/chill/wopi/edit/${uuid}?returnPath=` + encodeURIComponent(returnPath)
|
||||
);
|
||||
}
|
||||
|
||||
function download_doc(url: string): Promise<Blob> {
|
||||
return window.fetch(url).then((r) => {
|
||||
if (r.ok) {
|
||||
return r.blob();
|
||||
}
|
||||
return window.fetch(url).then((r) => {
|
||||
if (r.ok) {
|
||||
return r.blob();
|
||||
}
|
||||
|
||||
throw new Error("Could not download document");
|
||||
});
|
||||
throw new Error("Could not download document");
|
||||
});
|
||||
}
|
||||
|
||||
async function download_and_decrypt_doc(
|
||||
storedObject: StoredObject,
|
||||
atVersion: null | StoredObjectVersion,
|
||||
storedObject: StoredObject,
|
||||
atVersion: null | StoredObjectVersion,
|
||||
): Promise<Blob> {
|
||||
const algo = "AES-CBC";
|
||||
const algo = "AES-CBC";
|
||||
|
||||
const atVersionToDownload = atVersion ?? storedObject.currentVersion;
|
||||
const atVersionToDownload = atVersion ?? storedObject.currentVersion;
|
||||
|
||||
if (null === atVersionToDownload) {
|
||||
throw new Error("no version associated to stored object");
|
||||
}
|
||||
if (null === atVersionToDownload) {
|
||||
throw new Error("no version associated to stored object");
|
||||
}
|
||||
|
||||
// sometimes, the downloadInfo may be embedded into the storedObject
|
||||
console.log("storedObject", storedObject);
|
||||
let downloadInfo;
|
||||
if (
|
||||
typeof storedObject._links !== "undefined" &&
|
||||
typeof storedObject._links.downloadLink !== "undefined"
|
||||
) {
|
||||
downloadInfo = storedObject._links.downloadLink;
|
||||
} else {
|
||||
downloadInfo = await download_info_link(
|
||||
storedObject,
|
||||
atVersionToDownload,
|
||||
);
|
||||
}
|
||||
// sometimes, the downloadInfo may be embedded into the storedObject
|
||||
console.log("storedObject", storedObject);
|
||||
let downloadInfo;
|
||||
if (
|
||||
typeof storedObject._links !== "undefined" &&
|
||||
typeof storedObject._links.downloadLink !== "undefined"
|
||||
) {
|
||||
downloadInfo = storedObject._links.downloadLink;
|
||||
} else {
|
||||
downloadInfo = await download_info_link(storedObject, atVersionToDownload);
|
||||
}
|
||||
|
||||
const rawResponse = await window.fetch(downloadInfo.url);
|
||||
const rawResponse = await window.fetch(downloadInfo.url);
|
||||
|
||||
if (!rawResponse.ok) {
|
||||
throw new Error(
|
||||
"error while downloading raw file " +
|
||||
rawResponse.status +
|
||||
" " +
|
||||
rawResponse.statusText,
|
||||
);
|
||||
}
|
||||
if (!rawResponse.ok) {
|
||||
throw new Error(
|
||||
"error while downloading raw file " +
|
||||
rawResponse.status +
|
||||
" " +
|
||||
rawResponse.statusText,
|
||||
);
|
||||
}
|
||||
|
||||
if (atVersionToDownload.iv.length === 0) {
|
||||
return rawResponse.blob();
|
||||
}
|
||||
if (atVersionToDownload.iv.length === 0) {
|
||||
return rawResponse.blob();
|
||||
}
|
||||
|
||||
const rawBuffer = await rawResponse.arrayBuffer();
|
||||
try {
|
||||
const key = await window.crypto.subtle.importKey(
|
||||
"jwk",
|
||||
atVersionToDownload.keyInfos,
|
||||
{ name: algo },
|
||||
false,
|
||||
["decrypt"],
|
||||
);
|
||||
const iv = Uint8Array.from(atVersionToDownload.iv);
|
||||
const decrypted = await window.crypto.subtle.decrypt(
|
||||
{ name: algo, iv: iv },
|
||||
key,
|
||||
rawBuffer,
|
||||
);
|
||||
const rawBuffer = await rawResponse.arrayBuffer();
|
||||
try {
|
||||
const key = await window.crypto.subtle.importKey(
|
||||
"jwk",
|
||||
atVersionToDownload.keyInfos,
|
||||
{ name: algo },
|
||||
false,
|
||||
["decrypt"],
|
||||
);
|
||||
const iv = Uint8Array.from(atVersionToDownload.iv);
|
||||
const decrypted = await window.crypto.subtle.decrypt(
|
||||
{ name: algo, iv: iv },
|
||||
key,
|
||||
rawBuffer,
|
||||
);
|
||||
|
||||
return Promise.resolve(new Blob([decrypted]));
|
||||
} catch (e) {
|
||||
console.error("encounter error while keys and decrypt operations");
|
||||
console.error(e);
|
||||
return Promise.resolve(new Blob([decrypted]));
|
||||
} catch (e) {
|
||||
console.error("encounter error while keys and decrypt operations");
|
||||
console.error(e);
|
||||
|
||||
throw e;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,48 +234,45 @@ async function download_and_decrypt_doc(
|
||||
* storage.
|
||||
*/
|
||||
async function download_doc_as_pdf(storedObject: StoredObject): Promise<Blob> {
|
||||
if (null === storedObject.currentVersion) {
|
||||
throw new Error("the stored object does not count any version");
|
||||
}
|
||||
if (null === storedObject.currentVersion) {
|
||||
throw new Error("the stored object does not count any version");
|
||||
}
|
||||
|
||||
if (storedObject.currentVersion?.type === "application/pdf") {
|
||||
return download_and_decrypt_doc(
|
||||
storedObject,
|
||||
storedObject.currentVersion,
|
||||
);
|
||||
}
|
||||
if (storedObject.currentVersion?.type === "application/pdf") {
|
||||
return download_and_decrypt_doc(storedObject, storedObject.currentVersion);
|
||||
}
|
||||
|
||||
const convertLink = build_convert_link(storedObject.uuid);
|
||||
const response = await fetch(convertLink);
|
||||
const convertLink = build_convert_link(storedObject.uuid);
|
||||
const response = await fetch(convertLink);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Could not convert the document: " + response.status);
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error("Could not convert the document: " + response.status);
|
||||
}
|
||||
|
||||
return response.blob();
|
||||
return response.blob();
|
||||
}
|
||||
|
||||
async function is_object_ready(
|
||||
storedObject: StoredObject,
|
||||
storedObject: StoredObject,
|
||||
): Promise<StoredObjectStatusChange> {
|
||||
const new_status_response = await window.fetch(
|
||||
`/api/1.0/doc-store/stored-object/${storedObject.uuid}/is-ready`,
|
||||
);
|
||||
const new_status_response = await window.fetch(
|
||||
`/api/1.0/doc-store/stored-object/${storedObject.uuid}/is-ready`,
|
||||
);
|
||||
|
||||
if (!new_status_response.ok) {
|
||||
throw new Error("could not fetch the new status");
|
||||
}
|
||||
if (!new_status_response.ok) {
|
||||
throw new Error("could not fetch the new status");
|
||||
}
|
||||
|
||||
return await new_status_response.json();
|
||||
return await new_status_response.json();
|
||||
}
|
||||
|
||||
export {
|
||||
build_convert_link,
|
||||
build_wopi_editor_link,
|
||||
download_and_decrypt_doc,
|
||||
download_doc,
|
||||
download_doc_as_pdf,
|
||||
is_extension_editable,
|
||||
is_extension_viewable,
|
||||
is_object_ready,
|
||||
build_convert_link,
|
||||
build_wopi_editor_link,
|
||||
download_and_decrypt_doc,
|
||||
download_doc,
|
||||
download_doc_as_pdf,
|
||||
is_extension_editable,
|
||||
is_extension_viewable,
|
||||
is_object_ready,
|
||||
};
|
||||
|
Reference in New Issue
Block a user