mirror of
https://github.com/bringout/oca-storage.git
synced 2026-04-20 23:52:07 +02:00
Initial commit: OCA Storage packages (17 packages)
This commit is contained in:
commit
7a380f05d3
659 changed files with 41828 additions and 0 deletions
|
|
@ -0,0 +1,5 @@
|
|||
.fs_file_download_button {
|
||||
top: 10% !important;
|
||||
left: 50% !important;
|
||||
position: absolute !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/** @odoo-module */
|
||||
|
||||
/**
|
||||
* Copyright 2023 ACSONE SA/NV
|
||||
*/
|
||||
|
||||
import {Dialog} from "@web/core/dialog/dialog";
|
||||
|
||||
const {Component, useRef} = owl;
|
||||
|
||||
export class AltTextDialog extends Component {
|
||||
setup() {
|
||||
this.altText = useRef("altText");
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
if (this.props.close) {
|
||||
this.props.close();
|
||||
}
|
||||
}
|
||||
|
||||
async onConfirm() {
|
||||
try {
|
||||
await this.props.confirm(this.altText.el.value);
|
||||
} catch (e) {
|
||||
this.props.close();
|
||||
throw e;
|
||||
}
|
||||
this.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
AltTextDialog.components = {Dialog};
|
||||
AltTextDialog.template = "fs_image.AltTextDialog";
|
||||
AltTextDialog.props = {
|
||||
title: String,
|
||||
altText: String,
|
||||
confirm: Function,
|
||||
close: {type: Function, optional: true},
|
||||
};
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="fs_image.AltTextDialog" owl="1">
|
||||
<Dialog size="'md'" title="props.title">
|
||||
<div class="form-group row">
|
||||
<t t-if="props.readonly">
|
||||
<span t-esc="props.value or ''" />
|
||||
</t>
|
||||
<div class="col-sm-12 o_field_widget o_field_text">
|
||||
<input
|
||||
type="text"
|
||||
id="altText"
|
||||
t-ref="altText"
|
||||
t-att-value="props.altText"
|
||||
class="o_input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<t t-set-slot="footer" owl="1">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
t-ref="btn-confirm"
|
||||
t-on-click="onConfirm"
|
||||
>Save changes</button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
t-ref="btn-close"
|
||||
t-on-click="onClose"
|
||||
>Cancel</button>
|
||||
</t>
|
||||
</Dialog>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/** @odoo-module */
|
||||
|
||||
/**
|
||||
* Copyright 2023 ACSONE SA/NV
|
||||
*/
|
||||
import {
|
||||
ImageField,
|
||||
fileTypeMagicWordMap,
|
||||
imageCacheKey,
|
||||
} from "@web/views/fields/image/image_field";
|
||||
import {onWillUpdateProps, useState} from "@odoo/owl";
|
||||
|
||||
import {AltTextDialog} from "../dialogs/alttext_dialog.esm";
|
||||
import {download, downloadFile} from "@web/core/network/download";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {url} from "@web/core/utils/urls";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
|
||||
const placeholder = "/web/static/img/placeholder.png";
|
||||
|
||||
export class FSImageField extends ImageField {
|
||||
setup() {
|
||||
// Call super.setup() to initialize the state
|
||||
super.setup();
|
||||
this.state = useState({
|
||||
...this.props.value,
|
||||
...this.state,
|
||||
});
|
||||
onWillUpdateProps((nextProps) => {
|
||||
this.state.isUploading = false;
|
||||
const {filename, mimetype, alt_text, url} = nextProps.value || {};
|
||||
this.state.filename = filename;
|
||||
this.state.mimetype = mimetype;
|
||||
this.state.url = url;
|
||||
this.state.alt_text = alt_text;
|
||||
});
|
||||
this.dialogService = useService("dialog");
|
||||
}
|
||||
|
||||
getUrl(previewFieldName) {
|
||||
if (
|
||||
this.state.isValid &&
|
||||
this.props.value &&
|
||||
typeof this.props.value === "object"
|
||||
) {
|
||||
// Check if value is a dict
|
||||
if (this.props.value.content) {
|
||||
// We use the binary content of the value
|
||||
// Use magic-word technique for detecting image type
|
||||
const magic =
|
||||
fileTypeMagicWordMap[this.props.value.content[0]] || "png";
|
||||
return `data:image/${magic};base64,${this.props.value.content}`;
|
||||
}
|
||||
const model = this.props.record.resModel;
|
||||
const id = this.props.record.resId;
|
||||
let base_url = this.props.value.url;
|
||||
if (id !== undefined && id !== null && id !== false) {
|
||||
const field = previewFieldName;
|
||||
const filename = this.props.value.filename;
|
||||
base_url = `/web/image/${model}/${id}/${field}/${filename}`;
|
||||
}
|
||||
return url(base_url, {unique: imageCacheKey(this.rawCacheKey)});
|
||||
}
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
get hasTooltip() {
|
||||
return this.props.enableZoom && !this.props.isDebugMode && this.props.value;
|
||||
}
|
||||
|
||||
onFileUploaded(info) {
|
||||
this.state.isValid = true;
|
||||
this.props.update({
|
||||
filename: info.name,
|
||||
content: info.data,
|
||||
});
|
||||
}
|
||||
onAltTextEdit() {
|
||||
const self = this;
|
||||
const altText = this.props.value.alt_text || "";
|
||||
const dialogProps = {
|
||||
title: this.env._t("Alt Text"),
|
||||
altText: altText,
|
||||
confirm: (value) => {
|
||||
self.props.update({
|
||||
...self.props.value,
|
||||
alt_text: value,
|
||||
});
|
||||
},
|
||||
};
|
||||
this.dialogService.add(AltTextDialog, dialogProps);
|
||||
}
|
||||
async onFileDownload() {
|
||||
if (this.props.value.content) {
|
||||
const magic = fileTypeMagicWordMap[this.props.value.content[0]] || "png";
|
||||
await downloadFile(
|
||||
`data:image/${magic};base64,${this.props.value.content}`,
|
||||
this.state.filename,
|
||||
`image/${magic}`
|
||||
);
|
||||
} else {
|
||||
await download({
|
||||
data: {
|
||||
model: this.props.record.resModel,
|
||||
id: this.props.record.resId,
|
||||
field: this.props.name,
|
||||
filename: this.state.filename || "download",
|
||||
download: true,
|
||||
},
|
||||
url: "/web/image",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FSImageField.template = "fs_image.FSImageField";
|
||||
registry.category("fields").add("fs_image", FSImageField);
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="fs_image.FSImageField" owl="1">
|
||||
<div class="d-inline-block position-relative opacity-trigger-hover">
|
||||
<div
|
||||
t-attf-class="position-absolute d-flex justify-content-between w-100 bottom-0 opacity-0 opacity-100-hover {{isMobile ? 'o_mobile_controls' : ''}}"
|
||||
aria-atomic="true"
|
||||
t-att-style="sizeStyle"
|
||||
>
|
||||
<t t-if="!props.readonly">
|
||||
<FileUploader
|
||||
acceptedFileExtensions="props.acceptedFileExtensions"
|
||||
t-key="props.record.resId"
|
||||
onUploaded.bind="onFileUploaded"
|
||||
type="'image'"
|
||||
>
|
||||
<t t-set-slot="toggler">
|
||||
<button
|
||||
class="o_select_file_button btn btn-light border-0 rounded-circle m-1 p-1"
|
||||
data-tooltip="Edit"
|
||||
aria-label="Edit"
|
||||
>
|
||||
<i class="fa fa-pencil fa-fw" />
|
||||
</button>
|
||||
</t>
|
||||
<t t-if="props.value and state.isValid">
|
||||
<button
|
||||
class="o_alt_text_file_button btn btn-light border-0 rounded-circle m-1 p-1"
|
||||
data-tooltip="Alt Text"
|
||||
aria-label="Set Alt Text"
|
||||
t-on-click="onAltTextEdit"
|
||||
>
|
||||
<i class="fa fa-blind fa-fw" />
|
||||
</button>
|
||||
<button
|
||||
class="o_clear_file_button btn btn-light border-0 rounded-circle m-1 p-1"
|
||||
data-tooltip="Clear"
|
||||
aria-label="Clear"
|
||||
t-on-click="onFileRemove"
|
||||
>
|
||||
<i class="fa fa-trash-o fa-fw" />
|
||||
</button>
|
||||
</t>
|
||||
</FileUploader>
|
||||
</t>
|
||||
</div>
|
||||
<img
|
||||
class="img img-fluid w-100"
|
||||
alt="Binary file"
|
||||
t-att-src="this.getUrl(props.previewImage or props.name)"
|
||||
t-att-name="props.name"
|
||||
t-att-height="props.height"
|
||||
t-att-width="props.width"
|
||||
t-att-style="sizeStyle"
|
||||
t-att-alt="props.alt"
|
||||
t-on-error.stop="onLoadFailed"
|
||||
t-att-data-tooltip-template="hasTooltip and tooltipAttributes.template"
|
||||
t-att-data-tooltip-info="hasTooltip and tooltipAttributes.info"
|
||||
t-att-data-tooltip-delay="hasTooltip and props.zoomDelay"
|
||||
/>
|
||||
<button
|
||||
t-if="props.value and state.isValid"
|
||||
class="fs_file_download_button btn btn-light border-0 rounded-circle m-1 p-1 translate-middle opacity-0 opacity-100-hover"
|
||||
data-tooltip="Download"
|
||||
aria-label="Download"
|
||||
t-on-click="onFileDownload"
|
||||
>
|
||||
<i class="fa fa-download fa-fw" />
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
Loading…
Add table
Add a link
Reference in a new issue