Thibault Delavallée (OpenERP) has proposed merging
lp:~openerp-dev/openobject-server/trunk-image-standardize-tde into
lp:openobject-server.
Requested reviews:
OpenERP Core Team (openerp)
For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-image-standardize-tde/+merge/112376
Standardize avatars/images
See specifications in
http://pad.openerp.com/internalv61-project-task-3626-YDAVGI2M.
--
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-image-standardize-tde/+merge/112376
Your team OpenERP R&D Team is subscribed to branch
lp:~openerp-dev/openobject-server/trunk-image-standardize-tde.
=== modified file 'openerp/addons/base/res/res_users.py'
--- openerp/addons/base/res/res_users.py 2012-06-19 08:10:05 +0000
+++ openerp/addons/base/res/res_users.py 2012-06-27 15:06:35 +0000
@@ -25,7 +25,6 @@
import pytz
-import io, StringIO
from lxml import etree
from lxml.builder import E
import netsvc
@@ -33,7 +32,6 @@
import openerp.exceptions
from osv import fields,osv
from osv.orm import browse_record
-from PIL import Image
import pooler
import random
from service import security
@@ -152,33 +150,6 @@
body=(self.get_welcome_mail_body(cr, uid, context=context) % user))
return ir_mail_server.send_email(cr, uid, msg, context=context)
- def onchange_avatar(self, cr, uid, ids, value, context=None):
- if not value:
- return {'value': {'avatar_big': value, 'avatar': value} }
- return {'value': {'avatar_big': self._avatar_resize(cr, uid, value, 540, 450, context=context), 'avatar': self._avatar_resize(cr, uid, value, context=context)} }
-
- def _set_avatar(self, cr, uid, id, name, value, args, context=None):
- if not value:
- vals = {'avatar_big': value}
- else:
- vals = {'avatar_big': self._avatar_resize(cr, uid, value, 540, 450, context=context)}
- return self.write(cr, uid, [id], vals, context=context)
-
- def _avatar_resize(self, cr, uid, avatar, height=180, width=150, context=None):
- image_stream = io.BytesIO(avatar.decode('base64'))
- img = Image.open(image_stream)
- img.thumbnail((height, width), Image.ANTIALIAS)
- img_stream = StringIO.StringIO()
- img.save(img_stream, "PNG")
- return img_stream.getvalue().encode('base64')
-
- def _get_avatar(self, cr, uid, ids, name, args, context=None):
- result = dict.fromkeys(ids, False)
- for user in self.browse(cr, uid, ids, context=context):
- if user.avatar_big:
- result[user.id] = self._avatar_resize(cr, uid, user.avatar_big, context=context)
- return result
-
def _set_new_password(self, cr, uid, id, name, value, args, context=None):
if value is False:
# Do not update the password if no value is provided, ignore silently.
@@ -194,6 +165,35 @@
def _get_password(self, cr, uid, ids, arg, karg, context=None):
return dict.fromkeys(ids, '')
+ def _get_image_resized(self, cr, uid, ids, name, args, context=None):
+ result = dict.fromkeys(ids, False)
+ for user in self.browse(cr, uid, ids, context=context):
+ result[user.id] = {'image_medium': False, 'image_small': False}
+ if user.image:
+ result[user.id]['image_medium'] = tools.resize_image_medium(user.image)
+ result[user.id]['image_small'] = tools.resize_image_small(user.image)
+ return result
+
+ def _set_image_resized(self, cr, uid, id, name, value, args, context=None):
+ if not value:
+ vals = {'image': value}
+ else:
+ vals = {'image': tools.resize_image_big(value)}
+ return self.write(cr, uid, [id], vals, context=context)
+
+ def onchange_image(self, cr, uid, ids, value, context=None):
+ if not value:
+ return {'value': {
+ 'image': value,
+ 'image_medium': value,
+ 'image_small': value,
+ }}
+ return {'value': {
+ 'image': tools.resize_image_big(value),
+ 'image_medium': tools.resize_image_medium(value),
+ 'image_small': tools.resize_image_small(value),
+ }}
+
_columns = {
'id': fields.integer('ID'),
'name': fields.char('User Name', size=64, required=True, select=True,
@@ -208,11 +208,28 @@
"otherwise leave empty. After a change of password, the user has to login again."),
'user_email': fields.char('Email', size=64),
'signature': fields.text('Signature', size=64),
- 'avatar_big': fields.binary('Big-sized avatar', help="This field holds the image used as avatar for the user. The avatar field is used as an interface to access this field. The image is base64 encoded, and PIL-supported. It is stored as a 540x450 px image, in case a bigger image must be used."),
- 'avatar': fields.function(_get_avatar, fnct_inv=_set_avatar, string='Avatar', type="binary",
- store = {
- 'res.users': (lambda self, cr, uid, ids, c={}: ids, ['avatar_big'], 10),
- }, help="Image used as avatar for the user. It is automatically resized as a 180x150 px image. This field serves as an interface to the avatar_big field."),
+ 'image': fields.binary("Avatar",
+ help="This field holds the image used as avatar for the "\
+ "user. The avatar field is used as an interface to "\
+ "access this field. The image is base64 encoded, "\
+ "and PIL-supported. It is stored as a 540x450 px "\
+ "image, in case a bigger image must be used."),
+ 'image_medium': fields.function(_get_image_resized, fnct_inv=_set_image_resized,
+ string="Medium-sized avatar", type="binary", multi="_get_image_resized",
+ store = {
+ 'res.users': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
+ },
+ help="Medium-sized image of the user. It is automatically "\
+ "resized as a 180x180px image, with aspect ratio keps. "\
+ "Use this field in form views or some kanban views."),
+ 'image_small': fields.function(_get_image_resized, fnct_inv=_set_image_resized,
+ string="Smal-sized avatar", type="binary", multi="_get_image_resized",
+ store = {
+ 'res.users': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
+ },
+ help="Small-sized image of the user. It is automatically "\
+ "resized as a 50x50px image, with aspect ratio keps. "\
+ "Use this field in form views or some kanban views."),
'active': fields.boolean('Active'),
'action_id': fields.many2one('ir.actions.actions', 'Home Action', help="If specified, this action will be opened at logon for this user, in addition to the standard menu."),
'menu_id': fields.many2one('ir.actions.actions', 'Menu Action', help="If specified, the action will replace the standard menu for this user."),
@@ -321,16 +338,16 @@
pass
return result
- def _get_avatar(self, cr, uid, context=None):
- # default avatar file name: avatar0 -> avatar6.png, choose randomly
- avatar_path = openerp.modules.get_module_resource('base', 'static/src/img', 'avatar%d.png' % random.randint(0, 6))
- return self._avatar_resize(cr, uid, open(avatar_path, 'rb').read().encode('base64'), context=context)
+ def _get_image(self, cr, uid, context=None):
+ # default image file name: avatar0 -> avatar6.png, choose randomly
+ image_path = openerp.modules.get_module_resource('base', 'static/src/img', 'avatar%d.png' % random.randint(0, 6))
+ return tools.resize_image_big(open(image_path, 'rb').read().encode('base64'))
_defaults = {
'password' : '',
'context_lang': lambda self, cr, uid, context: context.get('lang', 'en_US'),
'context_tz': lambda self, cr, uid, context: context.get('tz', False),
- 'avatar': _get_avatar,
+ 'image_medium': _get_image,
'active' : True,
'menu_id': _get_menu,
'company_id': _get_company,
@@ -340,7 +357,7 @@
}
# User can write to a few of her own fields (but not her groups for example)
- SELF_WRITEABLE_FIELDS = ['menu_tips','password', 'signature', 'action_id', 'company_id', 'user_email', 'name', 'avatar', 'avatar_big']
+ SELF_WRITEABLE_FIELDS = ['menu_tips','password', 'signature', 'action_id', 'company_id', 'user_email', 'name', 'image', 'image_medium', 'image_small']
def write(self, cr, uid, ids, values, context=None):
if not hasattr(ids, '__iter__'):
=== modified file 'openerp/addons/base/res/res_users_view.xml'
--- openerp/addons/base/res/res_users_view.xml 2012-06-22 06:12:32 +0000
+++ openerp/addons/base/res/res_users_view.xml 2012-06-27 15:06:35 +0000
@@ -82,7 +82,7 @@
<field name="id" invisible="1"/>
<sheet>
<div class="oe_right">
- <field name="avatar" widget='image' nolabel="1" on_change="onchange_avatar(avatar)" class="oe_avatar"/>
+ <field name="image_medium" widget='image' nolabel="1" on_change="onchange_image(image_medium)" class="oe_avatar"/>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
@@ -196,7 +196,7 @@
<form string="Users" version="7.0">
<sheet>
<div class="oe_right oe_avatar">
- <field name="avatar" widget='image' on_change="onchange_avatar(avatar)"/>
+ <field name="image_small" widget='image' on_change="onchange_image(image_small)"/>
</div>
<div class="oe_title">
<h1>
=== modified file 'openerp/tools/__init__.py'
--- openerp/tools/__init__.py 2011-12-16 16:04:26 +0000
+++ openerp/tools/__init__.py 2012-06-27 15:06:35 +0000
@@ -19,19 +19,20 @@
#
##############################################################################
-import copy
-import win32
+from amount_to_text import *
+from amount_to_text_en import *
from config import config
-from misc import *
from convert import *
-from translate import *
+import copy
+from float_utils import *
from graph import graph
-from amount_to_text import *
-from amount_to_text_en import *
+from image import *
+from misc import *
from pdf_utils import *
+from sql import *
+from translate import *
from yaml_import import *
-from sql import *
-from float_utils import *
+import win32
#.apidoc title: Tools
=== added file 'openerp/tools/image.py'
--- openerp/tools/image.py 1970-01-01 00:00:00 +0000
+++ openerp/tools/image.py 2012-06-27 15:06:35 +0000
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-today OpenERP s.a. (<http://openerp.com>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import io
+from PIL import Image
+import StringIO
+
+def resize_image(base64_source, size=(1024, 1024), encoding='base64', filetype='PNG', avoid_if_small=False):
+ image_stream = io.BytesIO(base64_source.decode(encoding))
+ image = Image.open(image_stream)
+ # check image size: do not create a thumbnail if avoiding smaller images
+ if avoid_if_small and image.size[0] <= size[0] and image.size[1] <= size[1]:
+ return base64_source
+ # create a thumbnail: will resize and keep ratios
+ image.thumbnail(size, Image.ANTIALIAS)
+ # create a transparent image for background
+ background = Image.new('RGBA', size, (255, 255, 255, 0))
+ # past the resized image on the background
+ background.paste(image, ((size[0] - image.size[0]) / 2, (size[1] - image.size[1]) / 2))
+ # return an encoded image
+ background_stream = StringIO.StringIO()
+ background.save(background_stream, filetype)
+ return background_stream.getvalue().encode(encoding)
+
+def resize_image_big(base64_source, size=(1204, 1204), encoding='base64', filetype='PNG'):
+ return resize_image(base64_source, size, encoding, filetype, True)
+
+def resize_image_medium(base64_source, size=(180, 180), encoding='base64', filetype='PNG'):
+ return resize_image(base64_source, size, encoding, filetype)
+
+def resize_image_small(base64_source, size=(50, 50), encoding='base64', filetype='PNG'):
+ return resize_image(base64_source, size, encoding, filetype)
_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help : https://help.launchpad.net/ListHelp