Bonjour,
Je suis actuellement en plein travail sur la version 18 d'Odoo. Je découvre l'outil depuis quelques semaines. J'ai découvert le champ « Binary » qui permet d'importer des fichiers. Une fois un fichier chargé, il y a plusieurs icônes qui apparaissent. Dont une poubelle pour supprimer le fichier. J'aimerais avoir une boite de dialogue qui demande la confirmation d'être sûr de vouloir supprimer le fichier. J'ai réussi à faire ceci dans le code source en le modifiant. Mais pour être plus propre et modulable, il serait mieux d'avoir un module personnalisé qui me permettrait de faire ceci. Sauf que j'ai beau tenter plusieurs choses, rien ne fonctionne. Je n'ai pas forcément d'erreurs. Quand j'ai des erreurs, j'arrive à les corriger. En plus de ceci, j'utilise un autre module (https://apps.odoo.com/apps/modules/17.0/ps_binary_field_attachment_preview) qui me permet de visualiser les fichiers. Je ne sais pas s'il peut y avoir une sorte de conflit entre les modules.
Merci de votre aide !
Voici le code source modifié comme je le souhaite avec la confirmation:
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { isBinarySize, toBase64Length } from "@web/core/utils/binary";
import { download } from "@web/core/network/download";
import { standardFieldProps } from "../standard_field_props";
import { FileUploader } from "../file_handler";
import { _t } from "@web/core/l10n/translation";
import { Component, xml } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
export const MAX_FILENAME_SIZE_BYTES = 0xFF; // filenames do not exceed 255 bytes on Linux/Windows/MacOS
export class BinaryField extends Component {
static template = "web.BinaryField";
static components = {
FileUploader,
};
static props = {
...standardFieldProps,
acceptedFileExtensions: { type: String, optional: true },
fileNameField: { type: String, optional: true },
};
static defaultProps = {
acceptedFileExtensions: "*",
};
setup() {
this.notification = useService("notification");
this.dialog = useService("dialog");
}
get fileName() {
return (
this.props.record.data[this.props.fileNameField] ||
this.props.record.data[this.props.name] ||
""
).slice(0, toBase64Length(MAX_FILENAME_SIZE_BYTES));
}
update({ data, name }) {
const { fileNameField, record } = this.props;
const changes = { [this.props.name]: data || false };
if (fileNameField in record.fields && record.data[fileNameField] !== name) {
changes[fileNameField] = name || '';
}
return this.props.record.update(changes);
}
getDownloadData() {
return {
model: this.props.record.resModel,
id: this.props.record.resId,
field: this.props.name,
filename_field: this.fileName,
filename: this.fileName || "",
download: true,
data: isBinarySize(this.props.record.data[this.props.name])
? null
: this.props.record.data[this.props.name],
};
}
onClickDelete() {
this.dialog.add(ConfirmDeleteDialog, {
onConfirmCallback: () => this.update({}),
});
}
async onFileDownload() {
await download({
data: this.getDownloadData(),
url: "/web/content",
});
}
}
export class ListBinaryField extends BinaryField {
static template = "web.ListBinaryField";
}
export const binaryField = {
component: BinaryField,
displayName: _t("File"),
supportedOptions: [
{
label: _t("Accepted file extensions"),
name: "accepted_file_extensions",
type: "string",
},
],
supportedTypes: ["binary"],
extractProps: ({ attrs, options }) => ({
acceptedFileExtensions: options.accepted_file_extensions,
fileNameField: attrs.filename,
}),
};
export const listBinaryField = {
...binaryField,
component: ListBinaryField,
};
class ConfirmDeleteDialog extends Component {
static template = xml`
<Dialog title="'Confirmation de suppression'">
<div>
Êtes-vous sûr de vouloir supprimer ce fichier ?
</div>
<t t-set-slot="footer">
<button class="btn btn-danger" t-on-click="onConfirm">Supprimer</button>
<button class="btn btn-secondary" t-on-click="props.close">Annuler</button>
</t>
</Dialog>
`;
static components = { Dialog };
static props = ['close', 'onConfirmCallback'];
onConfirm() {
this.props.onConfirmCallback(); // Appel du callback
this.props.close(); // Fermeture manuelle du dialog
}
}
registry.category("fields").add("binary", binaryField);
registry.category("fields").add("list.binary", listBinaryField);
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="web.BinaryField">
<t t-if="!props.readonly">
<t t-if="props.record.data[props.name]">
<div class="w-100 d-inline-flex gap-1">
<FileUploader
acceptedFileExtensions="props.acceptedFileExtensions"
onUploaded.bind="update"
>
<t name="download" t-if="props.record.resId and !props.record.dirty">
<button
class="btn btn-link btn-sm lh-1 fa fa-download o_download_file_button"
data-tooltip="Download"
aria-label="Download"
t-on-click="onFileDownload"
>
</button>
</t>
<t t-set-slot="toggler">
<input type="text" class="o_input" t-att-value="fileName" readonly="readonly" />
<button
class="btn btn-link btn-sm lh-1 fa fa-pencil o_select_file_button"
data-tooltip="Edit"
aria-label="Edit"
>
</button>
</t>
<button
class="btn btn-link btn-sm lh-1 fa fa-trash o_clear_file_button"
data-tooltip="Clear"
aria-label="Clear"
t-on-click="onClickDelete"
/>
<!-- </button> -->
</FileUploader>
</div>
</t>
<t t-else="">
<label class="o_select_file_button btn btn-primary">
<FileUploader
acceptedFileExtensions="props.acceptedFileExtensions"
onUploaded.bind="update"
>
<t t-set-slot="toggler">
Upload your file
</t>
</FileUploader>
</label>
</t>
</t>
<t t-elif="props.record.resId and props.record.data[props.name]">
<a class="o_form_uri" href="#" t-on-click.prevent="onFileDownload">
<span class="fa fa-download me-2" />
<t t-if="fileName" t-esc="fileName" />
</a>
</t>
</t>
<t t-name="web.ListBinaryField" t-inherit="web.BinaryField">
<xpath expr="//label[hasclass('o_select_file_button')]" position="replace">
<label class="o_select_file_button btn btn-sm btn-link p-0">
<FileUploader
acceptedFileExtensions="props.acceptedFileExtensions"
onUploaded.bind="update"
>
<t t-set-slot="toggler">
<i class="fa fa-upload fa-fw"/> Upload your file
</t>
</FileUploader>
</label>
</xpath>
</t>
</templates>