Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12.0 af29d67045486936f1ad5fe49b66e1c297f4560e #703

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
581fe21
:book: IAM has to be under Installation section
Nov 8, 2018
84f4a44
:ambulance: Product Variant were downloaded on server instead of pass…
Nov 8, 2018
e4eea91
:zap: Save resized image to s3 instead of passing original (big) image
Nov 8, 2018
5eec8b9
:rainbow: remove obsolete <odoo><data>
Nov 14, 2018
3d6c024
:rose: correct spelling in docs (#684)
Ramil-Mukhametzyanov Nov 16, 2018
bc375d9
:ambulance: when the "image_resize_image" function was called, they r…
Nov 2, 2018
c044207
Merge branch '11.0' into 11.0-bc375d9170a5576eee8e81d10c2ed980c3541b13
Nov 21, 2018
7f16ab8
:green_heart:
Nov 21, 2018
96ff29d
:ambulance: error openning popup
Nov 22, 2018
168f657
Merge pull request #687 from GabbasovDinar/11.0-web_preview-fix
Nov 26, 2018
3e1a737
:ambulance: forgot to delete when merge
Nov 22, 2018
27e5052
Merge pull request #686 from Rusllan/11.0-bc375d9170a5576eee8e81d10c2…
Dec 5, 2018
0fcbc5a
:ambulance: web_website fixes after odoo update https://github.com/od…
KolushovAlexandr Dec 3, 2018
af29d67
Merge pull request #698 from KolushovAlexandr/11.0-web_website-fixes_…
Dec 10, 2018
13b1acf
Merge remote-tracking branch 'upstream/11.0' into 12.0-af29d670454869…
KolushovAlexandr Dec 12, 2018
1c4db11
:peace_symbol::one::two: some version conflicts in manifests are auto…
KolushovAlexandr Dec 12, 2018
336a3d3
:shield: base_attendance fixed js tours
KolushovAlexandr Dec 4, 2018
f71bdcf
:ambulance: minor fix after merging 11 to 12
KolushovAlexandr Dec 12, 2018
f0233fa
Merge branch '12.0' into 12.0-af29d67045486936f1ad5fe49b66e1c297f4560e
Dec 12, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion base_attendance/security/res_attendance_security.xml
Expand Up @@ -35,7 +35,7 @@
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>

<record id="base.default_user" model="res.users">
<record id="base.user_admin" model="res.users">
<field name="groups_id" eval="[(4,ref('base_attendance.group_hr_attendance_manager'))]"/>
</record>

Expand Down
15 changes: 11 additions & 4 deletions base_attendance/static/src/js/test_kiosk_tour.js
Expand Up @@ -30,16 +30,23 @@ odoo.define('base_attendance.tour', function (require) {
}

var steps = [{
trigger: 'a.oe_menu_toggler:contains("Attendance")',
trigger: 'a.full[href="#"]',
content: _t("Click to open app list"),
position: 'bottom',
}, {
trigger: 'a.dropdown-item.o_app:contains("Attendance")',
content: _t("Click to enter menu attendances"),
position: 'bottom',
}, {
trigger: 'a.oe_menu_leaf:contains("Kiosk")',
trigger: 'a.dropdown-toggle.o-no-caret.o_menu_header_lvl_1:contains("Attendance")',
content: _t("Click to open Manage Attendances menu"),
}, {
trigger: 'a.dropdown-item.o_menu_entry_lvl_2:contains("Kiosk")',
content: _t("Click to enter Kiosk"),
}];

steps = steps.concat(partner_check_in_out("Laith Jubair", 'red'));
steps = steps.concat(partner_check_in_out("Laith Jubair", 'green'));
steps = steps.concat(partner_check_in_out("Brandon Freeman", 'red'));
steps = steps.concat(partner_check_in_out("Brandon Freeman", 'green'));

tour.register('test_kiosk_tour', { test: true, url: '/web' }, steps);
});
1 change: 1 addition & 0 deletions ir_attachment_s3/__init__.py
@@ -1,2 +1,3 @@

from . import models
from . import controllers
3 changes: 2 additions & 1 deletion ir_attachment_s3/__manifest__.py
Expand Up @@ -3,7 +3,7 @@
"summary": """Upload attachments on Amazon S3""",
"category": "Tools",
"images": [],
"version": "11.0.1.1.2",
"version": "11.0.1.2.0",
"application": False,

"author": "IT-Projects LLC, Ildar Nasyrov",
Expand All @@ -18,6 +18,7 @@
],
"external_dependencies": {"python": ['boto3'], "bin": []},
"data": [
"security/ir.model.access.csv",
"views/res_config_settings_views.xml",
],
"qweb": [
Expand Down
1 change: 1 addition & 0 deletions ir_attachment_s3/controllers/__init__.py
@@ -0,0 +1 @@
from . import main
88 changes: 88 additions & 0 deletions ir_attachment_s3/controllers/main.py
@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import logging
import werkzeug

import odoo
from odoo.http import request, route
from odoo.addons.web.controllers.main import Binary
# TODO some code can be part of ir_attachment_url

_logger = logging.getLogger(__name__)


class BinaryExtended(Binary):

def redirect_to_url(self, url):
return werkzeug.utils.redirect(url, code=301)

@route()
def content_image(self, xmlid=None, model='ir.attachment', id=None, field='datas', filename_field='datas_fname', unique=None, filename=None, mimetype=None, download=None, width=0, height=0):

res = super(BinaryExtended, self).content_image(xmlid, model, id, field, filename_field, unique, filename, mimetype, download, width, height)

if not (res.status_code == 301 and (width or height)):
return res

# * check that it's image on s3
# * upload resized image if needed
# * return url to resized image

# FIND ATTACHMENT. The code is copy-pasted from binary_content method
env = request.env
# get object and content
obj = None
if xmlid:
obj = env.ref(xmlid, False)
elif id and model in env.registry:
obj = env[model].browse(int(id))

attachment = None
if model == 'ir.attachment':
attachment = obj
else:
attachment = env['ir.http'].find_field_attachment(env, model, field, obj)

if not attachment:
# imposible case?
_logger.error('Attachment is not found')
return res

# FIX SIZES
height = int(height or 0)
width = int(width or 0)
# resize maximum 500*500
if width > 500:
width = 500
if height > 500:
height = 500

# CHECK FOR CACHE.
# We may already uploaded that resized image
cache = env['ir.attachment.resized'].sudo().search([
('attachment_id', '=', attachment.id),
('width', '=', width),
('height', '=', height),
])
if cache:
url = cache.resized_attachment_id.url
return self.redirect_to_url(url)

# PREPARE CACHE
content = attachment.datas
content = odoo.tools.image_resize_image(base64_source=content, size=(width or None, height or None), encoding='base64', filetype='PNG')
resized_attachment = env['ir.attachment'].with_context(force_s3=True).create({
'name': '%sx%s %s' % (width, height, attachment.name),
'datas': content,
})

env['ir.attachment.resized'].sudo().create({
'attachment_id': attachment.id,
'width': width,
'height': height,
'resized_attachment_id': resized_attachment.id,
})

url = resized_attachment.url
return self.redirect_to_url(url)
5 changes: 5 additions & 0 deletions ir_attachment_s3/doc/changelog.rst
@@ -1,3 +1,8 @@
`1.2.0`
-------

- **Improvement:** Save resized image to s3 instead of passing original (big) image

`1.1.2`
-------

Expand Down
30 changes: 29 additions & 1 deletion ir_attachment_s3/doc/index.rst
Expand Up @@ -5,14 +5,42 @@
Installation
============

* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way
* `Using this quickstart instruction <https://boto3.readthedocs.io/en/latest/guide/quickstart.html>`__ install boto3 library and get credentials for it
* `Using this instruction <http://mikeferrier.com/2011/10/27/granting-access-to-a-single-s3-bucket-using-amazon-iam>`__ grant access to your s3 bucket
* Set your S3 bucket as public
* Optionaly, add following parameter to prevent heavy logs from boto3 library:

--log-handler=boto3.resources.action:WARNING


IAM
---

Minimal access policy for s3 credentials are as following::

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:CreateBucket",
"s3:GetBucketLocation",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::YOUBUCKETNAMEHERE",
"arn:aws:s3:::YOUBUCKETNAMEHERE/*"
]
}
]
}


You can also remove ``"s3:CreateBucket"`` if bucket already exists.

Configuration
=============

Expand Down
26 changes: 23 additions & 3 deletions ir_attachment_s3/models/ir_attachment.py
@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright 2016-2018 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# Copyright 2016-2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
import base64
import os
import hashlib
import logging

from odoo import api, models, _
from odoo import api, models, _, fields
from odoo.tools.safe_eval import safe_eval

_logger = logging.getLogger(__name__)
Expand All @@ -15,9 +18,21 @@
found on your installation')


class IrAttachmentResized(models.Model):
_name = 'ir.attachment.resized'
_description = 'Url to resized image'

attachment_id = fields.Many2one('ir.attachment')
width = fields.Integer()
height = fields.Integer()
resized_attachment_id = fields.Many2one('ir.attachment', ondelete='cascade')


class IrAttachment(models.Model):
_inherit = 'ir.attachment'

resized_ids = fields.One2many('ir.attachment.resized', 'attachment_id')

def _get_s3_settings(self, param_name, os_var_name):
config_obj = self.env['ir.config_parameter']
res = config_obj.sudo().get_param(param_name)
Expand Down Expand Up @@ -61,11 +76,12 @@ def _get_s3_resource(self):

def _inverse_datas(self):
condition = self._get_s3_settings('s3.condition', 'S3_CONDITION')
if condition:
if condition and not self.env.context.get('force_s3'):
condition = safe_eval(condition, mode="eval")
s3_records = self.sudo().search([('id', 'in', self.ids)] + condition)
else:
# if there is no condition then store all attachments on s3
# if there is no condition or force_s3 in context
# then store all attachments on s3
s3_records = self

if s3_records:
Expand All @@ -77,7 +93,9 @@ def _inverse_datas(self):
s3_records = s3_records._filter_protected_attachments()
s3_records = s3_records.filtered(lambda r: r.type != 'url')

resized_to_remove = self.env['ir.attachment.resized'].sudo()
for attach in self & s3_records: # datas field has got empty somehow in the result of ``s3_records = self.sudo().search([('id', 'in', self.ids)] + condition)`` search for non-superusers but it is in original recordset. Here we use original (with datas) in case it intersects with the search result
resized_to_remove |= attach.sudo().resized_ids
value = attach.datas
bin_data = base64.b64decode(value) if value else b''
fname = hashlib.sha1(bin_data).hexdigest()
Expand All @@ -101,4 +119,6 @@ def _inverse_datas(self):
}
super(IrAttachment, attach.sudo()).write(vals)

resized_to_remove.mapped('resized_attachment_id').unlink()
resized_to_remove.unlink()
super(IrAttachment, self - s3_records)._inverse_datas()
2 changes: 2 additions & 0 deletions ir_attachment_s3/security/ir.model.access.csv
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ir_attachment_resized,access_ir_attachment_resized,model_ir_attachment_resized,base.group_user,1,0,0,0
2 changes: 1 addition & 1 deletion ir_attachment_url/__manifest__.py
Expand Up @@ -3,7 +3,7 @@
"summary": """Use attachment URL and upload data to external storage""",
"category": "Tools",
"images": [],
"version": "12.0.1.1.6",
"version": "12.0.1.1.7",
"application": False,

"author": "IT-Projects LLC, Ildar Nasyrov",
Expand Down
5 changes: 5 additions & 0 deletions ir_attachment_url/doc/changelog.rst
@@ -1,3 +1,8 @@
`1.1.7`
-------

- **Fix:** Product Variant were downloaded on server instead of passing url

`1.1.6`
-------

Expand Down
53 changes: 38 additions & 15 deletions ir_attachment_url/models/ir_http.py
@@ -1,3 +1,7 @@
# Copyright 2016-2018 Ildar Nasyrov <https://it-projects.info/team/iledarn>
# Copyright 2017 Dinar Gabbasov <https://it-projects.info/team/GabbasovDinar>
# Copyright 2016-2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import mimetypes
import base64
import hashlib
Expand All @@ -15,6 +19,33 @@
class IrHttp(models.AbstractModel):
_inherit = 'ir.http'

@classmethod
def _find_field_attachment(cls, env, m, f, id):
domain = [
('res_model', '=', m),
('res_field', '=', f),
('res_id', '=', id),
('type', '=', 'url'),
]
return env['ir.attachment'].sudo().search(domain)

@classmethod
def find_field_attachment(cls, env, model, field, obj):
is_attachment = env[model]._fields[field].attachment
is_product = model == 'product.product' and field.startswith('image')
if not (is_attachment or is_product):
return None

att = cls._find_field_attachment(env, model, field, obj.id)

if not att and model == 'product.product':
# Special case. Product.product's image are computed and
# use product.template's image in most cases. But due to
# this computation odoo pass binary data (by downloading it
# from s3) instead of url. So, make a workaround for it
att = cls._find_field_attachment(env, 'product.template', field, obj.product_tmpl_id.id)
return att

@classmethod
def binary_content(cls, xmlid=None, model='ir.attachment', id=None, field='datas',
unique=False, filename=None, filename_field='datas_fname', download=False,
Expand Down Expand Up @@ -83,8 +114,7 @@ def binary_content(cls, xmlid=None, model='ir.attachment', id=None, field='datas
if module_resource_path.startswith(module_path):
with open(module_resource_path, 'rb') as f:
content = base64.b64encode(f.read())
# lint error fix (unused variable)
# last_update = pycompat.text_type(os.path.getmtime(module_resource_path))
# 'last_update' variable removed for lint error fix

if not module_resource_path:
module_resource_path = obj.url
Expand All @@ -93,19 +123,12 @@ def binary_content(cls, xmlid=None, model='ir.attachment', id=None, field='datas
status = 301
content = module_resource_path
else:
# begin redefined part of original binary_content of odoo/base/addons/models/ir_http
is_attachment = env[model]._fields[field].attachment
if is_attachment:
domain = [
('res_model', '=', model),
('res_field', '=', field),
('res_id', '=', obj.id),
('type', '=', 'url'),
]
att = env['ir.attachment'].sudo().search(domain)
if att:
content = att.url
status = 301
# begin redefined part of original binary_content of odoo/base/addons/ir/ir_http
att = env['ir.http'].find_field_attachment(env, model, field, obj)
if att:
content = att.url
status = 301

if not content:
content = obj[field] or ''
# end redefined part of original binary_content
Expand Down
2 changes: 1 addition & 1 deletion web_preview/__manifest__.py
Expand Up @@ -3,7 +3,7 @@
"summary": """Open attached images in popup""",
"category": "Web",
"images": ['images/screenshot-1.png'],
"version": "11.0.1.0.0",
"version": "11.0.1.0.1",
"application": False,

"author": "IT-Projects LLC, Dinar Gabbasov",
Expand Down
7 changes: 6 additions & 1 deletion web_preview/doc/changelog.rst
@@ -1,4 +1,9 @@
`1.0.1`
-------

**Fix:** Incorrect opening of the popup when using multiple binary fields in the model

`1.0.0`
-------

- Init version
**Init version**