Apply prettier rules

This commit is contained in:
2024-11-14 18:47:38 +01:00
parent 610227815a
commit aa0785fc71
291 changed files with 23646 additions and 22071 deletions

View File

@@ -1,86 +1,152 @@
<template>
<div v-if="'ready' === props.storedObject.status || 'stored_object_created' === props.storedObject.status" class="btn-group">
<button :class="Object.assign({'btn': true, 'btn-outline-primary': true, 'dropdown-toggle': true, 'btn-sm': props.small})" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Actions
</button>
<ul class="dropdown-menu">
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type) && props.storedObject.status !== 'stored_object_created'">
<wopi-edit-button :stored-object="props.storedObject" :classes="{'dropdown-item': true}" :execute-before-leave="props.executeBeforeLeave"></wopi-edit-button>
</li>
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type) && props.davLink !== undefined && props.davLinkExpiration !== undefined">
<desktop-edit-button :classes="{'dropdown-item': true}" :edit-link="props.davLink" :expiration-link="props.davLinkExpiration"></desktop-edit-button>
</li>
<li v-if="props.storedObject.type != 'application/pdf' && is_extension_viewable(props.storedObject.type) && props.canConvertPdf && props.storedObject.status !== 'stored_object_created'">
<convert-button :stored-object="props.storedObject" :filename="filename" :classes="{'dropdown-item': true}"></convert-button>
</li>
<li v-if="props.canDownload">
<download-button :stored-object="props.storedObject" :filename="filename" :classes="{'dropdown-item': true}"></download-button>
</li>
</ul>
</div>
<div v-else-if="'pending' === props.storedObject.status">
<div class="btn btn-outline-info">Génération en cours</div>
</div>
<div v-else-if="'failure' === props.storedObject.status">
<div class="btn btn-outline-danger">La génération a échoué</div>
</div>
<div
v-if="
'ready' === props.storedObject.status ||
'stored_object_created' === props.storedObject.status
"
class="btn-group"
>
<button
:class="
Object.assign({
btn: true,
'btn-outline-primary': true,
'dropdown-toggle': true,
'btn-sm': props.small,
})
"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Actions
</button>
<ul class="dropdown-menu">
<li
v-if="
props.canEdit &&
is_extension_editable(props.storedObject.type) &&
props.storedObject.status !== 'stored_object_created'
"
>
<wopi-edit-button
:stored-object="props.storedObject"
:classes="{ 'dropdown-item': true }"
:execute-before-leave="props.executeBeforeLeave"
></wopi-edit-button>
</li>
<li
v-if="
props.canEdit &&
is_extension_editable(props.storedObject.type) &&
props.davLink !== undefined &&
props.davLinkExpiration !== undefined
"
>
<desktop-edit-button
:classes="{ 'dropdown-item': true }"
:edit-link="props.davLink"
:expiration-link="props.davLinkExpiration"
></desktop-edit-button>
</li>
<li
v-if="
props.storedObject.type != 'application/pdf' &&
is_extension_viewable(props.storedObject.type) &&
props.canConvertPdf &&
props.storedObject.status !== 'stored_object_created'
"
>
<convert-button
:stored-object="props.storedObject"
:filename="filename"
:classes="{ 'dropdown-item': true }"
></convert-button>
</li>
<li v-if="props.canDownload">
<download-button
:stored-object="props.storedObject"
:filename="filename"
:classes="{ 'dropdown-item': true }"
></download-button>
</li>
</ul>
</div>
<div v-else-if="'pending' === props.storedObject.status">
<div class="btn btn-outline-info">Génération en cours</div>
</div>
<div v-else-if="'failure' === props.storedObject.status">
<div class="btn btn-outline-danger">La génération a échoué</div>
</div>
</template>
<script lang="ts" setup>
import {onMounted} from "vue";
import { onMounted } from "vue";
import ConvertButton from "./StoredObjectButton/ConvertButton.vue";
import DownloadButton from "./StoredObjectButton/DownloadButton.vue";
import WopiEditButton from "./StoredObjectButton/WopiEditButton.vue";
import {is_extension_editable, is_extension_viewable, is_object_ready} from "./StoredObjectButton/helpers";
import {
StoredObject, StoredObjectCreated,
is_extension_editable,
is_extension_viewable,
is_object_ready,
} from "./StoredObjectButton/helpers";
import {
StoredObject,
StoredObjectCreated,
StoredObjectStatusChange,
WopiEditButtonExecutableBeforeLeaveFunction
WopiEditButtonExecutableBeforeLeaveFunction,
} from "../types";
import DesktopEditButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/DesktopEditButton.vue";
interface DocumentActionButtonsGroupConfig {
storedObject: StoredObject|StoredObjectCreated,
small?: boolean,
canEdit?: boolean,
canDownload?: boolean,
canConvertPdf?: boolean,
returnPath?: string,
storedObject: StoredObject | StoredObjectCreated;
small?: boolean;
canEdit?: boolean;
canDownload?: boolean;
canConvertPdf?: boolean;
returnPath?: string;
/**
* Will be the filename displayed to the user when he·she download the document
* (the document will be saved on his disk with this name)
*
* If not set, 'document' will be used.
*/
filename?: string,
/**
* Will be the filename displayed to the user when he·she download the document
* (the document will be saved on his disk with this name)
*
* If not set, 'document' will be used.
*/
filename?: string;
/**
* If set, will execute this function before leaving to the editor
*/
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction,
/**
* If set, will execute this function before leaving to the editor
*/
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
/**
* a link to download and edit file using webdav
*/
davLink?: string,
/**
* a link to download and edit file using webdav
*/
davLink?: string;
/**
* the expiration date of the download, as a unix timestamp
*/
davLinkExpiration?: number,
/**
* the expiration date of the download, as a unix timestamp
*/
davLinkExpiration?: number;
}
const emit = defineEmits<(e: 'onStoredObjectStatusChange', newStatus: StoredObjectStatusChange) => void>();
const emit =
defineEmits<
(
e: "onStoredObjectStatusChange",
newStatus: StoredObjectStatusChange,
) => void
>();
const props = withDefaults(defineProps<DocumentActionButtonsGroupConfig>(), {
small: false,
canEdit: true,
canDownload: true,
canConvertPdf: true,
returnPath: window.location.pathname + window.location.search + window.location.hash
small: false,
canEdit: true,
canDownload: true,
canConvertPdf: true,
returnPath:
window.location.pathname +
window.location.search +
window.location.hash,
});
/**
@@ -93,49 +159,46 @@ let tryiesForReady = 0;
*/
const maxTryiesForReady = 120;
const checkForReady = function(): void {
if (
'ready' === props.storedObject.status
|| 'failure' === props.storedObject.status
|| 'stored_object_created' === props.storedObject.status
// stop reloading if the page stays opened for a long time
|| tryiesForReady > maxTryiesForReady
) {
return;
}
const checkForReady = function (): void {
if (
"ready" === props.storedObject.status ||
"failure" === props.storedObject.status ||
"stored_object_created" === props.storedObject.status ||
// stop reloading if the page stays opened for a long time
tryiesForReady > maxTryiesForReady
) {
return;
}
tryiesForReady = tryiesForReady + 1;
tryiesForReady = tryiesForReady + 1;
setTimeout(onObjectNewStatusCallback, 5000);
setTimeout(onObjectNewStatusCallback, 5000);
};
const onObjectNewStatusCallback = async function(): Promise<void> {
const onObjectNewStatusCallback = async function (): Promise<void> {
if (props.storedObject.status === "stored_object_created") {
return Promise.resolve();
}
const new_status = await is_object_ready(props.storedObject);
if (props.storedObject.status !== new_status.status) {
emit("onStoredObjectStatusChange", new_status);
return Promise.resolve();
} else if ("failure" === new_status.status) {
return Promise.resolve();
}
if ("ready" !== new_status.status) {
// we check for new status, unless it is ready
checkForReady();
}
if (props.storedObject.status === 'stored_object_created') {
return Promise.resolve();
}
const new_status = await is_object_ready(props.storedObject);
if (props.storedObject.status !== new_status.status) {
emit('onStoredObjectStatusChange', new_status);
return Promise.resolve();
} else if ('failure' === new_status.status) {
return Promise.resolve();
}
if ('ready' !== new_status.status) {
// we check for new status, unless it is ready
checkForReady();
}
return Promise.resolve();
};
onMounted(() => {
checkForReady();
})
checkForReady();
});
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,20 +1,22 @@
<script setup lang="ts">
import {StoredObject, StoredObjectCreated} from "../../types";
import {encryptFile, uploadFile} from "../_components/helper";
import {computed, ref, Ref} from "vue";
import { StoredObject, StoredObjectCreated } from "../../types";
import { encryptFile, uploadFile } from "../_components/helper";
import { computed, ref, Ref } from "vue";
interface DropFileConfig {
existingDoc?: StoredObjectCreated|StoredObject,
existingDoc?: StoredObjectCreated | StoredObject;
}
const props = defineProps<DropFileConfig>();
const emit = defineEmits<(e: 'addDocument', stored_object: StoredObjectCreated) => void,>();
const emit =
defineEmits<
(e: "addDocument", stored_object: StoredObjectCreated) => void
>();
const is_dragging: Ref<boolean> = ref(false);
const uploading: Ref<boolean> = ref(false);
const display_filename: Ref<string|null> = ref(null);
const display_filename: Ref<string | null> = ref(null);
const has_existing_doc = computed<boolean>(() => {
return props.existingDoc !== undefined && props.existingDoc !== null;
@@ -24,16 +26,16 @@ const onDragOver = (e: Event) => {
e.preventDefault();
is_dragging.value = true;
}
};
const onDragLeave = (e: Event) => {
e.preventDefault();
is_dragging.value = false;
}
};
const onDrop = (e: DragEvent) => {
console.log('on drop', e);
console.log("on drop", e);
e.preventDefault();
const files = e.dataTransfer?.files;
@@ -47,8 +49,8 @@ const onDrop = (e: DragEvent) => {
return;
}
handleFile(files[0])
}
handleFile(files[0]);
};
const onZoneClick = (e: Event) => {
e.stopPropagation();
@@ -59,22 +61,22 @@ const onZoneClick = (e: Event) => {
input.addEventListener("change", onFileChange);
input.click();
}
};
const onFileChange = async (event: Event): Promise<void> => {
const input = event.target as HTMLInputElement;
console.log('event triggered', input);
console.log("event triggered", input);
if (input.files && input.files[0]) {
console.log('file added', input.files[0]);
console.log("file added", input.files[0]);
const file = input.files[0];
await handleFile(file);
return Promise.resolve();
}
throw 'No file given';
}
throw "No file given";
};
const handleFile = async (file: File): Promise<void> => {
uploading.value = true;
@@ -92,34 +94,89 @@ const handleFile = async (file: File): Promise<void> => {
keyInfos: jsonWebKey,
type: type,
status: "stored_object_created",
}
};
emit('addDocument', storedObject);
emit("addDocument", storedObject);
uploading.value = false;
}
};
</script>
<template>
<div class="drop-file">
<div v-if="!uploading" :class="{ area: true, dragging: is_dragging}" @click="onZoneClick" @dragover="onDragOver" @dragleave="onDragLeave" @drop="onDrop">
<div
v-if="!uploading"
:class="{ area: true, dragging: is_dragging }"
@click="onZoneClick"
@dragover="onDragOver"
@dragleave="onDragLeave"
@drop="onDrop"
>
<p v-if="has_existing_doc" class="file-icon">
<i class="fa fa-file-pdf-o" v-if="props.existingDoc?.type === 'application/pdf'"></i>
<i class="fa fa-file-word-o" v-else-if="props.existingDoc?.type === 'application/vnd.oasis.opendocument.text'"></i>
<i class="fa fa-file-word-o" v-else-if="props.existingDoc?.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'"></i>
<i class="fa fa-file-word-o" v-else-if="props.existingDoc?.type === 'application/msword'"></i>
<i class="fa fa-file-excel-o" v-else-if="props.existingDoc?.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'"></i>
<i class="fa fa-file-excel-o" v-else-if="props.existingDoc?.type === 'application/vnd.ms-excel'"></i>
<i class="fa fa-file-image-o" v-else-if="props.existingDoc?.type === 'image/jpeg'"></i>
<i class="fa fa-file-image-o" v-else-if="props.existingDoc?.type === 'image/png'"></i>
<i class="fa fa-file-archive-o" v-else-if="props.existingDoc?.type === 'application/x-zip-compressed'"></i>
<i class="fa fa-file-code-o" v-else ></i>
<i
class="fa fa-file-pdf-o"
v-if="props.existingDoc?.type === 'application/pdf'"
></i>
<i
class="fa fa-file-word-o"
v-else-if="
props.existingDoc?.type ===
'application/vnd.oasis.opendocument.text'
"
></i>
<i
class="fa fa-file-word-o"
v-else-if="
props.existingDoc?.type ===
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
"
></i>
<i
class="fa fa-file-word-o"
v-else-if="props.existingDoc?.type === 'application/msword'"
></i>
<i
class="fa fa-file-excel-o"
v-else-if="
props.existingDoc?.type ===
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
"
></i>
<i
class="fa fa-file-excel-o"
v-else-if="
props.existingDoc?.type === 'application/vnd.ms-excel'
"
></i>
<i
class="fa fa-file-image-o"
v-else-if="props.existingDoc?.type === 'image/jpeg'"
></i>
<i
class="fa fa-file-image-o"
v-else-if="props.existingDoc?.type === 'image/png'"
></i>
<i
class="fa fa-file-archive-o"
v-else-if="
props.existingDoc?.type ===
'application/x-zip-compressed'
"
></i>
<i class="fa fa-file-code-o" v-else></i>
</p>
<p v-if="display_filename !== null" class="display-filename">{{ display_filename }}</p>
<p v-if="display_filename !== null" class="display-filename">
{{ display_filename }}
</p>
<!-- todo i18n -->
<p v-if="has_existing_doc">Déposez un document ou cliquez ici pour remplacer le document existant</p>
<p v-else>Déposez un document ou cliquez ici pour ouvrir le navigateur de fichier</p>
<p v-if="has_existing_doc">
Déposez un document ou cliquez ici pour remplacer le document
existant
</p>
<p v-else>
Déposez un document ou cliquez ici pour ouvrir le navigateur de
fichier
</p>
</div>
<div v-else class="waiting">
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
@@ -141,7 +198,8 @@ const handleFile = async (file: File): Promise<void> => {
font-weight: 200;
}
& > .area, & > .waiting {
& > .area,
& > .waiting {
width: 100%;
height: 10rem;
@@ -159,5 +217,4 @@ const handleFile = async (file: File): Promise<void> => {
}
}
}
</style>

View File

@@ -1,13 +1,12 @@
<script setup lang="ts">
import {StoredObject, StoredObjectCreated} from "../../types";
import {computed, ref, Ref} from "vue";
import { StoredObject, StoredObjectCreated } from "../../types";
import { computed, ref, Ref } from "vue";
import DropFile from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFile.vue";
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
interface DropFileConfig {
allowRemove: boolean,
existingDoc?: StoredObjectCreated|StoredObject,
allowRemove: boolean;
existingDoc?: StoredObjectCreated | StoredObject;
}
const props = withDefaults(defineProps<DropFileConfig>(), {
@@ -15,51 +14,53 @@ const props = withDefaults(defineProps<DropFileConfig>(), {
});
const emit = defineEmits<{
(e: 'addDocument', stored_object: StoredObjectCreated): void,
(e: 'removeDocument', stored_object: null): void
(e: "addDocument", stored_object: StoredObjectCreated): void;
(e: "removeDocument", stored_object: null): void;
}>();
const has_existing_doc = computed<boolean>(() => {
return props.existingDoc !== undefined && props.existingDoc !== null;
});
const dav_link_expiration = computed<number|undefined>(() => {
const dav_link_expiration = computed<number | undefined>(() => {
if (props.existingDoc === undefined || props.existingDoc === null) {
return undefined;
}
if (props.existingDoc.status !== 'ready') {
if (props.existingDoc.status !== "ready") {
return undefined;
}
return props.existingDoc._links?.dav_link?.expiration;
});
const dav_link_href = computed<string|undefined>(() => {
const dav_link_href = computed<string | undefined>(() => {
if (props.existingDoc === undefined || props.existingDoc === null) {
return undefined;
}
if (props.existingDoc.status !== 'ready') {
if (props.existingDoc.status !== "ready") {
return undefined;
}
return props.existingDoc._links?.dav_link?.href;
})
});
const onAddDocument = (s: StoredObjectCreated): void => {
emit('addDocument', s);
}
emit("addDocument", s);
};
const onRemoveDocument = (e: Event): void => {
e.stopPropagation();
e.preventDefault();
emit('removeDocument', null);
}
emit("removeDocument", null);
};
</script>
<template>
<div>
<drop-file :existingDoc="props.existingDoc" @addDocument="onAddDocument"></drop-file>
<drop-file
:existingDoc="props.existingDoc"
@addDocument="onAddDocument"
></drop-file>
<ul class="record_actions">
<li v-if="has_existing_doc">
@@ -72,12 +73,14 @@ const onRemoveDocument = (e: Event): void => {
/>
</li>
<li>
<button v-if="allowRemove" class="btn btn-delete" @click="onRemoveDocument($event)" ></button>
<button
v-if="allowRemove"
class="btn btn-delete"
@click="onRemoveDocument($event)"
></button>
</li>
</ul>
</div>
</template>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@@ -1,57 +1,61 @@
<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} from "./helpers";
import {
build_convert_link,
download_and_decrypt_doc,
download_doc,
} from "./helpers";
import mime from "mime";
import {reactive, ref} from "vue";
import {StoredObject, StoredObjectCreated} from "../../types";
import { reactive, ref } from "vue";
import { StoredObject, StoredObjectCreated } 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>();
const state: DownloadButtonState = reactive({content: null});
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');
btn.value?.removeAttribute("download");
btn.value?.removeAttribute("href");
btn.value?.removeAttribute("type");
}
</script>
<style scoped lang="sass">

View File

@@ -1,23 +1,24 @@
<script setup lang="ts">
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import {computed, reactive} from "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});
const state: DesktopEditButtonState = reactive({ modalOpened: false });
const props = defineProps<DesktopEditButtonConfig>();
const buildCommand = computed<string>(() => 'vnd.libreoffice.command:ofe|u|' + props.editLink);
const buildCommand = computed<string>(
() => "vnd.libreoffice.command:ofe|u|" + props.editLink,
);
const editionUntilFormatted = computed<string>(() => {
let d;
@@ -29,26 +30,52 @@ const editionUntilFormatted = computed<string>(() => {
}
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">
<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 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>
<p>
<small
>Vous pouvez naviguez sur d'autres pages pendant
l'édition.</small
>
</p>
</div>
</template>
</modal>

View File

@@ -1,43 +1,59 @@
<template>
<a v-if="!state.is_ready" :class="props.classes" @click="download_and_open($event)">
<a
v-if="!state.is_ready"
:class="props.classes"
@click="download_and_open($event)"
>
<i class="fa fa-download"></i>
Télécharger
</a>
<a v-else :class="props.classes" target="_blank" :type="props.storedObject.type" :download="buildDocumentName()" :href="state.href_url" ref="open_button">
<a
v-else
:class="props.classes"
target="_blank"
:type="props.storedObject.type"
:download="buildDocumentName()"
:href="state.href_url"
ref="open_button"
>
<i class="fa fa-external-link"></i>
Ouvrir
</a>
</template>
<script lang="ts" setup>
import {reactive, ref, nextTick, onMounted} from "vue";
import {build_download_info_link, download_and_decrypt_doc} from "./helpers";
import { reactive, ref, nextTick, onMounted } from "vue";
import { build_download_info_link, download_and_decrypt_doc } from "./helpers";
import mime from "mime";
import {StoredObject, StoredObjectCreated} from "../../types";
import { StoredObject, StoredObjectCreated } from "../../types";
interface DownloadButtonConfig {
storedObject: StoredObject|StoredObjectCreated,
classes: Record<string, boolean>,
filename?: string,
storedObject: StoredObject | StoredObjectCreated;
classes: Record<string, boolean>;
filename?: string;
}
interface DownloadButtonState {
is_ready: boolean,
is_running: boolean,
href_url: string,
is_ready: boolean;
is_running: boolean;
href_url: string;
}
const props = defineProps<DownloadButtonConfig>();
const state: DownloadButtonState = reactive({is_ready: false, is_running: false, href_url: "#"});
const state: DownloadButtonState = reactive({
is_ready: false,
is_running: false,
href_url: "#",
});
const open_button = ref<HTMLAnchorElement | null>(null);
function buildDocumentName(): string {
const document_name = props.filename || 'document';
const document_name = props.filename || "document";
const ext = mime.getExtension(props.storedObject.type);
if (null !== ext) {
return document_name + '.' + ext;
return document_name + "." + ext;
}
return document_name;
@@ -47,14 +63,14 @@ async function download_and_open(event: Event): Promise<void> {
const button = event.target as HTMLAnchorElement;
if (state.is_running) {
console.log('state is running, aborting');
console.log("state is running, aborting");
return;
}
state.is_running = true;
if (state.is_ready) {
console.log('state is ready. This should not happens');
console.log("state is ready. This should not happens");
return;
}
@@ -62,36 +78,40 @@ async function download_and_open(event: Event): Promise<void> {
let raw;
try {
raw = await download_and_decrypt_doc(urlInfo, props.storedObject.keyInfos, new Uint8Array(props.storedObject.iv));
raw = await download_and_decrypt_doc(
urlInfo,
props.storedObject.keyInfos,
new Uint8Array(props.storedObject.iv),
);
} catch (e) {
console.error("error while downloading and decrypting document");
console.error(e);
throw e;
}
console.log('document downloading (and decrypting) successfully');
console.log("document downloading (and decrypting) successfully");
console.log('creating the url')
console.log("creating the url");
state.href_url = window.URL.createObjectURL(raw);
console.log('url created', state.href_url);
console.log("url created", state.href_url);
state.is_running = false;
state.is_ready = true;
console.log('new button marked as ready');
console.log('will click on button');
console.log("new button marked as ready");
console.log("will click on button");
console.log('openbutton is now', open_button.value);
console.log("openbutton is now", open_button.value);
await nextTick();
console.log('next tick actions');
console.log('openbutton after next tick', open_button.value);
console.log("next tick actions");
console.log("openbutton after next tick", open_button.value);
open_button.value?.click();
console.log('open button should have been clicked');
console.log("open button should have been clicked");
const timer = setTimeout(reset_state, 45000);
}
function reset_state(): void {
state.href_url = '#';
state.href_url = "#";
state.is_ready = false;
state.is_running = false;
}

View File

@@ -1,20 +1,30 @@
<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, StoredObjectCreated, WopiEditButtonExecutableBeforeLeaveFunction} from "../../types";
import { build_wopi_editor_link } from "./helpers";
import {
StoredObject,
StoredObjectCreated,
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>();
@@ -22,20 +32,20 @@ const props = defineProps<WopiEditButtonConfig>();
let executed = false;
async function beforeLeave(event: Event): Promise<true> {
console.log(executed);
if (props.executeBeforeLeave === undefined || executed === true) {
console.log(executed);
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);
}
event.preventDefault();
await props.executeBeforeLeave();
executed = true;
const link = event.target as HTMLAnchorElement;
link.click();
return Promise.resolve(true);
}
</script>
@@ -44,4 +54,3 @@ i.fa::before {
color: var(--bs-dropdown-link-hover-color);
}
</style>

View File

@@ -1,198 +1,228 @@
import {StoredObject, StoredObjectStatus, StoredObjectStatusChange} from "../../types";
import {
StoredObject,
StoredObjectStatus,
StoredObjectStatusChange,
} from "../../types";
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",
],
]);
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(object_name: string) {
return `/asyncupload/temp_url/generate/GET?object_name=${object_name}`;
return `/asyncupload/temp_url/generate/GET?object_name=${object_name}`;
}
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");
});
}
async function download_and_decrypt_doc(
urlGenerator: string,
keyData: JsonWebKey,
iv: Uint8Array,
): Promise<Blob> {
const algo = "AES-CBC";
// get an url to download the object
const downloadInfoResponse = await window.fetch(urlGenerator);
if (!downloadInfoResponse.ok) {
throw new Error(
"error while downloading url " +
downloadInfoResponse.status +
" " +
downloadInfoResponse.statusText,
);
}
throw new Error('Could not download document');
});
const downloadInfo = (await downloadInfoResponse.json()) as { url: string };
const rawResponse = await window.fetch(downloadInfo.url);
if (!rawResponse.ok) {
throw new Error(
"error while downloading raw file " +
rawResponse.status +
" " +
rawResponse.statusText,
);
}
if (iv.length === 0) {
console.log("returning document immediatly");
return rawResponse.blob();
}
console.log("start decrypting doc");
const rawBuffer = await rawResponse.arrayBuffer();
try {
const key = await window.crypto.subtle.importKey(
"jwk",
keyData,
{ name: algo },
false,
["decrypt"],
);
console.log("key created");
const decrypted = await window.crypto.subtle.decrypt(
{ name: algo, iv: iv },
key,
rawBuffer,
);
console.log("doc decrypted");
return Promise.resolve(new Blob([decrypted]));
} catch (e) {
console.error("get error while keys and decrypt operations");
console.error(e);
throw e;
}
}
async function download_and_decrypt_doc(urlGenerator: string, keyData: JsonWebKey, iv: Uint8Array): Promise<Blob>
{
const algo = 'AES-CBC';
// get an url to download the object
const downloadInfoResponse = await window.fetch(urlGenerator);
if (!downloadInfoResponse.ok) {
throw new Error("error while downloading url " + downloadInfoResponse.status + " " + downloadInfoResponse.statusText);
}
const downloadInfo = await downloadInfoResponse.json() as {url: string};
const rawResponse = await window.fetch(downloadInfo.url);
if (!rawResponse.ok) {
throw new Error("error while downloading raw file " + rawResponse.status + " " + rawResponse.statusText);
}
if (iv.length === 0) {
console.log('returning document immediatly');
return rawResponse.blob();
}
console.log('start decrypting doc');
const rawBuffer = await rawResponse.arrayBuffer();
try {
const key = await window.crypto.subtle
.importKey('jwk', keyData, { name: algo }, false, ['decrypt']);
console.log('key created');
const decrypted = await window.crypto.subtle
.decrypt({ name: algo, iv: iv }, key, rawBuffer);
console.log('doc decrypted');
return Promise.resolve(new Blob([decrypted]));
} catch (e) {
console.error('get error while keys and decrypt operations');
console.error(e);
throw e;
}
}
async function is_object_ready(storedObject: StoredObject): Promise<StoredObjectStatusChange>
{
const new_status_response = await window
.fetch( `/api/1.0/doc-store/stored-object/${storedObject.uuid}/is-ready`);
async function is_object_ready(
storedObject: StoredObject,
): Promise<StoredObjectStatusChange> {
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");
throw new Error("could not fetch the new status");
}
return await new_status_response.json();
}
export {
build_convert_link,
build_download_info_link,
build_wopi_editor_link,
download_and_decrypt_doc,
download_doc,
is_extension_editable,
is_extension_viewable,
is_object_ready,
build_convert_link,
build_download_info_link,
build_wopi_editor_link,
download_and_decrypt_doc,
download_doc,
is_extension_editable,
is_extension_viewable,
is_object_ready,
};

View File

@@ -1,184 +1,195 @@
<template>
<a
:class="btnClasses"
:title="$t(buttonTitle)"
@click="openModal"
>
<span>{{ $t(buttonTitle) }}</span>
</a>
<teleport to="body">
<div>
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
{{ $t('upload_a_document') }}
</template>
<template #body>
<div
id="dropZoneWrapper"
ref="dropZoneWrapper"
>
<div
data-stored-object="data-stored-object"
:data-label-preparing="$t('data_label_preparing')"
:data-label-quiet-button="$t('data_label_quiet_button')"
:data-label-ready="$t('data_label_ready')"
:data-dict-file-too-big="$t('data_dict_file_too_big')"
:data-dict-default-message="$t('data_dict_default_message')"
:data-dict-remove-file="$t('data_dict_remove_file')"
:data-dict-max-files-exceeded="$t('data_dict_max_files_exceeded')"
:data-dict-cancel-upload="$t('data_dict_cancel_upload')"
:data-dict-cancel-upload-confirm="$t('data_dict_cancel_upload_confirm')"
:data-dict-upload-canceled="$t('data_dict_upload_canceled')"
:data-dict-remove="$t('data_dict_remove')"
:data-allow-remove="!options.required"
data-temp-url-generator="/asyncupload/temp_url/generate/GET"
<a :class="btnClasses" :title="$t(buttonTitle)" @click="openModal">
<span>{{ $t(buttonTitle) }}</span>
</a>
<teleport to="body">
<div>
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<input
type="hidden"
data-async-file-upload="data-async-file-upload"
data-generate-temp-url-post="/asyncupload/temp_url/generate/post?expires_delay=180&amp;submit_delay=3600"
data-temp-url-get="/asyncupload/temp_url/generate/GET"
:data-max-files="options.maxFiles"
:data-max-post-size="options.maxPostSize"
:v-model="dataAsyncFileUpload"
>
<input
type="hidden"
data-stored-object-key="1"
>
<input
type="hidden"
data-stored-object-iv="1"
>
<input
type="hidden"
data-async-file-type="1"
>
</div>
</div>
</template>
<template #header>
{{ $t("upload_a_document") }}
</template>
<template #footer>
<button
class="btn btn-create"
@click.prevent="saveDocument"
>
{{ $t('action.add') }}
</button>
</template>
</modal>
</div>
</teleport>
<template #body>
<div id="dropZoneWrapper" ref="dropZoneWrapper">
<div
data-stored-object="data-stored-object"
:data-label-preparing="$t('data_label_preparing')"
:data-label-quiet-button="
$t('data_label_quiet_button')
"
:data-label-ready="$t('data_label_ready')"
:data-dict-file-too-big="
$t('data_dict_file_too_big')
"
:data-dict-default-message="
$t('data_dict_default_message')
"
:data-dict-remove-file="$t('data_dict_remove_file')"
:data-dict-max-files-exceeded="
$t('data_dict_max_files_exceeded')
"
:data-dict-cancel-upload="
$t('data_dict_cancel_upload')
"
:data-dict-cancel-upload-confirm="
$t('data_dict_cancel_upload_confirm')
"
:data-dict-upload-canceled="
$t('data_dict_upload_canceled')
"
:data-dict-remove="$t('data_dict_remove')"
:data-allow-remove="!options.required"
data-temp-url-generator="/asyncupload/temp_url/generate/GET"
>
<input
type="hidden"
data-async-file-upload="data-async-file-upload"
data-generate-temp-url-post="/asyncupload/temp_url/generate/post?expires_delay=180&amp;submit_delay=3600"
data-temp-url-get="/asyncupload/temp_url/generate/GET"
:data-max-files="options.maxFiles"
:data-max-post-size="options.maxPostSize"
:v-model="dataAsyncFileUpload"
/>
<input type="hidden" data-stored-object-key="1" />
<input type="hidden" data-stored-object-iv="1" />
<input type="hidden" data-async-file-type="1" />
</div>
</div>
</template>
<template #footer>
<button
class="btn btn-create"
@click.prevent="saveDocument"
>
{{ $t("action.add") }}
</button>
</template>
</modal>
</div>
</teleport>
</template>
<script>
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import { searchForZones } from '../../module/async_upload/uploader';
import Modal from "ChillMainAssets/vuejs/_components/Modal";
import { searchForZones } from "../../module/async_upload/uploader";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
const i18n = {
messages: {
fr: {
upload_a_document: "Téléversez un document",
data_label_preparing: "Chargement...",
data_label_quiet_button: "Téléchargez le fichier existant",
data_label_ready: "Prêt à montrer",
data_dict_file_too_big: "Fichier trop volumineux",
data_dict_default_message: "Glissez votre fichier ou cliquez ici",
data_dict_remove_file: "Enlevez votre fichier pour en téléversez un autre",
data_dict_max_files_exceeded: "Nombre maximum de fichiers atteint. Enlevez les fichiers précédents",
data_dict_cancel_upload: "Annulez le téléversement",
data_dict_cancel_upload_confirm: "Êtes-vous sûr·e de vouloir annuler ce téléversement?",
data_dict_upload_canceled: "Téléversement annulé",
data_dict_remove: "Enlevez le fichier existant",
}
}
messages: {
fr: {
upload_a_document: "Téléversez un document",
data_label_preparing: "Chargement...",
data_label_quiet_button: "Téléchargez le fichier existant",
data_label_ready: "Prêt à montrer",
data_dict_file_too_big: "Fichier trop volumineux",
data_dict_default_message: "Glissez votre fichier ou cliquez ici",
data_dict_remove_file:
"Enlevez votre fichier pour en téléversez un autre",
data_dict_max_files_exceeded:
"Nombre maximum de fichiers atteint. Enlevez les fichiers précédents",
data_dict_cancel_upload: "Annulez le téléversement",
data_dict_cancel_upload_confirm:
"Êtes-vous sûr·e de vouloir annuler ce téléversement?",
data_dict_upload_canceled: "Téléversement annulé",
data_dict_remove: "Enlevez le fichier existant",
},
},
};
export default {
name: "AddAsyncUpload",
components: {
Modal
},
i18n,
name: "AddAsyncUpload",
components: {
Modal,
},
i18n,
props: {
buttonTitle: {
type: String,
default: 'Ajouter un document',
},
buttonTitle: {
type: String,
default: "Ajouter un document",
},
options: {
type: Object,
default: {
maxFiles: 1,
maxPostSize: 262144000, // 250MB
required: false,
}
},
},
btnClasses: {
type: Object,
type: Object,
default: {
btn: true,
'btn-create': true
}
"btn-create": true,
},
},
},
emits: ["addDocument"],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-centered modal-md",
},
};
},
updated() {
if (this.modal.showModal) {
searchForZones(this.$refs.dropZoneWrapper);
}
},
emits: ['addDocument'],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-centered modal-md"
},
}
},
updated() {
if (this.modal.showModal){
searchForZones(this.$refs.dropZoneWrapper);
}
},
methods: {
openModal() {
this.modal.showModal = true;
},
saveDocument() {
const dropzone = this.$refs.dropZoneWrapper;
if (dropzone) {
const inputKey = dropzone.querySelector('input[data-stored-object-key]');
const inputIv = dropzone.querySelector('input[data-stored-object-iv]');
const inputObject = dropzone.querySelector('input[data-async-file-upload]');
const inputType = dropzone.querySelector('input[data-async-file-type]');
methods: {
openModal() {
this.modal.showModal = true;
},
saveDocument() {
const dropzone = this.$refs.dropZoneWrapper;
if (dropzone) {
const inputKey = dropzone.querySelector(
"input[data-stored-object-key]",
);
const inputIv = dropzone.querySelector(
"input[data-stored-object-iv]",
);
const inputObject = dropzone.querySelector(
"input[data-async-file-upload]",
);
const inputType = dropzone.querySelector(
"input[data-async-file-type]",
);
const url = '/api/1.0/docstore/stored-object.json';
const body = {
filename: inputObject.value,
keyInfos: JSON.parse(inputKey.value),
iv: JSON.parse(inputIv.value),
type: inputType.value,
};
makeFetch('POST', url, body)
.then(r => {
this.$emit("addDocument", r);
this.modal.showModal = false;
})
.catch((error) => {
if (error.name === 'ValidationException') {
for (let v of error.violations) {
this.$toast.open({message: v });
}
} else {
console.error(error);
this.$toast.open({message: 'An error occurred'});
}
});
} else {
this.$toast.open({message: 'An error occurred - drop zone not found'});
}
}
}
}
const url = "/api/1.0/docstore/stored-object.json";
const body = {
filename: inputObject.value,
keyInfos: JSON.parse(inputKey.value),
iv: JSON.parse(inputIv.value),
type: inputType.value,
};
makeFetch("POST", url, body)
.then((r) => {
this.$emit("addDocument", r);
this.modal.showModal = false;
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
console.error(error);
this.$toast.open({ message: "An error occurred" });
}
});
} else {
this.$toast.open({
message: "An error occurred - drop zone not found",
});
}
},
},
};
</script>

View File

@@ -1,45 +1,42 @@
<template>
<a
class="btn btn-download"
:title="$t(buttonTitle)"
:data-key="JSON.stringify(storedObject.keyInfos)"
:data-iv="JSON.stringify(storedObject.iv)"
:data-mime-type="storedObject.type"
:data-label-preparing="$t('dataLabelPreparing')"
:data-label-ready="$t('dataLabelReady')"
:data-temp-url-get-generator="url"
@click.once="downloadDocument"
/>
<a
class="btn btn-download"
:title="$t(buttonTitle)"
:data-key="JSON.stringify(storedObject.keyInfos)"
:data-iv="JSON.stringify(storedObject.iv)"
:data-mime-type="storedObject.type"
:data-label-preparing="$t('dataLabelPreparing')"
:data-label-ready="$t('dataLabelReady')"
:data-temp-url-get-generator="url"
@click.once="downloadDocument"
/>
</template>
<script>
import { download } from '../../module/async_upload/downloader';
import { download } from "../../module/async_upload/downloader";
const i18n = {
messages: {
fr: {
dataLabelPreparing: "Chargement...",
dataLabelReady: "",
}
}
messages: {
fr: {
dataLabelPreparing: "Chargement...",
dataLabelReady: "",
},
},
};
export default {
name: "AddAsyncUploadDownloader",
i18n,
props: [
'buttonTitle',
'storedObject'
],
computed: {
url() {
return `/asyncupload/temp_url/generate/GET?object_name=${this.storedObject.filename}`;
}
},
methods: {
downloadDocument(e) {
download(e.target);
}
}
}
name: "AddAsyncUploadDownloader",
i18n,
props: ["buttonTitle", "storedObject"],
computed: {
url() {
return `/asyncupload/temp_url/generate/GET?object_name=${this.storedObject.filename}`;
},
},
methods: {
downloadDocument(e) {
download(e.target);
},
},
};
</script>

View File

@@ -1,18 +1,19 @@
import {makeFetch} from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
import {PostStoreObjectSignature} from "../../types";
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
import { PostStoreObjectSignature } from "../../types";
const algo = 'AES-CBC';
const algo = "AES-CBC";
const URL_POST = '/asyncupload/temp_url/generate/post';
const URL_POST = "/asyncupload/temp_url/generate/post";
const keyDefinition = {
name: algo,
length: 256
length: 256,
};
const createFilename = (): string => {
let text = "";
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 7; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
@@ -23,9 +24,12 @@ const createFilename = (): string => {
export const uploadFile = async (uploadFile: ArrayBuffer): Promise<string> => {
const params = new URLSearchParams();
params.append('expires_delay', "180");
params.append('submit_delay', "180");
const asyncData: PostStoreObjectSignature = await makeFetch("GET", URL_POST + "?" + params.toString());
params.append("expires_delay", "180");
params.append("submit_delay", "180");
const asyncData: PostStoreObjectSignature = await makeFetch(
"GET",
URL_POST + "?" + params.toString(),
);
const suffix = createFilename();
const filename = asyncData.prefix + suffix;
const formData = new FormData();
@@ -39,7 +43,7 @@ export const uploadFile = async (uploadFile: ArrayBuffer): Promise<string> => {
const response = await window.fetch(asyncData.url, {
method: "POST",
body: formData,
})
});
if (!response.ok) {
console.error("Error while sending file to store", response);
@@ -47,14 +51,23 @@ export const uploadFile = async (uploadFile: ArrayBuffer): Promise<string> => {
}
return Promise.resolve(filename);
}
};
export const encryptFile = async (originalFile: ArrayBuffer): Promise<[ArrayBuffer, Uint8Array, JsonWebKey]> => {
console.log('encrypt', originalFile);
export const encryptFile = async (
originalFile: ArrayBuffer,
): Promise<[ArrayBuffer, Uint8Array, JsonWebKey]> => {
console.log("encrypt", originalFile);
const iv = crypto.getRandomValues(new Uint8Array(16));
const key = await window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ]);
const exportedKey = await window.crypto.subtle.exportKey('jwk', key);
const encrypted = await window.crypto.subtle.encrypt({ name: algo, iv: iv}, key, originalFile);
const key = await window.crypto.subtle.generateKey(keyDefinition, true, [
"encrypt",
"decrypt",
]);
const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
const encrypted = await window.crypto.subtle.encrypt(
{ name: algo, iv: iv },
key,
originalFile,
);
return Promise.resolve([encrypted, iv, exportedKey]);
};