mirror of
https://github.com/OCA/knowledge.git
synced 2025-07-27 19:08:42 -06:00
[8.0][ADD] attachment_zipped_download
This commit is contained in:
parent
484cc67eff
commit
19be196209
3
attachment_zipped_download/__init__.py
Normal file
3
attachment_zipped_download/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import controllers
|
||||
from . import models
|
16
attachment_zipped_download/__openerp__.py
Normal file
16
attachment_zipped_download/__openerp__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2022 Tecnativa - Víctor Martínez
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
{
|
||||
"name": "Attachment Zipped Download",
|
||||
"version": "8.0.1.0.0",
|
||||
"category": "Tools",
|
||||
"website": "https://github.com/OCA/knowledge",
|
||||
"author": "Tecnativa, Odoo Community Association (OCA)",
|
||||
"license": "AGPL-3",
|
||||
"depends": ["base"],
|
||||
"data": [
|
||||
"views/ir_attachment_view.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
2
attachment_zipped_download/controllers/__init__.py
Normal file
2
attachment_zipped_download/controllers/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import main
|
23
attachment_zipped_download/controllers/main.py
Normal file
23
attachment_zipped_download/controllers/main.py
Normal file
@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 César Fernández Domínguez <cesfernandez@outlook.com>
|
||||
# Copyright 2022 Tecnativa - Víctor Martínez
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
from openerp import _, http
|
||||
from openerp.http import request
|
||||
|
||||
|
||||
class AttachmentZippedDownloadController(http.Controller):
|
||||
|
||||
@http.route("/web/attachment/download_zip", type="http", auth="user")
|
||||
def download_zip(self, ids=None, debug=0):
|
||||
ids = [] if not ids else ids
|
||||
if len(ids) == 0:
|
||||
return
|
||||
list_ids = map(int, ids.split(","))
|
||||
out_file = request.env["ir.attachment"].browse(list_ids)._create_temp_zip()
|
||||
return http.send_file(
|
||||
filepath_or_fp=out_file,
|
||||
mimetype="application/zip",
|
||||
as_attachment=True,
|
||||
filename=_("attachments.zip"),
|
||||
)
|
3
attachment_zipped_download/models/__init__.py
Normal file
3
attachment_zipped_download/models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import ir_attachment
|
||||
from . import ir_attachment_action_download
|
52
attachment_zipped_download/models/ir_attachment.py
Normal file
52
attachment_zipped_download/models/ir_attachment.py
Normal file
@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 César Fernández Domínguez <cesfernandez@outlook.com>
|
||||
# Copyright 2022 Tecnativa - Víctor Martínez
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
import base64
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
|
||||
from openerp import _, api, models
|
||||
from openerp.exceptions import Warning as UserError
|
||||
|
||||
|
||||
class IrAttachment(models.Model):
|
||||
_inherit = "ir.attachment"
|
||||
|
||||
@api.multi
|
||||
def action_attachments_download(self):
|
||||
items = self.filtered(lambda x: x.type == "binary")
|
||||
if not items:
|
||||
raise UserError(
|
||||
_("None attachment selected. Only binary attachments allowed.")
|
||||
)
|
||||
item_names = [it._compute_zip_file_name() for it in items]
|
||||
if len(item_names) != len(set(item_names)):
|
||||
raise UserError(
|
||||
_("All file names must be unique.")
|
||||
)
|
||||
ids = ",".join(map(str, items.ids))
|
||||
return {
|
||||
"type": "ir.actions.act_url",
|
||||
"url": "/web/attachment/download_zip?ids=%s" % (ids),
|
||||
"target": "self",
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def _create_temp_zip(self):
|
||||
zip_buffer = BytesIO()
|
||||
with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
|
||||
for attachment in self:
|
||||
attachment.check("read")
|
||||
zip_file.writestr(
|
||||
attachment._compute_zip_file_name(),
|
||||
base64.b64decode(attachment.datas),
|
||||
)
|
||||
zip_file.close()
|
||||
return zip_buffer
|
||||
|
||||
@api.multi
|
||||
def _compute_zip_file_name(self):
|
||||
"""Give a chance of easily changing the name of the file inside the ZIP."""
|
||||
self.ensure_one()
|
||||
return self.name
|
@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2023 Foodles (https://www.foodles.com/)
|
||||
# @author Pierre Verkest <pierreverkest84@gmail.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from openerp import _, api, models
|
||||
|
||||
|
||||
class IrAttachmentActionDownloadMixin(models.AbstractModel):
|
||||
_name = "ir.attachment.action_download"
|
||||
_description = """
|
||||
Mixin to help download attachments linked to record(s).
|
||||
"""
|
||||
|
||||
@api.multi
|
||||
def _get_downloadable_attachments(self):
|
||||
"""Give a chance to easily overwrite this method
|
||||
on sub modules to limit restict attachement able to downloads
|
||||
|
||||
In some case we probably want the user download some specific
|
||||
document that are probably related to the current model
|
||||
|
||||
By default return all attachment link the the record.
|
||||
"""
|
||||
return self.env["ir.attachment"].search(
|
||||
[("res_model", "=", self._name), ("res_id", "in", self.ids)]
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def action_download_attachments(self):
|
||||
"""Return action to:
|
||||
* emit a warning message if no attachment found
|
||||
* download a file if only 1 file found
|
||||
* zip and download the list of attachment returns by `_get_downloadable_attachments`
|
||||
"""
|
||||
attachments = self._get_downloadable_attachments()
|
||||
if not attachments:
|
||||
title = _("No attachment!")
|
||||
text = _("There is no document found to download.")
|
||||
return {
|
||||
"type": "ir.actions.client",
|
||||
"tag": "action_warn",
|
||||
"params": {
|
||||
"title": title,
|
||||
"text": text,
|
||||
"sticky": True,
|
||||
},
|
||||
}
|
||||
|
||||
if len(attachments) == 1:
|
||||
return {
|
||||
"target": "self",
|
||||
"type": "ir.actions.act_url",
|
||||
"url": "/web/binary/saveas?model=ir.attachment&field=datas&filename_field=name&id=%s" % attachments.id,
|
||||
}
|
||||
else:
|
||||
return attachments.action_attachments_download()
|
12
attachment_zipped_download/readme/CONTRIBUTORS.rst
Normal file
12
attachment_zipped_download/readme/CONTRIBUTORS.rst
Normal file
@ -0,0 +1,12 @@
|
||||
* César Fernández Domínguez <cesfernandez@outlook.com>
|
||||
|
||||
* `Tecnativa <https://www.tecnativa.com>`_:
|
||||
|
||||
* Víctor Martínez
|
||||
* Pedro M. Baeza
|
||||
|
||||
* Pierre Verkest <pierreverkest@gmail.com>
|
||||
|
||||
* `Ecosoft <https://ecosoft.co.th>`_:
|
||||
|
||||
* Tharathip Chaweewongphan <tharathipc@ecosoft.co.th>
|
4
attachment_zipped_download/readme/DESCRIPTION.rst
Normal file
4
attachment_zipped_download/readme/DESCRIPTION.rst
Normal file
@ -0,0 +1,4 @@
|
||||
This module allows downloading multiple attachments as a zip file.
|
||||
|
||||
This also provide a helper class `IrAttachmentActionDownloadMixin`
|
||||
to be used by developer to add action method on models.
|
86
attachment_zipped_download/readme/USAGE.rst
Normal file
86
attachment_zipped_download/readme/USAGE.rst
Normal file
@ -0,0 +1,86 @@
|
||||
#. Go to *Settings > Technical > Database Structure > Attachments* and select some files.
|
||||
#. Go to *More > Download* and a zip file containing the selected files will be downloaded.
|
||||
|
||||
## For developer
|
||||
|
||||
You can reuse the `IrAttachmentActionDownloadMixin` on your
|
||||
favorite models::
|
||||
|
||||
from openerp import models
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_name = "stock.picking"
|
||||
_inherit = ["stock.picking", "ir.attachment.action_download"]
|
||||
|
||||
|
||||
Then you can add an action button on list view line or on the action button
|
||||
(when multiple lines are selected) to download all files::
|
||||
|
||||
<openerp>
|
||||
<!--
|
||||
add a button on list view to download all attachement present
|
||||
on the given transfert
|
||||
-->
|
||||
<record id="vpicktree" model="ir.ui.view">
|
||||
<field name="inherit_id" ref="stock.vpicktree"/>
|
||||
<field name="name">stock.picking.tree download attachments</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="picking_type_id" position="after">
|
||||
<button name="action_download_attachments"
|
||||
type="object"
|
||||
icon="fa-download"
|
||||
string="Download attachment(s)"
|
||||
aria-label="Download Proof documents"
|
||||
class="float-right"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Add "Download attachments" item in the Action menu when
|
||||
multiple records are selected
|
||||
-->
|
||||
<record id="action_download_picking_attachements" model="ir.actions.server">
|
||||
<field name="name">Download attachments</field>
|
||||
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = self.action_download_attachments(cr, uid, context.get('active_ids', []), context=context)
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_download_picking_attachements_values" model="ir.values">
|
||||
<field name="name">Download attachments</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
<field
|
||||
name="value"
|
||||
eval="'ir.actions.server,%d'%action_download_picking_attachements"
|
||||
/>
|
||||
<field name="object" eval="True"/>
|
||||
</record>
|
||||
</openerp>
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Even you will be able to generate a zip file with multiple document with the
|
||||
same name it's advice to overwrite `_compute_zip_file_name` to improve the
|
||||
name. When a slash (`/`) is present in the path it will create a directory.
|
||||
This example will create a directory per stock.picking using its name::
|
||||
|
||||
class IrAttachment(models.Model):
|
||||
_inherit = "ir.attachment"
|
||||
|
||||
def _compute_zip_file_name(self):
|
||||
self.ensure_one()
|
||||
if self.res_model and self.res_model == "stock.picking":
|
||||
return (
|
||||
self.env[self.res_model]
|
||||
.browse(self.res_id)
|
||||
.display_name.replace("/", "-")
|
||||
+ "/"
|
||||
+ self.name
|
||||
)
|
||||
return super()._compute_zip_file_name()
|
BIN
attachment_zipped_download/static/description/icon.png
Normal file
BIN
attachment_zipped_download/static/description/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
23
attachment_zipped_download/views/ir_attachment_view.xml
Normal file
23
attachment_zipped_download/views/ir_attachment_view.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="action_attachments_download" model="ir.actions.server">
|
||||
<field name="name">Download</field>
|
||||
<field name="model_id" ref="base.model_ir_attachment" />
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = self.action_attachments_download(cr, uid, context.get('active_ids', []), context=context)
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_attachments_download_values" model="ir.values">
|
||||
<field name="name">Download</field>
|
||||
<field name="model">ir.attachment</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
<field
|
||||
name="value"
|
||||
eval="'ir.actions.server,%d'%action_attachments_download"
|
||||
/>
|
||||
<field name="object" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
Loading…
Reference in New Issue
Block a user