Merge pull request #601 from expsa/dev_base_attch_preview

Dev base attch preview
This commit is contained in:
Tahir Hassan 2024-08-08 16:16:15 +04:00 committed by GitHub
commit f731e0a896
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 336 additions and 243 deletions

View File

@ -2,284 +2,299 @@ odoo.define('odx_m2m_attachment_preview.odx_many2many_attachment_preview', funct
"use strict";
var AbstractField = require('web.AbstractField');
var field_registry = require('web.field_registry');
var core = require('web.core');
var relational_fields = require('web.relational_fields');
var DocumentViewer = require('odx_m2m_attachment_preview.DocumentViewer');
var session = require('web.session');
var rpc = require('web.rpc');
var Dialog = require('web.Dialog');
var AbstractField = require('web.AbstractField');
var field_registry = require('web.field_registry');
var core = require('web.core');
var relational_fields = require('web.relational_fields');
var DocumentViewer = require('odx_m2m_attachment_preview.DocumentViewer');
var session = require('web.session');
var rpc = require('web.rpc');
var Notification = require('web.Notification');
var _t = core._t;
var _lt = core._lt;
var qweb = core.qweb;
/**
* Widget to upload or delete one or more files at the same time.
*/
var FieldMany2ManyAttachmentPreview = AbstractField.extend({
template: "FieldBinaryFileUploader",
template_files: "FieldAttachmentFileUploader.files",
supportedFieldTypes: ['many2many'],
fieldsToFetch: {
name: {type: 'char'},
mimetype: {type: 'char'},
},
events: {
'click .o_attach': '_onAttach',
'click .o_attachment_delete': '_onDelete',
'change .o_input_file': '_onFileChanged',
'click .o_attachment_wrap': '_previewAttachment',
},
var _t = core._t;
var _lt = core._lt;
var qweb = core.qweb;
/**
* @constructor
* Widget to upload or delete one or more files at the same time.
*/
init: function () {
this._super.apply(this, arguments);
var FieldMany2ManyAttachmentPreview = AbstractField.extend({
if (this.field.type !== 'many2many' || this.field.relation !== 'ir.attachment') {
var msg = _t("The type of the field '%s' must be a many2many field with a relation to 'ir.attachment' model.");
throw _.str.sprintf(msg, this.field.string);
}
this.uploadedFiles = {};
this.uploadingFiles = [];
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
this.accepted_file_extensions = (this.nodeOptions && this.nodeOptions.accepted_file_extensions) || this.accepted_file_extensions || '*';
$(window).on(this.fileupload_id, this._onFileLoaded.bind(this));
this.fileMaxSize = 0;
this.metadata = {};
this._getMaxFileSize();
},
destroy: function () {
this._super();
$(window).off(this.fileupload_id);
},
template: "FieldBinaryFileUploader",
template_files: "FieldAttachmentFileUploader.files",
supportedFieldTypes: ['many2many'],
fieldsToFetch: {
name: {
type: 'char'
},
mimetype: {
type: 'char'
},
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
events: {
'click .o_attach': '_onAttach',
'click .o_attachment_delete': '_onDelete',
'change .o_input_file': '_onFileChanged',
'click .o_attachment_wrap': '_previewAttachment',
},
/**
* @constructor
*/
init: function () {
this._super.apply(this, arguments);
/**
* Compute the URL of an attachment.
*
* @private
* @param {Object} attachment
* @returns {string} URL of the attachment
*/
_getFileUrl: function (attachment) {
// return '/web/content/' + attachment.id + '?download=true';
return '/web/content/' + attachment.id;
},
/**
* Process the field data to add some information (url, etc.).
*
* @private
*/
_generatedMetadata: function () {
var self = this;
_.each(this.value.data, function (record) {
// tagging `allowUnlink` ascertains if the attachment was user
// uploaded or was an existing or system generated attachment
self.metadata[record.id] = {
allowUnlink: self.uploadedFiles[record.data.id] || false,
url: self._getFileUrl(record.data),
};
});
},
/**
* @private
* @override
*/
_render: function () {
// render the attachments ; as the attachments will changes after each
// _setValue, we put the rendering here to ensure they will be updated
this._generatedMetadata();
this.$('.oe_placeholder_files, .o_attachments')
.replaceWith($(qweb.render(this.template_files, {
widget: this,
})));
this.$('.oe_fileupload').show();
this.$('.o_image[data-mimetype^="image"]').each(function () {
var $img = $(this);
if (/gif|jpe|jpg|png/.test($img.data('mimetype')) && $img.data('src')) {
$img.css('background-image', "url('" + $img.data('src') + "')");
if (this.field.type !== 'many2many' || this.field.relation !== 'ir.attachment') {
var msg = _t("The type of the field '%s' must be a many2many field with a relation to 'ir.attachment' model.");
throw _.str.sprintf(msg, this.field.string);
}
});
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
this.uploadedFiles = {};
this.uploadingFiles = [];
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
this.accepted_file_extensions = (this.nodeOptions && this.nodeOptions.accepted_file_extensions) || this.accepted_file_extensions || '*';
$(window).on(this.fileupload_id, this._onFileLoaded.bind(this));
this.fileMaxSize = 0;
this.metadata = {};
this._getMaxFileSize();
},
/**
* @private
*/
_onAttach: function () {
// This widget uses a hidden form to upload files. Clicking on 'Attach'
// will simulate a click on the related input.
this.$('.o_input_file').click();
},
/**
* @private
* @param {MouseEvent} ev
*/
_onDelete: function (ev) {
ev.preventDefault();
ev.stopPropagation();
destroy: function () {
this._super();
$(window).off(this.fileupload_id);
},
var fileID = $(ev.currentTarget).data('id');
var record = _.findWhere(this.value.data, {res_id: fileID});
if (record) {
this._setValue({
operation: 'FORGET',
ids: [record.id],
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Compute the URL of an attachment.
*
* @private
* @param {Object} attachment
* @returns {string} URL of the attachment
*/
_getFileUrl: function (attachment) {
// return '/web/content/' + attachment.id + '?download=true';
return '/web/content/' + attachment.id;
},
/**
* Process the field data to add some information (url, etc.).
*
* @private
*/
_generatedMetadata: function () {
var self = this;
_.each(this.value.data, function (record) {
// tagging `allowUnlink` ascertains if the attachment was user
// uploaded or was an existing or system generated attachment
self.metadata[record.id] = {
allowUnlink: self.uploadedFiles[record.data.id] || false,
url: self._getFileUrl(record.data),
};
});
var metadata = this.metadata[record.id];
if (!metadata || metadata.allowUnlink) {
this._rpc({
model: 'ir.attachment',
method: 'unlink',
args: [record.res_id],
},
/**
* @private
* @override
*/
_render: function () {
// render the attachments ; as the attachments will changes after each
// _setValue, we put the rendering here to ensure they will be updated
this._generatedMetadata();
this.$('.oe_placeholder_files, .o_attachments')
.replaceWith($(qweb.render(this.template_files, {
widget: this,
})));
this.$('.oe_fileupload').show();
this.$('.o_image[data-mimetype^="image"]').each(function () {
var $img = $(this);
if (/gif|jpe|jpg|png/.test($img.data('mimetype')) && $img.data('src')) {
$img.css('background-image', "url('" + $img.data('src') + "')");
}
});
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
*/
_onAttach: function () {
// This widget uses a hidden form to upload files. Clicking on 'Attach'
// will simulate a click on the related input.
this.$('.o_input_file').click();
},
/**
* @private
* @param {MouseEvent} ev
*/
_onDelete: function (ev) {
ev.preventDefault();
ev.stopPropagation();
var fileID = $(ev.currentTarget).data('id');
var record = _.findWhere(this.value.data, {
res_id: fileID
});
if (record) {
this._setValue({
operation: 'FORGET',
ids: [record.id],
});
var metadata = this.metadata[record.id];
if (!metadata || metadata.allowUnlink) {
this._rpc({
model: 'ir.attachment',
method: 'unlink',
args: [record.res_id],
});
}
}
}
},
/**
* @private
* @param {Event} ev
*/
_onFileChanged: function (ev) {
var self = this;
ev.stopPropagation();
},
/**
* @private
* @param {Event} ev
*/
_onFileChanged: function (ev) {
var self = this;
ev.stopPropagation();
var files = ev.target.files;
var attachment_ids = this.value.res_ids;
var file_status = true;
var files = ev.target.files;
var attachment_ids = this.value.res_ids;
var file_status = true;
// Don't create an attachment if the upload window is cancelled.
if(files.length === 0)
return;
// Don't create an attachment if the upload window is cancelled.
if (files.length === 0)
return;
_.each(files, function (file) {
var record = _.find(self.value.data, function (attachment) {
return attachment.data.name === file.name;
});
_.each(files, function (file) {
var record = _.find(self.value.data, function (attachment) {
return attachment.data.name === file.name;
});
// check max file size
if (self.fileMaxSize !== 0) {
if(file.size > self.fileMaxSize){
// check max file size
if (file.size > self.fileMaxSize) {
file_status = false;
Dialog.alert(this, _.str.sprintf(_t('The selected file exceeds the maximum file size of %s MB'),(self.fileMaxSize/1024/1024)), {
title: _t("Validation Error"),
self.displayNotification({
type: "danger",
message: _.str.sprintf(_t('The selected file exceeds the maximum file size of %s MB'), (self.fileMaxSize / 1024 / 1024))
});
return false;
}
}
if(file_status){
if (record) {
var metadata = self.metadata[record.id];
if (!metadata || metadata.allowUnlink) {
// there is a existing attachment with the same name so we
// replace it
attachment_ids = _.without(attachment_ids, record.res_id);
self._rpc({
model: 'ir.attachment',
method: 'unlink',
args: [record.res_id],
});
if (file_status) {
if (record) {
var metadata = self.metadata[record.id];
if (!metadata || metadata.allowUnlink) {
// there is a existing attachment with the same name so we
// replace it
attachment_ids = _.without(attachment_ids, record.res_id);
self._rpc({
model: 'ir.attachment',
method: 'unlink',
args: [record.res_id],
});
}
}
self.uploadingFiles.push(file);
}
self.uploadingFiles.push(file);
}
});
});
if (file_status) {
this._setValue({
operation: 'REPLACE_WITH',
ids: attachment_ids,
});
this.$('form.o_form_binary_form').submit();
this.$('.oe_fileupload').hide();
ev.target.value = "";
}
},
/**
* @private
*/
_onFileLoaded: function () {
var self = this;
// the first argument isn't a file but the jQuery.Event
var files = Array.prototype.slice.call(arguments, 1);
// files has been uploaded, clear uploading
this.uploadingFiles = [];
var attachment_ids = this.value.res_ids;
_.each(files, function (file) {
if (file.error) {
self.displayNotification({
title: _t('Uploading Error'),
message: file.error,
type: 'danger'
});
} else {
attachment_ids.push(file.id);
self.uploadedFiles[file.id] = true;
}
});
if(file_status){
this._setValue({
operation: 'REPLACE_WITH',
ids: attachment_ids,
});
},
this.$('form.o_form_binary_form').submit();
this.$('.oe_fileupload').hide();
ev.target.value = "";
}
},
/**
* @private
*/
_onFileLoaded: function () {
var self = this;
// the first argument isn't a file but the jQuery.Event
var files = Array.prototype.slice.call(arguments, 1);
// files has been uploaded, clear uploading
this.uploadingFiles = [];
_previewAttachment: function (ev) {
ev.stopPropagation();
ev.preventDefault();
var self = this;
var activeAttachmentID = $(ev.currentTarget).data('id');
this._rpc({
model: 'ir.attachment',
method: 'read_as_sudo',
kwargs: {
domain: [
['id', 'in', this.value.res_ids]
],
fields: ['id', 'mimetype', 'index_content'],
},
}).then(result => {
this.attachments = result.map(r => {
return {
id: r.id,
mimetype: r.mimetype,
fileType: r.index_content
}
});
var attachmentViewer = new DocumentViewer(this, this.attachments, activeAttachmentID);
attachmentViewer.appendTo($('body'));
})
},
var attachment_ids = this.value.res_ids;
_.each(files, function (file) {
if (file.error) {
self.displayNotification({ title: _t('Uploading Error'), message: file.error, type: 'danger' });
} else {
attachment_ids.push(file.id);
self.uploadedFiles[file.id] = true;
}
});
_getMaxFileSize: async function () {
var self = this;
this._setValue({
operation: 'REPLACE_WITH',
ids: attachment_ids,
});
},
await rpc.query({
model: 'res.users',
method: 'read',
args: [
[session.uid],
['set_restriction', 'max_size']
],
}).then(function (result) {
_previewAttachment: function(ev) {
ev.stopPropagation();
ev.preventDefault();
var self = this;
var activeAttachmentID = $(ev.currentTarget).data('id');
this._rpc({
model: 'ir.attachment',
method: 'read_as_sudo',
kwargs: {
domain: [['id', 'in', this.value.res_ids]],
fields: ['id', 'mimetype', 'index_content'],
},
}).then(result => {
this.attachments = result.map(r => {
return {
id: r.id,
mimetype: r.mimetype,
fileType: r.index_content
if (result[0].set_restriction) {
self.fileMaxSize = result[0].max_size * 1024 * 1024;
}
});
var attachmentViewer = new DocumentViewer(this, this.attachments, activeAttachmentID);
attachmentViewer.appendTo($('body'));
})
},
}
});
_getMaxFileSize: async function () {
var self = this;
await rpc.query({
model: 'res.users',
method: 'read',
args: [[session.uid], ['set_restriction','max_size']],
}).then(function (result) {
if(result[0].set_restriction){
self.fileMaxSize = result[0].max_size * 1024 * 1024;
}
});
}
});
field_registry.add("many2many_attachment_preview", FieldMany2ManyAttachmentPreview);
field_registry.add("many2many_attachment_preview", FieldMany2ManyAttachmentPreview);
});

View File

@ -33,9 +33,10 @@
'maintainer': 'Cybrosys Techno Solutions',
'images': ['static/description/banner.jpg'],
'website': 'https://www.cybrosys.com',
'depends': ['base', 'web'],
'depends': ['base', 'web',],
'data': {
'views/res_users.xml'
'views/res_users.xml',
'views/assets.xml'
},
'license': 'AGPL-3',
'installable': True,

View File

@ -0,0 +1,67 @@
odoo.define('size_restriction_for_attachments/static/src/js/file_uploader_size_restriction.js', function (require) {
"use strict";
const components = {
FileUploader: require("mail/static/src/components/file_uploader/file_uploader.js")
};
const { patch } = require("web.utils");
var session = require('web.session');
var rpc = require('web.rpc');
var core = require('web.core');
var _t = core._t;
patch(components.FileUploader,"size_restriction_for_attachments/static/src/js/file_uploader_size_restriction.js" ,{
/**
* using to set fileMaxSize variable on setup stage
*/
setup () {
this.fileMaxSize = 0;
this._getMaxFileSize();
},
/**
* get max file size from user restriction field
*/
_getMaxFileSize: async function () {
var self = this;
await rpc.query({
model: 'res.users',
method: 'read',
args: [[session.uid], ['set_restriction','max_size']],
}).then(function (result) {
if(result[0].set_restriction){
self.fileMaxSize = result[0].max_size * 1024 * 1024;
}
});
},
/**
* @override
*/
async uploadFiles(files) {
var self = this;
if(files.length > 0){
for (const file of files) {
if (file.size > self.fileMaxSize) {
this.env.services["notification"].notify({
type: "danger",
message: owl.utils.escape(
_.str.sprintf(_t('The selected file exceeds the maximum file size of %s MB'),(self.fileMaxSize/1024/1024))
),
});
return false;
}
}
}
return this._super(files);
},
});
});

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_backend" name="Export Web Backend" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/size_restriction_for_attachments/static/src/js/size.js"></script>
</xpath>
</template>
</odoo>