[IMP] *: adds generic user avatar

Description of the issue/feature this PR addresses:
It is currently quite difficult to differentiate users. Most of the time, people
don't take the time to upload an actual avatar so everybody looks the same. This
PR generates a custom avatar with the users initials and random color to
differentiate them. For res.users, res.partner and hr.employee, image fields now
hold the binary image and avatar are used to show the image or svg.

Current behavior before PR:
Avatar had only random colors and was being saved in database, being inefficient

Desired behavior after PR is merged:
A new mixin defines image fields and in case no image is set, it generates an
SVG image with the user's initials and random color.

closes odoo/odoo#69819

Task: 2404630
Related: odoo/enterprise#18199
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
Leonardo Pavan Rocha
2021-06-01 14:36:23 +00:00
parent 2215e69dae
commit c53724ebc3
95 changed files with 320 additions and 241 deletions
+1 -1
View File
@@ -111,7 +111,7 @@
<field name="name"/>
</em>
<div class="col-2 text-right">
<img t-att-src="kanban_image('res.partner', 'image_128', record.partner_id.raw_value)" t-att-title="record.partner_id.value" t-att-alt="record.partner_id.value" class="oe_kanban_avatar o_image_24_cover"/>
<img t-att-src="kanban_image('res.partner', 'avatar_128', record.partner_id.raw_value)" t-att-title="record.partner_id.value" t-att-alt="record.partner_id.value" class="oe_kanban_avatar o_image_24_cover"/>
</div>
</div>
<div class="row">
@@ -118,8 +118,7 @@
</strong></div>
<div class="row">
<div class="col flex-grow-0 pr-2">
<img t-if="invoice.invoice_user_id.image_1024" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(invoice.invoice_user_id.image_1024)" alt="Contact"/>
<img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/img/user_menu_avatar.png" alt="Contact"/>
<img class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(invoice.invoice_user_id.avatar_1024)" alt="Contact"/>
</div>
<div class="col pl-0">
<span t-field="invoice.invoice_user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/>
@@ -79,7 +79,7 @@ QUnit.module('calendar', {
"should have img tag");
assert.hasAttrValue(form.$('.o_field_many2manytags.avatar.o_field_widget .badge:first img'),
'data-src',
'/web/image/partner/1/image_128',
'/web/image/partner/1/avatar_128',
"should have correct avatar image");
form.destroy();
+1 -1
View File
@@ -277,7 +277,7 @@
<field name="videocall_location" string="Join Video Call" widget="CopyClipboardURL" />
<field name="attendee_status" invisible="1"/>
<field name="partner_id" string="Organizer"/>
<field name="partner_ids" filters="1" widget="many2manyattendee" write_model="calendar.contacts" write_field="partner_id" filter_field="partner_checked" avatar_field="image_128"/>
<field name="partner_ids" filters="1" widget="many2manyattendee" write_model="calendar.contacts" write_field="partner_id" filter_field="partner_checked" avatar_field="avatar_128"/>
<field name="is_highlighted" invisible="1"/>
<field name="is_organizer_alone" invisible="1"/>
<field name="description" attrs="{'invisible': [('description', '=', False)]}"/>
+2 -2
View File
@@ -428,7 +428,7 @@
<field name="arch" type="xml">
<calendar string="Leads Generation" create="0" mode="month" date_start="activity_date_deadline" color="user_id" hide_time="true" event_limit="5">
<field name="expected_revenue"/>
<field name="partner_id" avatar_field="image_128"/>
<field name="partner_id" avatar_field="avatar_128"/>
<field name="user_id" filters="1" invisible="1"/>
</calendar>
</field>
@@ -498,7 +498,7 @@
<field name="company_currency"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('res.users', 'image_128', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value"/>
<img t-att-src="activity_image('res.users', 'avatar_128', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value"/>
<div>
<field name="name" display="full"/>
<field name="expected_revenue" widget="monetary" display="full" muted="1"/>
+1 -1
View File
@@ -408,7 +408,7 @@
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<calendar date_start="date_begin" date_stop="date_end" string="Event Organization" mode="month" color="event_type_id" event_limit="5">
<field name="user_id" avatar_field="image_128"/>
<field name="user_id" avatar_field="avatar_128"/>
<field name="seats_expected"/>
<field name="seats_reserved"/>
<field name="seats_used"/>
@@ -167,7 +167,7 @@
<field name="purchaser_id"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('res.partner', 'image_128', record.purchaser_id.raw_value)" t-att-title="record.purchaser_id.value" t-att-alt="record.purchaser_id.value"/>
<img t-att-src="activity_image('res.partner', 'avatar_128', record.purchaser_id.raw_value)" t-att-title="record.purchaser_id.value" t-att-alt="record.purchaser_id.value"/>
<div>
<field name="vehicle_id" display="full"/>
</div>
+1 -1
View File
@@ -148,7 +148,7 @@
<em><field name="description"/></em>
<div>
<t t-foreach="record.unique_owner_ids.raw_value.slice(0,11)" t-as="owner">
<img class="oe_kanban_avatar o_image_24_cover" t-att-src="kanban_image('res.users', 'image_128', owner)" t-att-data-member_id="owner" alt="Owner"/>
<img class="oe_kanban_avatar o_image_24_cover" t-att-src="kanban_image('res.users', 'avatar_128', owner)" t-att-data-member_id="owner" alt="Owner"/>
</t>
</div>
</div>
+1 -1
View File
@@ -156,7 +156,7 @@
</div>
</strong>
<t t-foreach="record.user_ids.raw_value.slice(0,11)" t-as="member">
<img class="o_kanban_badge_avatars" t-att-src="kanban_image('res.users', 'image_128', member)" t-att-data-member_id="member" alt="Member"/>
<img class="o_kanban_badge_avatars" t-att-src="kanban_image('res.users', 'avatar_128', member)" t-att-data-member_id="member" alt="Member"/>
</t>
</div>
</div>
+1 -1
View File
@@ -153,7 +153,7 @@
<div class="o_kanban_content">
<p><strong><h4 class="oe_goal_name text-center" tooltip="kanban-tooltip"><field name="definition_id" /></h4></strong></p>
<div class="float-left">
<img class="o_image_24_cover" t-att-src="kanban_image('res.users', 'image_128', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value"/>
<img class="o_image_24_cover" t-att-src="kanban_image('res.users', 'avatar_128', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value"/>
</div>
<field name="user_id" />
<div class="o_goal_state_block">
-1
View File
@@ -14,7 +14,6 @@
<field name="name">Administrator</field>
<field name="company_id" ref="base.main_company"/>
<field name="email">admin@example.com</field>
<field name="image_1920" type="base64" file="base/static/img/avatar_grey.png"/>
<field name="type">private</field>
</record>
+31 -9
View File
@@ -10,9 +10,8 @@ from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.osv.query import Query
from odoo.exceptions import ValidationError, AccessError
from odoo.modules.module import get_module_resource
from odoo.tools.misc import format_date
from odoo.osv import expression
from odoo.tools.misc import format_date
class HrEmployeePrivate(models.Model):
@@ -26,7 +25,7 @@ class HrEmployeePrivate(models.Model):
_name = "hr.employee"
_description = "Employee"
_order = 'name'
_inherit = ['hr.employee.base', 'mail.thread', 'mail.activity.mixin', 'resource.mixin', 'image.mixin']
_inherit = ['hr.employee.base', 'mail.thread', 'mail.activity.mixin', 'resource.mixin', 'avatar.mixin']
_mail_post_access = 'read'
# resource and user
@@ -100,7 +99,6 @@ class HrEmployeePrivate(models.Model):
km_home_work = fields.Integer(string="Home-Work Distance", groups="hr.group_hr_user", tracking=True)
job_id = fields.Many2one(tracking=True)
image_1920 = fields.Image()
phone = fields.Char(related='address_home_id.phone', related_sudo=False, readonly=False, string="Private Phone", groups="hr.group_hr_user")
# employee in company
child_ids = fields.One2many('hr.employee', 'parent_id', string='Direct subordinates')
@@ -125,11 +123,35 @@ class HrEmployeePrivate(models.Model):
('user_uniq', 'unique (user_id, company_id)', "A user cannot be linked to multiple employees in the same company.")
]
def _get_placeholder_filename(self, field=None):
image_fields = ['image_%s' % size for size in [1920, 1024, 512, 256, 128]]
if field in image_fields:
return 'hr/static/src/img/default_image.png'
return super()._get_placeholder_filename(field=field)
@api.depends('name', 'user_id.avatar_1920', 'image_1920')
def _compute_avatar_1920(self):
super()._compute_avatar_1920()
@api.depends('name', 'user_id.avatar_1024', 'image_1024')
def _compute_avatar_1024(self):
super()._compute_avatar_1024()
@api.depends('name', 'user_id.avatar_512', 'image_512')
def _compute_avatar_512(self):
super()._compute_avatar_512()
@api.depends('name', 'user_id.avatar_256', 'image_256')
def _compute_avatar_256(self):
super()._compute_avatar_256()
@api.depends('name', 'user_id.avatar_128', 'image_128')
def _compute_avatar_128(self):
super()._compute_avatar_128()
def _compute_avatar(self, avatar_field, image_field):
for employee in self:
avatar = employee[image_field]
if not avatar:
if employee.user_id:
avatar = employee.user_id[avatar_field]
else:
avatar = employee._avatar_get_placeholder()
employee[avatar_field] = avatar
def name_get(self):
if self.check_access_rights('read', raise_exception=False):
+16 -13
View File
@@ -32,26 +32,29 @@ class HrEmployeePublic(models.Model):
color = fields.Integer(readonly=True)
employee_type = fields.Selection(readonly=True)
employee_id = fields.Many2one('hr.employee', 'Employee', compute="_compute_employee_id", search="_search_employee_id", compute_sudo=True)
# hr.employee.public specific fields
child_ids = fields.One2many('hr.employee.public', 'parent_id', string='Direct subordinates', readonly=True)
image_1920 = fields.Image("Original Image", compute='_compute_image', compute_sudo=True)
image_1024 = fields.Image("Image 1024", compute='_compute_image', compute_sudo=True)
image_512 = fields.Image("Image 512", compute='_compute_image', compute_sudo=True)
image_256 = fields.Image("Image 256", compute='_compute_image', compute_sudo=True)
image_128 = fields.Image("Image 128", compute='_compute_image', compute_sudo=True)
image_1920 = fields.Image("Image", related='employee_id.image_1920', compute_sudo=True)
image_1024 = fields.Image("Image 1024", related='employee_id.image_1024', compute_sudo=True)
image_512 = fields.Image("Image 512", related='employee_id.image_512', compute_sudo=True)
image_256 = fields.Image("Image 256", related='employee_id.image_256', compute_sudo=True)
image_128 = fields.Image("Image 128", related='employee_id.image_128', compute_sudo=True)
avatar_1920 = fields.Image("Avatar", related='employee_id.avatar_1920', compute_sudo=True)
avatar_1024 = fields.Image("Avatar 1024", related='employee_id.avatar_1024', compute_sudo=True)
avatar_512 = fields.Image("Avatar 512", related='employee_id.avatar_512', compute_sudo=True)
avatar_256 = fields.Image("Avatar 256", related='employee_id.avatar_256', compute_sudo=True)
avatar_128 = fields.Image("Avatar 128", related='employee_id.avatar_128', compute_sudo=True)
parent_id = fields.Many2one('hr.employee.public', 'Manager', readonly=True)
coach_id = fields.Many2one('hr.employee.public', 'Coach', readonly=True)
user_partner_id = fields.Many2one(related='user_id.partner_id', related_sudo=False, string="User's partner")
def _compute_image(self):
def _search_employee_id(self, operator, value):
return [('id', operator, value)]
def _compute_employee_id(self):
for employee in self:
# We have to be in sudo to have access to the images
employee_id = self.sudo().env['hr.employee'].browse(employee.id)
employee.image_1920 = employee_id.image_1920
employee.image_1024 = employee_id.image_1024
employee.image_512 = employee_id.image_512
employee.image_256 = employee_id.image_256
employee.image_128 = employee_id.image_128
employee.employee_id = self.env['hr.employee'].browse(employee.id)
@api.model
def _get_fields(self):
+1 -1
View File
@@ -26,7 +26,7 @@
</tr>
<tr style="height:70%;">
<td align="center" valign="center">
<img t-if="employee.image_1920" t-att-src="image_data_uri(employee.image_1920)" style="max-height:85pt;max-width:90%" alt="Employee Image"/>
<img t-att-src="image_data_uri(employee.avatar_1920)" style="max-height:85pt;max-width:90%" alt="Employee Image"/>
</td>
</tr>
</table>
@@ -156,10 +156,10 @@ QUnit.module('hr', {}, function () {
assert.strictEqual(kanban.$('.o_kanban_record').text().trim(), '');
assert.containsN(kanban, '.o_m2o_avatar', 4);
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(0) > img').data('src'), '/web/image/hr.employee.public/11/image_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(1) > img').data('src'), '/web/image/hr.employee.public/7/image_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(2) > img').data('src'), '/web/image/hr.employee.public/11/image_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(3) > img').data('src'), '/web/image/hr.employee.public/23/image_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(0) > img').data('src'), '/web/image/hr.employee.public/11/avatar_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(1) > img').data('src'), '/web/image/hr.employee.public/7/avatar_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(2) > img').data('src'), '/web/image/hr.employee.public/11/avatar_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(3) > img').data('src'), '/web/image/hr.employee.public/23/avatar_128');
kanban.destroy();
});
@@ -235,7 +235,7 @@ QUnit.module('hr', {}, function () {
assert.containsN(form, '.o_field_many2manytags.avatar.o_field_widget .badge', 2,
"should have 2 records");
assert.strictEqual(form.$('.o_field_many2manytags.avatar.o_field_widget .badge:first img').data('src'),
'/web/image/hr.employee.public/11/image_128',
'/web/image/hr.employee.public/11/avatar_128',
"should have correct avatar image");
await dom.click(form.$('.o_field_many2manytags.avatar .badge:first .o_m2m_avatar'));
@@ -350,7 +350,7 @@ QUnit.module('hr', {}, function () {
assert.containsN(form, '.o_field_many2manytags.avatar.o_field_widget .badge', 2,
"should have 2 records");
assert.strictEqual(form.$('.o_field_many2manytags.avatar.o_field_widget .badge:first img').data('src'),
'/web/image/hr.employee.public/11/image_128',
'/web/image/hr.employee.public/11/avatar_128',
"should have correct avatar image");
await dom.click(form.$('.o_field_many2manytags.avatar .badge:first .o_m2m_avatar'));
+2 -2
View File
@@ -40,7 +40,7 @@
<!-- Used by other modules-->
</div>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"zoom": true, "preview_image":"image_128"}'/>
<field name="avatar_1920" widget='image' class="oe_avatar" options='{"zoom": true, "preview_image":"avatar_128"}'/>
<div class="oe_title">
<label for="name" string="Employee Name"/>
<h1>
@@ -122,7 +122,7 @@
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click o_kanban_record_has_image_fill o_hr_kanban_record">
<field name="image_128" widget="image" class="o_kanban_image_fill_left" options="{'zoom': true, 'zoom_delay': 1000, 'background': true, 'preventClicks': false}"/>
<field name="avatar_128" widget="image" class="o_kanban_image_fill_left" options="{'zoom': true, 'zoom_delay': 1000, 'background': true, 'preventClicks': false}"/>
<div class="oe_kanban_details">
<div class="o_kanban_record_top">
+4 -3
View File
@@ -73,7 +73,8 @@
</button>
</div>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"zoom": true, "preview_image":"image_128"}'/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"zoom": true, "preview_image":"avatar_128"}'/>
<div class="oe_title">
<h1 class="d-flex">
<field name="name" placeholder="Employee's Name" required="True"/>
@@ -244,7 +245,7 @@
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click o_kanban_record_has_image_fill o_hr_kanban_record">
<field name="image_128" widget="image" class="o_kanban_image_fill_left" options="{'zoom': true, 'zoom_delay': 1000, 'background': true, 'preventClicks': false}"/>
<field name="avatar_128" widget="image" class="o_kanban_image_fill_left" options="{'zoom': true, 'zoom_delay': 1000, 'background': true, 'preventClicks': false}"/>
<div class="oe_kanban_details">
<div class="o_kanban_record_top">
@@ -321,7 +322,7 @@
<field name="id"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('hr.employee', 'image_128', record.id.raw_value)" role="img" t-att-title="record.id.value" t-att-alt="record.id.value"/>
<img t-att-src="activity_image('hr.employee', 'avatar_128', record.id.raw_value)" role="img" t-att-title="record.id.value" t-att-alt="record.id.value"/>
<div>
<field name="name" display="full"/>
<field name="job_id" muted="1" display="full"/>
+2 -1
View File
@@ -75,7 +75,8 @@
</div>
</button>
</div>
<field name="image_1920" widget='image' class="oe_avatar" options='{"zoom": true, "preview_image":"image_128"}'/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"zoom": true, "preview_image":"avatar_128"}'/>
<div class="oe_title">
<h1>
<field name="name" placeholder="Employee's Name" required="True" readonly="context.get('from_my_profile', False)"/>
@@ -38,7 +38,7 @@
<t t-set="checked_in" t-value="widget.employee.attendance_state=='checked_in'"/>
<t t-if="widget.employee">
<div class="o_hr_attendance_user_badge o_home_menu_background">
<img class="img rounded-circle" t-attf-src="/web/image?model=hr.employee&amp;field=image_128&amp;id=#{widget.employee.id}" t-att-title="widget.employee.name" t-att-alt="widget.employee.name"/>
<img class="img rounded-circle" t-attf-src="/web/image?model=hr.employee&amp;field=avatar_128&amp;id=#{widget.employee.id}" t-att-title="widget.employee.name" t-att-alt="widget.employee.name"/>
</div>
<h1 class="mb8"><t t-esc="widget.employee.name"/></h1>
<h3 class="mt8 mb24"><t t-if="!checked_in">Welcome!</t><t t-else="">Want to check out?</t></h3>
@@ -66,7 +66,7 @@
</div>
<t t-if="widget.employee_id">
<div class="o_hr_attendance_user_badge o_home_menu_background">
<img class="img rounded-circle" t-attf-src="/web/image?model=hr.employee&amp;field=image_128&amp;id=#{widget.employee_id}" t-att-title="widget.employee_name" t-att-alt="widget.employee_name"/>
<img class="img rounded-circle" t-attf-src="/web/image?model=hr.employee&amp;field=avatar_128&amp;id=#{widget.employee_id}" t-att-title="widget.employee_name" t-att-alt="widget.employee_name"/>
</div>
<h1 class="mb8"><t t-esc="widget.employee_name"/></h1>
<h3 class="mt8 mb24"><t t-if="!checked_in">Welcome!</t><t t-else="">Want to check out?</t></h3>
@@ -109,7 +109,7 @@
<div class="o_hr_attendance_kiosk_mode">
<t t-if="widget.attendance">
<div class="o_hr_attendance_user_badge o_home_menu_background">
<img class="img rounded-circle" t-attf-src="/web/image?model=hr.employee&amp;field=image_128&amp;id=#{widget.attendance.employee_id[0]}" t-att-title="widget.employee_name" t-att-alt="widget.employee_name"/>
<img class="img rounded-circle" t-attf-src="/web/image?model=hr.employee&amp;field=avatar_128&amp;id=#{widget.attendance.employee_id[0]}" t-att-title="widget.employee_name" t-att-alt="widget.employee_name"/>
</div>
<t t-if="widget.attendance.check_out">
<h1 class="mb0">Goodbye <t t-esc="widget.employee_name"/>!</h1>
@@ -41,7 +41,7 @@
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click">
<div>
<img t-att-src="kanban_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="oe_kanban_avatar o_image_24_cover mr4"/>
<img t-att-src="kanban_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="oe_kanban_avatar o_image_24_cover mr4"/>
<span class="o_kanban_record_title">
<strong><t t-esc="record.employee_id.value"/></strong>
</span>
@@ -125,7 +125,7 @@
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<div class="o_kanban_image">
<img t-att-src="kanban_image('hr.employee', 'image_128', record.id.raw_value)" alt="Employee"/>
<img t-att-src="kanban_image('hr.employee', 'avatar_128', record.id.raw_value)" alt="Employee"/>
</div>
<div class="oe_kanban_details">
<div id="textbox">
@@ -276,7 +276,7 @@
<field name="employee_id"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value"/>
<img t-att-src="activity_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value"/>
<div>
<field name="name" display="full"/>
<field name="job_id" muted="1" display="full"/>
+2 -2
View File
@@ -355,7 +355,7 @@
<field name="currency_id"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value"/>
<img t-att-src="activity_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value"/>
<div>
<field name="name" display="full"/>
<field name="total_amount" widget="monetary" muted="1" display="full"/>
@@ -920,7 +920,7 @@
<field name="currency_id"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value"/>
<img t-att-src="activity_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value"/>
<div>
<field name="name" display="full"/>
<field name="total_amount" widget="monetary" muted="1" display="full"/>
@@ -65,7 +65,7 @@
<div t-attf-class="oe_kanban_global_click">
<div>
<span>
<img t-att-src="kanban_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="oe_kanban_avatar o_image_40_cover float-left mr4"/>
<img t-att-src="kanban_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="oe_kanban_avatar o_image_40_cover float-left mr4"/>
</span>
<span>
<div>
@@ -297,7 +297,7 @@
<div class="oe_kanban_global_click container">
<div class="row no-gutters">
<div class="col-3">
<img t-att-src="kanban_image('hr.employee', 'image_128', record.employee_id.raw_value)"
<img t-att-src="kanban_image('hr.employee', 'avatar_128', record.employee_id.raw_value)"
t-att-title="record.employee_id.value"
t-att-alt="record.employee_id.value"
class="o_image_64_cover float-left mr4"/>
@@ -351,7 +351,7 @@
<field name="employee_id"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value"/>
<img t-att-src="activity_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value"/>
<div>
<field name="employee_id"/>
<span class="ml-3 text-muted">
+2 -2
View File
@@ -109,7 +109,7 @@
<div class="oe_kanban_global_click container">
<div class="row no-gutters">
<div class="col-3">
<img t-att-src="kanban_image('hr.employee', 'image_128', record.employee_id.raw_value)"
<img t-att-src="kanban_image('hr.employee', 'avatar_128', record.employee_id.raw_value)"
t-att-title="record.employee_id.value"
t-att-alt="record.employee_id.value"
class="o_image_64_cover float-left mr4"/>
@@ -178,7 +178,7 @@
<field name="number_of_days"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" width="50" height="50"/>
<img t-att-src="activity_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" width="50" height="50"/>
<div>
<field name="name"/>
<span class="ml-3 text-muted">
@@ -10,13 +10,13 @@
use bg-images to get a clean and centred images -->
<a t-if="! is_self"
class="o_media_object rounded-circle o_employee_redirect"
t-att-style="'background-image:url(\'/web/image/hr.employee.public/' + employee.id + '/image_1024/\')'"
t-att-style="'background-image:url(\'/web/image/hr.employee.public/' + employee.id + '/avatar_1024/\')'"
t-att-alt="employee.name"
t-att-data-employee-id="employee.id"
t-att-href="employee.link"/>
<div t-if="is_self"
class="o_media_object rounded-circle"
t-att-style="'background-image:url(\'/web/image/hr.employee.public/' + employee.id + '/image_1024/\')'"/>
t-att-style="'background-image:url(\'/web/image/hr.employee.public/' + employee.id + '/avatar_1024/\')'"/>
</div>
<div class="media-body">
@@ -151,7 +151,7 @@
<t t-name="hr_orgchart_emp_popover_title">
<div>
<span t-att-style='"background-image:url(\"/web/image/hr.employee.public/" + employee.id + "/image_1024/\")"'/>
<span t-att-style='"background-image:url(\"/web/image/hr.employee.public/" + employee.id + "/avatar_1024/\")"'/>
<a href="#" class="float-right o_employee_redirect" t-att-data-employee-id="employee.id"><i class="fa fa-external-link" role="img" aria-label='Redirect' title="Redirect"></i></a>
<b><t t-esc="employee.name"/></b>
</div>
@@ -88,7 +88,7 @@
<table>
<tr>
<td width="75">
<img src="/web/image/res.users/${object.user_id.id}/image_128" alt="Avatar" style="vertical-align:baseline; width: 64px; height: 64px; object-fit: cover;" />
<img src="/web/image/res.users/${object.user_id.id}/avatar_128" alt="Avatar" style="vertical-align:baseline; width: 64px; height: 64px; object-fit: cover;" />
</td>
<td>
<strong>${object.user_id.name}</strong><br/>
@@ -189,7 +189,7 @@
<table>
<tr>
<td width="75">
<img src="/web/image/res.users/${object.user_id.id}/image_128" alt="Avatar" style="vertical-align:baseline; width: 64px; height: 64px; object-fit: cover;" />
<img src="/web/image/res.users/${object.user_id.id}/avatar_128" alt="Avatar" style="vertical-align:baseline; width: 64px; height: 64px; object-fit: cover;" />
</td>
<td>
<strong>${object.user_id.name}</strong><br/>
@@ -172,7 +172,7 @@
<div class="oe_kanban_global_click o_kanban_record_has_image_fill px-0 pb-0">
<div class="oe_kanban_details d-flex flex-column">
<div class="o_kanban_record_top px-2 h-50">
<img t-att-src="kanban_image('hr.employee', 'image_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="o_image_40_cover float-left"/>
<img t-att-src="kanban_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="o_image_40_cover float-left"/>
<div class="o_kanban_record_headings pl-4 pr-2">
<div class="text-truncate">
<strong class="o_kanban_record_title">
@@ -28,7 +28,7 @@
color="color"
event_limit="5">
<!-- Sidebar favorites filters -->
<field name="employee_id" write_model="hr.user.work.entry.employee" write_field="employee_id" avatar_field="image_128"/>
<field name="employee_id" write_model="hr.user.work.entry.employee" write_field="employee_id" avatar_field="avatar_128"/>
<field name="state"/>
</calendar>
</field>
@@ -837,7 +837,7 @@ var WebsiteLivechatMessage = AbstractMessage.extend({
getAvatarSource: function () {
var source = this._serverURL;
if (this.hasAuthor()) {
source += '/web/partner_image/' + this.getAuthorID();
source += '/web/image/res.partner/' + this.getAuthorID() + '/avatar_128';
} else {
source += '/mail/static/src/img/smiley/avatar.jpg';
}
@@ -1634,7 +1634,7 @@ var AbstractMessage = Class.extend({
*/
getAvatarSource: function () {
if (this.hasAuthor()) {
return '/web/image/res.partner/' + this.getAuthorID() + '/image_128';
return '/web/image/res.partner/' + this.getAuthorID() + '/avatar_128';
}
},
/**
@@ -105,7 +105,7 @@
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<div class="o_kanban_image">
<img t-att-src="kanban_image('res.users', 'image_1024', record.id.raw_value)" alt="User"/>
<img t-att-src="kanban_image('res.users', 'avatar_1024', record.id.raw_value)" alt="User"/>
</div>
<div class="o_kanban_details">
<h4 class="o_kanban_record_title"><field name="name"/></h4>
+1 -1
View File
@@ -91,7 +91,7 @@ class LunchController(http.Controller):
res.update({
'username': user.sudo().name,
'userimage': '/web/image?model=res.users&id=%s&field=image_128' % user.id,
'userimage': '/web/image?model=res.users&id=%s&field=avatar_128' % user.id,
'wallet': request.env['lunch.cashmove'].get_wallet_balance(user, False),
'is_manager': is_manager,
'locations': request.env['lunch.location'].search_read([], ['name']),
+1 -1
View File
@@ -235,7 +235,7 @@ class MailController(http.Controller):
request.env[res_model].browse(res_id).check_access_rule('read')
if partner_id in request.env[res_model].browse(res_id).sudo().exists().message_ids.mapped('author_id').ids:
status, headers, _content = request.env['ir.http'].sudo().binary_content(
model='res.partner', id=partner_id, field='image_128', default_mimetype='image/png')
model='res.partner', id=partner_id, field='avatar_128', default_mimetype='image/png')
# binary content return an empty string and not a placeholder if obj[field] is False
if _content != '':
content = _content
+1 -1
View File
@@ -123,7 +123,7 @@ class Message(models.Model):
author_id = fields.Many2one(
'res.partner', 'Author', index=True, ondelete='set null',
help="Author of the message. If not set, email_from may hold an email address that did not match any partner.")
author_avatar = fields.Binary("Author's avatar", related='author_id.image_128', depends=['author_id'], readonly=False)
author_avatar = fields.Binary("Author's avatar", related='author_id.avatar_128', depends=['author_id'], readonly=False)
# recipients: include inactive partners (they may have been archived after
# the message was sent, but they should remain visible in the relation)
partner_ids = fields.Many2many('res.partner', string='Recipients', context={'active_test': False})
@@ -7,7 +7,7 @@
<div class="o_Activity_sidebar">
<div class="o_Activity_user">
<t t-if="activity.assignee">
<img class="o_Activity_userAvatar" t-attf-src="/web/image/res.users/{{ activity.assignee.id }}/image_128" t-att-alt="activity.assignee.nameOrDisplayName"/>
<img class="o_Activity_userAvatar" t-attf-src="/web/image/res.users/{{ activity.assignee.id }}/avatar_128" t-att-alt="activity.assignee.nameOrDisplayName"/>
</t>
<div class="o_Activity_iconContainer"
t-att-class="{
@@ -65,7 +65,7 @@
<dt>Created</dt>
<dd class="o_Activity_detailsCreation">
<t t-esc="formattedCreateDatetime"/>
<img class="o_Activity_detailsUserAvatar o_Activity_detailsCreatorAvatar" t-attf-src="/web/image/res.users/{{ activity.creator.id }}/image_128" t-att-title="activity.creator.nameOrDisplayName" t-att-alt="activity.creator.nameOrDisplayName"/>
<img class="o_Activity_detailsUserAvatar o_Activity_detailsCreatorAvatar" t-attf-src="/web/image/res.users/{{ activity.creator.id }}/avatar_128" t-att-title="activity.creator.nameOrDisplayName" t-att-alt="activity.creator.nameOrDisplayName"/>
<span class="o_Activity_detailsCreator">
<t t-esc="activity.creator.nameOrDisplayName"/>
</span>
@@ -74,7 +74,7 @@
<t t-if="activity.assignee">
<dt>Assigned to</dt>
<dd class="o_Activity_detailsAssignation">
<img class="o_Activity_detailsUserAvatar o_Activity_detailsAssignationUserAvatar" t-attf-src="/web/image/res.users/{{ activity.assignee.id }}/image_128" t-att-title="activity.assignee.nameOrDisplayName" t-att-alt="activity.assignee.nameOrDisplayName"/>
<img class="o_Activity_detailsUserAvatar o_Activity_detailsAssignationUserAvatar" t-attf-src="/web/image/res.users/{{ activity.assignee.id }}/avatar_128" t-att-title="activity.assignee.nameOrDisplayName" t-att-alt="activity.assignee.nameOrDisplayName"/>
<t t-esc="activity.assignee.nameOrDisplayName"/>
</dd>
</t>
@@ -131,7 +131,7 @@ class Composer extends Component {
get currentPartnerAvatar() {
const avatar = this.env.messaging.currentUser
? this.env.session.url('/web/image', {
field: 'image_128',
field: 'avatar_128',
id: this.env.messaging.currentUser.id,
model: 'res.users',
})
@@ -1324,7 +1324,7 @@ QUnit.test('basic rendering of message', async function (assert) {
);
assert.strictEqual(
message.querySelector(`:scope .o_Message_authorAvatar`).dataset.src,
"/web/image/res.partner/11/image_128",
"/web/image/res.partner/11/avatar_128",
"should have url of message in author avatar sidebar"
);
assert.strictEqual(
@@ -87,7 +87,7 @@ QUnit.test('basic rendering', async function (assert) {
);
assert.strictEqual(
messageEl.querySelector(`:scope .o_Message_authorAvatar`).dataset.src,
'/web/image/res.partner/7/image_128',
'/web/image/res.partner/7/avatar_128',
"message author avatar should GET image of the related partner"
);
assert.strictEqual(
@@ -373,7 +373,7 @@ function factory(dependencies) {
if (this === this.env.messaging.partnerRoot) {
return '/mail/static/src/img/odoobot.png';
}
return `/web/image/res.partner/${this.id}/image_128`;
return `/web/image/res.partner/${this.id}/avatar_128`;
}
/**
+3 -3
View File
@@ -17,7 +17,7 @@
<div class="o_thread_message" style="margin-bottom: 10px">
<div class="o_thread_message_sidebar">
<div class="o_avatar_stack">
<img t-attf-src="/web/image#{activity.user_id[0] >= 0 ? ('/res.users/' + activity.user_id[0] + '/image_128') : ''}" class="o_thread_message_avatar rounded-circle mb8" t-att-title="activity.user_id[1]" t-att-alt="activity.user_id[1]"/>
<img t-attf-src="/web/image#{activity.user_id[0] >= 0 ? ('/res.users/' + activity.user_id[0] + '/avatar_128') : ''}" class="o_thread_message_avatar rounded-circle mb8" t-att-title="activity.user_id[1]" t-att-alt="activity.user_id[1]"/>
<i t-att-class="'o_avatar_icon fa ' + activity.icon + ' bg-' + (activity.state == 'planned'? 'success' : (activity.state == 'today'? 'warning' : 'danger')) + '-full'"
t-att-title="activity.activity_type_id[1]"/>
</div>
@@ -42,7 +42,7 @@
<dd class="mb8">
<t t-esc="activity.create_date.format(datetimeFormat)"/>
by
<img t-attf-src="/web/image#{activity.create_uid[0] >= 0 ? ('/res.users/' + activity.create_uid[0] + '/image_128') : ''}"
<img t-attf-src="/web/image#{activity.create_uid[0] >= 0 ? ('/res.users/' + activity.create_uid[0] + '/avatar_128') : ''}"
height="18" width="18"
class="o_object_fit_cover rounded-circle mr4"
t-att-title="activity.create_uid[1]"
@@ -51,7 +51,7 @@
</dd>
<dt>Assigned to</dt>
<dd class="mb8">
<img t-attf-src="/web/image#{activity.user_id[0] >= 0 ? ('/res.users/' + activity.user_id[0] + '/image_128') : ''}" height="18" width="18" class="o_object_fit_cover rounded-circle mr4" t-att-title="activity.user_id[1]" t-att-alt="activity.user_id[1]"/>
<img t-attf-src="/web/image#{activity.user_id[0] >= 0 ? ('/res.users/' + activity.user_id[0] + '/avatar_128') : ''}" height="18" width="18" class="o_object_fit_cover rounded-circle mr4" t-att-title="activity.user_id[1]" t-att-alt="activity.user_id[1]"/>
<b><t t-esc="activity.user_id[1]"/></b>
<em>, due on </em><span t-attf-class="o_activity_color_#{activity.state}"><t t-esc="activity.date_deadline.format(dateFormat)"/></span>
</dd>
@@ -212,7 +212,7 @@ class MockModels {
description: { string: 'description', type: 'text' },
display_name: { string: "Displayed name", type: "char" },
email: { type: 'char' },
image_128: { string: "Image 128", type: 'image' },
avatar_128: { string: "Image 128", type: 'image' },
im_status: { string: "IM Status", type: 'char' },
message_follower_ids: { relation: 'mail.followers', string: "Followers", type: "one2many" },
message_attachment_count: { string: 'Attachment count', type: 'integer' },
@@ -113,10 +113,10 @@ QUnit.module('mail', {}, function () {
assert.strictEqual(kanban.$('.o_kanban_record').text().trim(), '');
assert.containsN(kanban, '.o_m2o_avatar', 4);
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(0) > img').data('src'), '/web/image/res.users/11/image_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(1) > img').data('src'), '/web/image/res.users/7/image_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(2) > img').data('src'), '/web/image/res.users/11/image_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(3) > img').data('src'), '/web/image/res.users/23/image_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(0) > img').data('src'), '/web/image/res.users/11/avatar_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(1) > img').data('src'), '/web/image/res.users/7/avatar_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(2) > img').data('src'), '/web/image/res.users/11/avatar_128');
assert.strictEqual(kanban.$('.o_m2o_avatar:nth(3) > img').data('src'), '/web/image/res.users/23/avatar_128');
kanban.destroy();
});
@@ -141,7 +141,7 @@ QUnit.module('mail', {}, function () {
assert.containsN(form, '.o_field_many2manytags.avatar.o_field_widget .badge', 2,
"should have 2 records");
assert.strictEqual(form.$('.o_field_many2manytags.avatar.o_field_widget .badge:first img').data('src'), '/web/image/res.users/11/image_128',
assert.strictEqual(form.$('.o_field_many2manytags.avatar.o_field_widget .badge:first img').data('src'), '/web/image/res.users/11/avatar_128',
"should have correct avatar image");
await dom.click(form.$('.o_field_many2manytags.avatar .badge:first .o_m2m_avatar'));
+1 -1
View File
@@ -222,7 +222,7 @@
<field name="priority" eval="2"/>
<field name="arch" type="xml">
<calendar string="Activity" date_start="date_deadline" color="activity_type_id">
<field name="user_id" avatar_field="image_128"/>
<field name="user_id" avatar_field="avatar_128"/>
<field name="res_name"/>
<field name="date_deadline"/>
<field name="summary"/>
+1 -1
View File
@@ -79,7 +79,7 @@
<field name="id"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('res.partner', 'image_128', record.id.raw_value)" role="img" t-att-title="record.id.value" t-att-alt="record.id.value"/>
<img t-att-src="activity_image('res.partner', 'avatar_128', record.id.raw_value)" role="img" t-att-title="record.id.value" t-att-alt="record.id.value"/>
<div>
<field name="name" display="full"/>
<field name="parent_id" muted="1" display="full"/>
+1 -1
View File
@@ -437,7 +437,7 @@
<field name="arch" type="xml">
<calendar date_start="date_planned_start" date_stop="date_planned_finished"
string="Manufacturing Orders" event_limit="5" quick_add="False">
<field name="user_id" avatar_field="image_128"/>
<field name="user_id" avatar_field="avatar_128"/>
<field name="product_id"/>
<field name="product_qty"/>
</calendar>
+1 -1
View File
@@ -398,7 +398,7 @@
<span t-if="record.working_state.raw_value != 'blocked' and record.working_user_ids.raw_value.length == 0 and record.last_working_user_id.raw_value"><i class="fa fa-pause" role="img" aria-label="Pause" title="Pause"/></span>
<span t-if="record.working_state.raw_value == 'blocked' and (record.working_user_ids.raw_value.length == 0 or record.last_working_user_id.raw_value)"><i class="fa fa-stop" role="img" aria-label="Stop" title="Stop"/></span>
<t t-if="record.last_working_user_id.raw_value">
<img t-att-src="kanban_image('res.users', 'image_128', record.last_working_user_id.raw_value)" class="oe_kanban_avatar" alt="Avatar"/>
<img t-att-src="kanban_image('res.users', 'avatar_128', record.last_working_user_id.raw_value)" class="oe_kanban_avatar" alt="Avatar"/>
</t>
</div>
</div>
+1 -1
View File
@@ -120,7 +120,7 @@
<t t-if="record.message_partner_ids.raw_value.length &gt; 1">
<div class="clearfix"></div>
<t t-foreach="record.message_partner_ids.raw_value" t-as="follower">
<img t-att-src="kanban_image('res.partner', 'image_128', follower)" class="oe_kanban_avatar o_image_24_cover float-right mt-2" t-att-data-member_id="follower" alt="Follower"/>
<img t-att-src="kanban_image('res.partner', 'avatar_128', follower)" class="oe_kanban_avatar o_image_24_cover float-right mt-2" t-att-data-member_id="follower" alt="Follower"/>
</t>
<div class="clearfix"></div>
</t>
@@ -25,7 +25,7 @@ odoo.define('point_of_sale.ClientDetailsEdit', function(require) {
if (this.changes.image_1920) {
return this.changes.image_1920;
} else if (partner.id) {
return `/web/image?model=res.partner&id=${partner.id}&field=image_128&write_date=${partner.write_date}&unique=1`;
return `/web/image?model=res.partner&id=${partner.id}&field=avatar_128&write_date=${partner.write_date}&unique=1`;
} else {
return false;
}
@@ -32,7 +32,7 @@
Oops! Something went wrong. Try to reload the page and log in.
</div>
<div class="media">
<img alt="Avatar" class="o_portal_chatter_avatar" t-attf-src="/web/image/res.partner/#{widget.options['partner_id']}/image_128/50x50"
<img alt="Avatar" class="o_portal_chatter_avatar" t-attf-src="/web/image/res.partner/#{widget.options['partner_id']}/avatar_128/50x50"
t-if="!widget.options['is_user_public'] or !widget.options['token']"/>
<div class="media-body">
<div class="o_portal_chatter_composer_input">
+1 -2
View File
@@ -71,8 +71,7 @@
<li t-if="is_connected" t-attf-class="#{_item_class} o_no_autohide_item">
<a href="#" role="button" data-toggle="dropdown" t-attf-class="dropdown-toggle #{_link_class}">
<t t-if="_avatar">
<t t-if="user_id.image_256" t-set="avatar_source" t-value="image_data_uri(user_id.image_256)"/>
<t t-else="" t-set="avatar_source" t-value="'/web/static/img/placeholder.png'"/>
<t t-set="avatar_source" t-value="image_data_uri(user_id.avatar_256)"/>
<img t-att-src="avatar_source" t-attf-class="rounded-circle #{_avatar_class}" width="24" height="24" alt="" loading="eager"/>
</t>
<i t-if="_icon" t-attf-class="fa fa-1x fa-fw fa-user-circle-o #{_icon_class}"/>
@@ -178,7 +178,7 @@ PortalChatter.include({
return {
mes_index: messageIndex,
publisher_id: this.options.partner_id,
publisher_avatar: _.str.sprintf('/web/image/%s/%s/image_128/50x50', 'res.partner', this.options.partner_id),
publisher_avatar: _.str.sprintf('/web/image/res.partner/%s/avatar_128/50x50', this.options.partner_id),
publisher_name: _t("Write your comment"),
publisher_datetime: '',
publisher_comment: '',
@@ -203,7 +203,7 @@ PortalChatter.include({
if (rawRating.publisher_id && rawRating.publisher_id.length >= 2) {
ratingData.publisher_id = rawRating.publisher_id[0];
ratingData.publisher_name = rawRating.publisher_id[1];
ratingData.publisher_avatar = _.str.sprintf('/web/image/%s/%s/image_128/50x50', 'res.partner', ratingData.publisher_id);
ratingData.publisher_avatar = _.str.sprintf('/web/image/res.partner/%s/avatar_128/50x50', ratingData.publisher_id);
}
var commentData = _.extend(this._newPublisherCommentData(messageIndex), ratingData);
return commentData;
@@ -91,8 +91,7 @@
<h6>Customer</h6>
<div class="row">
<div class="col flex-grow-0 pr-3">
<img t-if="project.partner_id.image_1024" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(project.partner_id.image_1024)" alt="Contact"/>
<img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/img/user_menu_avatar.png" alt="Contact"/>
<img class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(project.partner_id.avatar_1024)" alt="Contact"/>
</div>
<div class="col pl-sm-0">
<address t-field="project.partner_id" t-options='{"widget": "contact", "fields": ["name", "email", "phone"]}'/>
@@ -103,8 +102,7 @@
<h6>Project Manager</h6>
<div class="row">
<div class="col flex-grow-0 pr-3">
<img t-if="project.user_id.image_1024" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(project.user_id.image_1024)" alt="Contact"/>
<img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/img/user_menu_avatar.png" alt="Contact"/>
<img class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(project.user_id.avatar_1024)" alt="Contact"/>
</div>
<div class="col pl-sm-0">
<address t-field="project.user_id" t-options='{"widget": "contact", "fields": ["name", "email", "phone"]}'/>
@@ -212,8 +210,7 @@
<strong>Assigned to</strong>
<div class="row">
<div class="col flex-grow-0 pr-3">
<img t-if="task.user_id.image_1024" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(task.user_id.image_1024)" alt="Contact"/>
<img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/img/user_menu_avatar.png" alt="Contact"/>
<img class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(task.user_id.avatar_1024)" alt="Contact"/>
</div>
<div class="col pl-md-0">
<div t-field="task.user_id" t-options='{"widget": "contact", "fields": ["name", "email", "phone"]}'/>
@@ -224,8 +221,7 @@
<strong>Reported by</strong>
<div class="row">
<div class="col flex-grow-0 pr-3">
<img t-if="task.partner_id.image_1024" class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(task.partner_id.image_1024)" alt="Contact"/>
<img t-else="" class="rounded-circle mt-1 o_portal_contact_img" src="/web/static/img/user_menu_avatar.png" alt="Contact"/>
<img class="rounded-circle mt-1 o_portal_contact_img" t-att-src="image_data_uri(task.partner_id.avatar_1024)" alt="Contact"/>
</div>
<div class="col pl-md-0">
<div t-field="task.partner_id" t-options='{"widget": "contact", "fields": ["name", "email", "phone"]}'/>
+2 -2
View File
@@ -1048,7 +1048,7 @@
<field name="arch" type="xml">
<calendar date_start="date_deadline" string="Tasks" mode="month" color="user_id" event_limit="5"
hide_time="true" quick_add="False" js_class="project_calendar">
<field name="user_id" avatar_field="image_128" filters="1"/>
<field name="user_id" avatar_field="avatar_128" filters="1"/>
<field name="date_deadline"/>
<field name="project_id"/>
<field name="priority" widget="priority"/>
@@ -1088,7 +1088,7 @@
<field name="user_id"/>
<templates>
<div t-name="activity-box">
<img t-att-src="activity_image('res.users', 'image_128', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value"/>
<img t-att-src="activity_image('res.users', 'avatar_128', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value"/>
<div>
<field name="name" display="full"/>
<field name="project_id" muted="1" display="full" invisible="context.get('default_project_id', False)"/>
+1 -2
View File
@@ -156,8 +156,7 @@
<div class="small mb-1"><strong class="text-muted">Purchase Representative</strong></div>
<div class="row flex-nowrap">
<div class="col flex-grow-0 pr-2">
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="order.user_id.image_1024" t-att-src="image_data_uri(order.user_id.image_1024)" alt="Contact"/>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="not order.user_id.image_1024" src="/web/static/img/placeholder.png" alt="Contact"/>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-att-src="image_data_uri(order.user_id.avatar_1024)" alt="Contact"/>
</div>
<div class="col pl-0" style="min-width: 150px">
<span t-field="order.user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/>
+1 -2
View File
@@ -180,8 +180,7 @@
<div class="small mb-1"><strong class="text-muted">Salesperson</strong></div>
<div class="row flex-nowrap">
<div class="col flex-grow-0 pr-2">
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="sale_order.user_id.image_1024" t-att-src="image_data_uri(sale_order.user_id.image_1024)" alt="Contact"/>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-if="not sale_order.user_id.image_1024" src="/web/static/img/placeholder.png" alt="Contact"/>
<img class="rounded-circle mr4 float-left o_portal_contact_img" t-att-src="image_data_uri(sale_order.user_id.avatar_1024)" alt="Contact"/>
</div>
<div class="col pl-0" style="min-width: 150px">
<span t-field="sale_order.user_id" t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True}'/>
+1 -1
View File
@@ -123,7 +123,7 @@
<field name="arch" type="xml">
<calendar string="Sales Orders" date_start="date_order" color="state" hide_time="true" event_limit="5">
<field name="currency_id" invisible="1"/>
<field name="partner_id" avatar_field="image_128"/>
<field name="partner_id" avatar_field="avatar_128"/>
<field name="amount_total" widget="monetary"/>
<field name="payment_term_id"/>
<field name="state" filters="1" invisible="1"/>
@@ -297,7 +297,7 @@
<t t-set="employee" t-value="repartition_employee[employee_id]"/>
<tr name="row_time_by_people">
<td style="width: 3%; vertical-align: middle;">
<img class="img rounded-circle mr-2 mb-2" t-attf-src="/web/image?model=hr.employee&amp;field=image_128&amp;id=#{employee['employee_id']}" t-att-title="employee['employee_name']" t-att-alt="employee['employee_name']" width="25" height="25"/>
<img class="img rounded-circle mr-2 mb-2" t-attf-src="/web/image?model=hr.employee&amp;field=avatar_128&amp;id=#{employee['employee_id']}" t-att-title="employee['employee_name']" t-att-alt="employee['employee_name']" width="25" height="25"/>
</td>
<td style="width: 15%; vertical-align: middle;" >
<a type="action" data-model="account.analytic.line" t-att-data-domain="json.dumps(timesheet_domain)" t-att-data-context="json.dumps({'search_default_employee_id': employee_id})" data-views="[[0, &quot;list&quot;]]" tabindex="-1">
@@ -60,7 +60,7 @@
</div>
<div class="o_kanban_card_content d-flex">
<div>
<img t-att-src="kanban_image('res.users', 'image_128', record.user_id.raw_value)" class="o_kanban_image o_image_64_cover" alt="Avatar"/>
<img t-att-src="kanban_image('res.users', 'avatar_128', record.user_id.raw_value)" class="o_kanban_image o_image_64_cover" alt="Avatar"/>
</div>
<div class="oe_kanban_details d-flex flex-column ml-3">
<strong class="o_kanban_record_title oe_partner_heading"><field name="user_id"/></strong>
+2 -2
View File
@@ -57,13 +57,13 @@
<field name="id"/>
<field name="name"/>
<field name="email"/>
<field name="image_128"/>
<field name="avatar_128"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_card oe_kanban_global_click">
<div class="o_kanban_card_content d-flex">
<div>
<img t-att-src="kanban_image('res.users', 'image_128', record.id.raw_value)"
<img t-att-src="kanban_image('res.users', 'avatar_128', record.id.raw_value)"
class="o_kanban_image o_image_64_cover" alt="Avatar"/>
</div>
<div class="oe_kanban_details d-flex flex-column ml-3">
+4 -21
View File
@@ -1420,15 +1420,6 @@ class Binary(http.Controller):
return self._get_content_common(xmlid=None, model='ir.attachment', id=id, field='datas', unique=unique, filename=filename,
filename_field='name', download=None, mimetype=None, access_token=None, token=None)
@http.route(['/web/partner_image',
'/web/partner_image/<int:rec_id>',
'/web/partner_image/<int:rec_id>/<string:field>',
'/web/partner_image/<int:rec_id>/<string:field>/<string:model>/'], type='http', auth="public")
def content_image_partner(self, rec_id, field='image_128', model='res.partner', **kwargs):
# other kwargs are ignored on purpose
return self._content_image(id=rec_id, model='res.partner', field=field,
placeholder='user_placeholder.jpg')
@http.route(['/web/image',
'/web/image/<string:xmlid>',
'/web/image/<string:xmlid>/<string:filename>',
@@ -1459,7 +1450,7 @@ class Binary(http.Controller):
def _content_image(self, xmlid=None, model='ir.attachment', id=None, field='datas',
filename_field='name', unique=None, filename=None, mimetype=None,
download=None, width=0, height=0, crop=False, quality=0, access_token=None,
placeholder=None, **kwargs):
**kwargs):
status, headers, image_base64 = request.env['ir.http'].binary_content(
xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename,
filename_field=filename_field, download=download, mimetype=mimetype,
@@ -1467,25 +1458,17 @@ class Binary(http.Controller):
return Binary._content_image_get_response(
status, headers, image_base64, model=model, id=id, field=field, download=download,
width=width, height=height, crop=crop, quality=quality,
placeholder=placeholder)
width=width, height=height, crop=crop, quality=quality)
@staticmethod
def _content_image_get_response(
status, headers, image_base64, model='ir.attachment', id=None,
field='datas', download=None, width=0, height=0, crop=False,
quality=0, placeholder='placeholder.png'):
quality=0):
if status in [301, 304] or (status != 200 and download):
return request.env['ir.http']._response_by_status(status, headers, image_base64)
if not image_base64:
if placeholder is None and model in request.env:
# Try to browse the record in case a specific placeholder
# is supposed to be used. (eg: Unassigned users on a task)
record = request.env[model].browse(int(id)) if id else request.env[model]
placeholder_filename = record._get_placeholder_filename(field=field)
placeholder_content = Binary.placeholder(image=placeholder_filename)
else:
placeholder_content = Binary.placeholder()
placeholder_content = Binary.placeholder()
# Since we set a placeholder for any missing image, the status must be 200. In case one
# wants to configure a specific 404 page (e.g. though nginx), a 404 status will cause
# troubles.
+1 -1
View File
@@ -46,7 +46,7 @@ var UserMenu = Widget.extend({
self.$('.oe_topbar_name').text(topbar_name);
var avatar_src = session.url('/web/image', {
model:'res.users',
field: 'image_128',
field: 'avatar_128',
id: session.uid,
});
$avatar.attr('src', avatar_src);
@@ -2071,8 +2071,7 @@ var FieldBinaryImage = AbstractFieldBinary.extend({
this.$el.addClass(this.attrs.class);
}
const image_field = this.field.manual ? this.name:'image_128';
var urlThumb = this._getImageUrl(this.model, this.res_id, image_field, unique);
var urlThumb = this._getImageUrl(this.model, this.res_id, this.name, unique);
this.$el.empty();
$img = this.$el;
@@ -1049,7 +1049,7 @@ const Many2OneAvatar = FieldMany2One.extend({
*/
_render() {
const m2oAvatar = qweb.render(this._template, {
url: `/web/image/${this.field.relation}/${this.value.res_id}/image_128`,
url: `/web/image/${this.field.relation}/${this.value.res_id}/avatar_128`,
value: this.m2o_value,
widget: this,
});
@@ -2769,7 +2769,7 @@ var FieldMany2ManyTagsAvatar = FieldMany2ManyTags.extend({
_getRenderTagsContext: function () {
var result = this._super.apply(this, arguments);
result.avatarModel = this.nodeOptions.avatarModel || this.field.relation;
result.avatarField = this.nodeOptions.avatarField || 'image_128';
result.avatarField = this.nodeOptions.avatarField || 'avatar_128';
return result;
},
});
@@ -128,7 +128,7 @@ var CalendarView = AbstractView.extend({
}
if (!child.attrs.avatar_field && fields[fieldName].relation) {
if (fields[fieldName].relation.includes(['res.users', 'res.partner', 'hr.employee'])) {
filters[fieldName].avatar_field = 'image_128';
filters[fieldName].avatar_field = 'avatar_128';
}
filters[fieldName].avatar_model = fields[fieldName].relation;
}
@@ -3637,7 +3637,7 @@ QUnit.module('fields', {}, function () {
assert.hasClass(form.$('.o_form_view'), 'o_form_readonly');
assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Aline');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/17/image_128"]');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/17/avatar_128"]');
await testUtils.form.clickEdit(form);
@@ -3645,16 +3645,16 @@ QUnit.module('fields', {}, function () {
assert.containsOnce(form, '.o_input_dropdown');
assert.strictEqual(form.$('.o_input_dropdown input').val(), 'Aline');
assert.containsOnce(form, '.o_external_button');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/17/image_128"]');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/17/avatar_128"]');
await testUtils.fields.many2one.clickOpenDropdown("user_id");
await testUtils.fields.many2one.clickItem("user_id", "Christine");
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/19/image_128"]');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/19/avatar_128"]');
await testUtils.form.clickSave(form);
assert.hasClass(form.$('.o_form_view'), 'o_form_readonly');
assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Christine');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/19/image_128"]');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/19/avatar_128"]');
await testUtils.form.clickEdit(form);
await testUtils.fields.editAndTrigger(form.$('.o_field_many2one[name="user_id"] input'),
@@ -3697,12 +3697,12 @@ QUnit.module('fields', {}, function () {
assert.hasClass(form.$('.o_form_view'), 'o_form_editable');
assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Aline');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/17/image_128"]');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/17/avatar_128"]');
await testUtils.fields.editInput(form.$('.o_field_widget[name=int_field]'), 1);
assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Christine');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/19/image_128"]');
assert.containsOnce(form, '.o_m2o_avatar > img[data-src="/web/image/user/19/avatar_128"]');
await testUtils.fields.editInput(form.$('.o_field_widget[name=int_field]'), 2);
@@ -3729,9 +3729,9 @@ QUnit.module('fields', {}, function () {
});
assert.strictEqual(list.$('.o_data_cell span').text(), 'AlineChristineAline');
assert.containsOnce(list.$('.o_data_cell:nth(0)'), '.o_m2o_avatar >img[data-src="/web/image/user/17/image_128"]');
assert.containsOnce(list.$('.o_data_cell:nth(1)'), '.o_m2o_avatar > img[data-src="/web/image/user/19/image_128"]');
assert.containsOnce(list.$('.o_data_cell:nth(2)'), '.o_m2o_avatar > img[data-src="/web/image/user/17/image_128"]');
assert.containsOnce(list.$('.o_data_cell:nth(0)'), '.o_m2o_avatar >img[data-src="/web/image/user/17/avatar_128"]');
assert.containsOnce(list.$('.o_data_cell:nth(1)'), '.o_m2o_avatar > img[data-src="/web/image/user/19/avatar_128"]');
assert.containsOnce(list.$('.o_data_cell:nth(2)'), '.o_m2o_avatar > img[data-src="/web/image/user/17/avatar_128"]');
assert.containsNone(list.$('.o_data_cell:nth(3)'), '.o_m2o_avatar > img');
list.destroy();
@@ -3754,10 +3754,10 @@ QUnit.module('fields', {}, function () {
});
assert.strictEqual(list.$('.o_data_cell span').text(), 'AlineChristineAline');
assert.containsOnce(list.$('.o_data_cell:nth(0)'), '.o_m2o_avatar > img[data-src="/web/image/user/17/image_128"]');
assert.containsOnce(list.$('.o_data_cell:nth(0)'), '.o_m2o_avatar > img[data-src="/web/image/user/17/avatar_128"]');
await testUtils.dom.click(list.$('.o_data_row:first() .o_data_cell:first()'));
assert.containsOnce(list.$('.o_data_cell:nth(0)'), '.o_m2o_avatar > img[data-src="/web/image/user/17/image_128"]');
assert.containsOnce(list.$('.o_data_cell:nth(0)'), '.o_m2o_avatar > img[data-src="/web/image/user/17/avatar_128"]');
list.destroy();
});
@@ -2010,7 +2010,7 @@ QUnit.module('relational_fields', {
});
assert.containsN(form, '.o_field_many2manytags.avatar.o_field_widget .badge', 2, "should have 2 records");
assert.strictEqual(form.$('.o_field_many2manytags.avatar.o_field_widget .badge:first img').data('src'), '/web/image/partner/2/image_128',
assert.strictEqual(form.$('.o_field_many2manytags.avatar.o_field_widget .badge:first img').data('src'), '/web/image/partner/2/avatar_128',
"should have correct avatar image");
form.destroy();
+1 -1
View File
@@ -16,7 +16,7 @@
<a href="/web/content/local_link">x</a>
<span t-attf-style="background-image: url('/web/image/2')" t-att-empty="False">xxx</span>
<div widget="html" t-field="user.signature"/>
<div widget="image" t-field="user.image_1920" t-options="{'widget': 'image'}"/>
<div widget="image" t-field="user.avatar_1920" t-options="{'widget': 'image'}"/>
</body>
</html>
</template>
+1 -1
View File
@@ -73,7 +73,7 @@ class TestQweb(TransactionCaseWithUserDemo):
<div widget="html"><span class="toto">
span<span class="fa"></span><img src="http://test.cdn/web/image/1" loading="lazy">
</span></div>
<div widget="image"><img src="http://test.cdn/web/image/res.users/%(user_id)s/image_1920/%(filename)s" class="img img-fluid" alt="%(alt)s" loading="lazy"/></div>
<div widget="image"><img src="http://test.cdn/web/image/res.users/%(user_id)s/avatar_1920/%(filename)s" class="img img-fluid" alt="%(alt)s" loading="lazy"/></div>
</body>
</html>""" % format_data).encode('utf8'))
@@ -97,7 +97,7 @@
<div class="o_kanban_detail_ungrouped row mx-0">
<div class="o_wvisitor_kanban_image">
<img t-if="record.partner_image.raw_value"
t-att-src="kanban_image('res.partner', 'image_128', record.partner_id.raw_value)"
t-att-src="kanban_image('res.partner', 'avatar_128', record.partner_id.raw_value)"
width="54px" height="54px" alt="Visitor"/>
<img t-else=""
t-attf-src="/base/static/img/avatar_grey.png"
+1 -1
View File
@@ -30,7 +30,7 @@ options.registry.many2one.include({
var $img = $(this).find('img');
var css = window.getComputedStyle($img[0]);
$img.css({width: css.width, height: css.height});
$img.attr('src', '/web/image/res.partner/' + self.ID + '/image_1024');
$img.attr('src', '/web/image/res.partner/' + self.ID + '/avatar_1024');
});
setTimeout(function () {
$nodes.removeClass('o_dirty');
@@ -144,7 +144,7 @@
t-att-title="record.author_id.value"
t-att-alt="record.author_id.value"
class="oe_kanban_avatar o_image_24_cover"
t-att-src="kanban_image('res.partner', 'image_128', record.author_id.raw_value)"/>
t-att-src="kanban_image('res.partner', 'avatar_128', record.author_id.raw_value)"/>
</div>
</div>
</div>
@@ -83,7 +83,7 @@
</t>
<div class="media mt-3">
<a t-attf-href="/partners/#{slug(partner)}?#{current_grade and 'grade_id=%s&amp;' % current_grade.id}#{current_country and 'country_id=%s' % current_country.id}"
t-field="partner.image_128"
t-field="partner.avatar_128"
class="mr-3 text-center o_width_128"
t-options='{"widget": "image", "qweb_img_responsive": False, "class": "o_image_128_max"}'
></a>
@@ -166,7 +166,7 @@
<t t-if="any(p.website_published for p in partner.implemented_partner_ids)">
<h3 id="references">References</h3>
<div t-foreach="partner.implemented_partner_ids" t-if="reference.website_published" t-as="reference" class="media mt-3">
<span t-field="reference.image_128" class="d-block mr-3 text-center o_width_128" t-options='{"widget": "image", "qweb_img_responsive": False, "class": "o_image_128_max"}'/>
<span t-field="reference.avatar_128" class="d-block mr-3 text-center o_width_128" t-options='{"widget": "image", "qweb_img_responsive": False, "class": "o_image_128_max"}'/>
<div class="media-body" style="min-height: 64px;">
<span t-field="reference.self"/>
<div t-field='reference.website_short_description'/>
@@ -36,7 +36,7 @@
<t t-foreach="partners" t-as="partner">
<div class="media mt-3">
<a t-attf-href="/customers/#{slug(partner)}"
t-field="partner.image_128"
t-field="partner.avatar_128"
class="d-block mr-3 text-center o_width_128"
t-options='{"widget": "image", "qweb_img_responsive": False, "class": "o_image_128_max"}'
></a>
@@ -204,7 +204,7 @@
</a>
</h4>
<div><a t-attf-href="/partners/#{slug(partner.assigned_partner_id)}"
t-field="partner.assigned_partner_id.image_128"
t-field="partner.assigned_partner_id.avatar_128"
class="d-block"
t-options='{"widget": "image", "qweb_img_responsive": False, "class": "o_image_128_max"}'
/>
@@ -226,7 +226,7 @@
<div t-foreach="partner.implemented_partner_ids" t-as="reference" class="media mt-3">
<t t-if="reference.website_published">
<a t-attf-href="/customers/#{slug(reference)}">
<span t-field="reference.image_128" class="d-block mr-3 text-center o_width_128" t-options='{"widget": "image", "qweb_img_responsive": False, "class": "o_image_128_max"}'/>
<span t-field="reference.avatar_128" class="d-block mr-3 text-center o_width_128" t-options='{"widget": "image", "qweb_img_responsive": False, "class": "o_image_128_max"}'/>
</a>
<div class="media-body" style="min-height: 64px;">
<a t-attf-href="/customers/#{slug(reference)}">
@@ -54,7 +54,7 @@
</div>
<div class="oe_kanban_bottom_right">
<field name="kanban_state" widget="state_selection" groups="base.group_user"/>
<img t-att-src="kanban_image('res.partner', 'image_128', record.partner_id.raw_value)"
<img t-att-src="kanban_image('res.partner', 'avatar_128', record.partner_id.raw_value)"
t-att-title="record.partner_id.value" t-att-alt="record.partner_id.value"
class="oe_kanban_avatar"/>
</div>
@@ -75,8 +75,8 @@
<calendar date_start="date" date_delay="duration" string="Event Tracks" color="location_id" event_limit="5">
<field name="location_id" filters="1"/>
<field name="event_id"/>
<field name="partner_id" avatar_field="image_128"/>
<field name="user_id" avatar_field="image_128"/>
<field name="partner_id" avatar_field="avatar_128"/>
<field name="user_id" avatar_field="avatar_128"/>
</calendar>
</field>
</record>
+7 -7
View File
@@ -538,7 +538,7 @@
data-toggle="tooltip"
data-trigger="hover"
title="My profile">
<img class="o_forum_avatar rounded-circle mr-1" t-att-src="website.image_url(user, 'image_128', '30x30')" alt="Avatar"/>
<img class="o_forum_avatar rounded-circle mr-1" t-att-src="website.image_url(user, 'avatar_128', '30x30')" alt="Avatar"/>
<div>
<h6 class="my-0" t-esc="user_id.name"/>
<small class="text-muted font-weight-bold"><t t-esc="user_id.karma"/>xp</small>
@@ -622,7 +622,7 @@
</span>
<span t-if="my" t-attf-class="w-100 w-md-auto mb-2 mb-md-0 border rounded pl-2 d-inline-flex align-items-center justify-content-between #{search and 'ml-md-2'}">
<div>
<img t-if="uid" class="o_forum_avatar rounded-circle mr-1" t-att-src="website.image_url(user, 'image_128', '16x16')" alt="Avatar"/>
<img t-if="uid" class="o_forum_avatar rounded-circle mr-1" t-att-src="website.image_url(user, 'avatar_128', '16x16')" alt="Avatar"/>
<span t-if="my == 'favourites'"> My <b>Favourites</b></span>
<span t-elif="my == 'followed'"> I'm <b>Following</b></span>
<span t-elif="my == 'mine'"> My <b>Posts</b></span>
@@ -973,7 +973,7 @@
<!-- Edition: post an answer -->
<template id="post_answer">
<div class="d-flex align-items-center mt-5 mb-2">
<img t-if="uid" t-attf-class="o_forum_avatar rounded-circle mr-2" t-att-src="website.image_url(user, 'image_128', '24x24')" alt="Avatar"/>
<img t-if="uid" t-attf-class="o_forum_avatar rounded-circle mr-2" t-att-src="website.image_url(user, 'avatar_128', '24x24')" alt="Avatar"/>
<h4 class="my-0">Your Answer</h4>
</div>
<t t-if="request.params.get('nocontent')">
@@ -1390,7 +1390,7 @@
<t t-set="display_info" t-value="show_name or show_date or show_karma"/>
<t t-if="allow_biography and object.can_display_biography" t-set="bio_popover_data">
<div class="d-flex o_wforum_bio_popover_wrap">
<img class="o_forum_avatar_big flex-shrink-0 mr-3" t-att-src="website.image_url(object.create_uid, 'image_128', '75x75')" alt="Avatar"/>
<img class="o_forum_avatar_big flex-shrink-0 mr-3" t-att-src="website.image_url(object.create_uid, 'avatar_128', '75x75')" alt="Avatar"/>
<div>
<h5 class="o_wforum_bio_popover_name mb-0" t-field="object.create_uid" t-options='{"widget": "contact", "country_image": True, "fields": ["name", "country_id"]}'/>
@@ -1411,7 +1411,7 @@
<span t-if="object_validable" class="o_wforum_author_box_check rounded-circle bg-success position-absolute">
<i class="fa fa-check fa-fw small text-white"/>
</span>
<img t-attf-class="rounded-circle o_forum_avatar #{not display_info and 'shadow'}" t-att-src="website.image_url(object.create_uid, 'image_128', '40x40')" alt="Avatar"/>
<img t-attf-class="rounded-circle o_forum_avatar #{not display_info and 'shadow'}" t-att-src="website.image_url(object.create_uid, 'avatar_128', '40x40')" alt="Avatar"/>
</a>
<div t-if="show_name or show_date or show_karma" t-attf-class="d-flex #{compact and 'align-items-baseline ml-1' or 'flex-column justify-content-around ml-2'}">
@@ -1444,7 +1444,7 @@
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input name="post_id" t-att-value="object.id" type="hidden" class="mt8"/>
<div class="d-flex w-100">
<img class="d-none d-md-inline-block rounded-circle o_forum_avatar mr-3" t-att-src="website.image_url(user, 'image_128', '40x40')" alt="Avatar"/>
<img class="d-none d-md-inline-block rounded-circle o_forum_avatar mr-3" t-att-src="website.image_url(user, 'avatar_128', '40x40')" alt="Avatar"/>
<div class="w-100">
<textarea name="comment" class="form-control mb-2" placeholder="Comment this post..."/>
<div>
@@ -1600,7 +1600,7 @@
<div class="custom-control custom-checkbox">
<input type="checkbox" t-att-value="user.id" class="custom-control-input" t-attf-id="user_#{user.id}"/>
<label class="custom-control-label" t-attf-for="user_#{user.id}">
<img class="d-inline img o_forum_avatar" t-att-src="website.image_url(user, 'image_128', '40x40')" alt="Avatar"/>
<img class="d-inline img o_forum_avatar" t-att-src="website.image_url(user, 'avatar_128', '40x40')" alt="Avatar"/>
<span t-esc="user.name" class="d-inline"/>
</label>
</div>
@@ -483,7 +483,7 @@
<h3 class="mb32 text-center">Your application has been sent to:</h3>
</div>
<div class="col-lg-1 offset-lg-4">
<p t-field="responsible.image_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded-circle d-block mx-auto o_image_64_cover'}"/>
<p t-field="responsible.avatar_128" t-options="{'widget': 'image', 'qweb_img_responsive': False, 'class': 'rounded-circle d-block mx-auto o_image_64_cover'}"/>
</div>
<div class="col-lg-5 o_responsible_data">
<h4 class="mt0" t-field="responsible.name"/>
@@ -101,7 +101,7 @@
<h3>The Team</h3>
<t t-foreach="team" t-as="user">
<div class="media mt-3">
<img t-if="user.image_128" t-att-src="image_data_uri(user.image_128)" class="o_image_64_cover rounded o_livechat_operator_avatar" t-att-alt="user.livechat_username or user.name"/>
<img t-att-src="image_data_uri(user.avatar_128)" class="o_image_64_cover rounded o_livechat_operator_avatar" t-att-alt="user.livechat_username or user.name"/>
<div class="media-body">
<h5>
<t t-if="user.livechat_username">
@@ -41,7 +41,7 @@
Speaking With
<div name="livechat_operator_id"
class="o_field_many2one_avatar o_field_widget font-weight-bold float-right d-inline-block">
<img t-attf-src="/web/image/res.partner/#{record.livechat_operator_id.raw_value}/image_128"
<img t-attf-src="/web/image/res.partner/#{record.livechat_operator_id.raw_value}/avatar_128"
alt="Operator Avatar" class="o_m2o_avatar"/>
<field name="livechat_operator_name"/>
</div>
@@ -60,7 +60,7 @@
<div class="col mx-2">
<div t-if="record.livechat_operator_id.raw_value"
class="o_field_many2one_avatar o_field_widget font-weight-bold" name="livechat_operator_id">
<img t-attf-src="/web/image/res.partner/#{record.livechat_operator_id.raw_value}/image_128"
<img t-attf-src="/web/image/res.partner/#{record.livechat_operator_id.raw_value}/avatar_128"
alt="Operator Avatar" class="o_m2o_avatar"/>
<field name="livechat_operator_name"/>
</div>
@@ -54,7 +54,7 @@
<t t-set="partner" t-value="partners[partner_id]"/>
<div class="media mt-3">
<a t-attf-href="/members/#{slug(partner)}"
t-field="partner.image_128"
t-field="partner.avatar_128"
t-options='{"widget": "image", "qweb_img_responsive": False, "class": "o_image_64_cover mr-3"}'
></a>
<div class="media-body" style="min-height: 64px;">
@@ -17,7 +17,7 @@
<template id="partner_detail" name="Partner Details">
<h1 class="col-lg-12 text-center" id="partner_name" t-field="partner.display_name"/>
<div class="col-lg-4">
<div t-field="partner.image_1920" t-options='{"widget": "image", "preview_image": "image_512", "class": "d-block mx-auto mb16"}'/>
<div t-field="partner.avatar_1920" t-options='{"widget": "image", "preview_image": "avatar_512", "class": "d-block mx-auto mb16"}'/>
<address>
<div t-field="partner.self" t-options='{
"widget": "contact",
+2 -2
View File
@@ -76,8 +76,8 @@ class WebsiteProfile(http.Controller):
@http.route([
'/profile/avatar/<int:user_id>',
], type='http', auth="public", website=True, sitemap=False)
def get_user_profile_avatar(self, user_id, field='image_256', width=0, height=0, crop=False, **post):
if field not in ('image_128', 'image_256'):
def get_user_profile_avatar(self, user_id, field='avatar_256', width=0, height=0, crop=False, **post):
if field not in ('image_128', 'image_256', 'avatar_128', 'avatar_256'):
return werkzeug.exceptions.Forbidden()
can_sudo = self._check_avatar_access(user_id, **post)
@@ -92,7 +92,7 @@
<div class="col-3">
<div class="card o_card_people">
<div class="card-body">
<img class="o_forum_avatar_img w-100 mb-3" t-att-src="website.image_url(user, 'image_128')"/>
<img class="o_forum_avatar_img w-100 mb-3" t-att-src="website.image_url(user, 'avatar_128')"/>
<div class="text-center">
<a href="#" class="o_forum_profile_pic_edit btn btn-primary" aria-label="Edit">
<i class="fa fa-pencil fa-1g float-sm-none float-md-left" title="Edit"></i>
@@ -194,7 +194,7 @@
<!-- ==== Header Left ==== -->
<div class="col-12 col-md-4 col-lg-3">
<div t-attf-class="d-flex align-items-start h-100 #{'justify-content-between' if (request.env.user == user) else 'justify-content-around' }">
<div class="o_wprofile_pict d-inline-block mb-3 mb-md-0" t-attf-style="background-image: url(#{website.image_url(user, 'image_1024')});"/>
<div class="o_wprofile_pict d-inline-block mb-3 mb-md-0" t-attf-style="background-image: url(#{website.image_url(user, 'avatar_1024')});"/>
<a class="btn btn-primary d-inline-block d-md-none"
t-if="request.env.user == user and user.karma != 0"
t-attf-href="/profile/edit?url_param=#{edit_button_url_param}&amp;user_id=#{user.id}">
@@ -554,7 +554,7 @@
<div class="d-inline-block position-relative">
<img class="rounded-circle img-fluid"
style="width: 128px; height: 128px; object-fit: cover;"
t-att-src="'/profile/avatar/%s?field=image_256%s' % (user['id'], '&amp;res_model=%s&amp;res_id=%s' % (record._name, record.id) if record else '')"/>
t-att-src="'/profile/avatar/%s?field=avatar_256%s' % (user['id'], '&amp;res_model=%s&amp;res_id=%s' % (record._name, record.id) if record else '')"/>
<img class="position-absolute" t-attf-src="/website_profile/static/src/img/rank_#{user_index + 1}.svg" alt="User rank" style="bottom: 0; right: -10px"/>
</div>
<h3 class="mt-2 mb-0" t-esc="user['name']"></h3>
@@ -576,7 +576,7 @@
<span t-esc="user['position']"/>
</td>
<td class="align-middle d-none d-sm-table-cell">
<img class="o_object_fit_cover rounded-circle o_wprofile_img_small" width="30" height="30" t-att-src="'/profile/avatar/%s?field=image_128%s' % (user['id'], '&amp;res_model=%s&amp;res_id=%s' % (record._name, record.id) if record else '')"/>
<img class="o_object_fit_cover rounded-circle o_wprofile_img_small" width="30" height="30" t-att-src="'/profile/avatar/%s?field=avatar_128%s' % (user['id'], '&amp;res_model=%s&amp;res_id=%s' % (record._name, record.id) if record else '')"/>
</td>
<td class="align-middle w-md-75">
<span class="font-weight-bold" t-esc="user['name']"/><br/>
+1 -2
View File
@@ -434,8 +434,7 @@ class WebsiteSlides(WebsiteProfile):
def _get_top3_users(self):
return request.env['res.users'].sudo().search_read([
('karma', '>', 0),
('website_published', '=', True),
('image_1920', '!=', False)], ['id'], limit=3, order='karma desc')
('website_published', '=', True)], ['id'], limit=3, order='karma desc')
@http.route([
'/slides/<model("slide.channel"):channel>',
@@ -502,7 +502,7 @@
<template id='slides_misc_user_image' name="User Avatar">
<img t-att-class="img_class if img_class else 'rounded-circle float-left'"
t-att-style="img_style if img_style else 'width: 32px; height: 32px; object-fit: cover;'"
t-att-src="'/profile/avatar/%s?field=image_128' % user.id"
t-att-src="'/profile/avatar/%s?field=avatar_128' % user.id"
t-att-alt="user.name"/>
</template>
</data></odoo>
@@ -23,7 +23,6 @@
<field name="name">Administrator</field>
<field name="company_id" ref="main_company"/>
<field name="email">admin@example.com</field>
<field name="image_1920" type="base64" file="base/static/img/avatar_grey.png"/>
</record>
<record id="public_partner" model="res.partner">
-1
View File
@@ -19,7 +19,6 @@ System</span>]]></field>
<field name="company_id" ref="main_company"/>
<field name="company_ids" eval="[Command.link(ref('main_company'))]"/>
<field name="groups_id" eval="[Command.set([])]"/>
<field name="image_1920" type="base64" file="base/static/img/avatar_grey.png"/>
<field name="signature"><![CDATA[<span>-- <br/>
Administrator</span>]]></field>
</record>
+1
View File
@@ -34,6 +34,7 @@ from . import report_layout
from . import report_paperformat
from . import image_mixin
from . import avatar_mixin
from . import res_country
from . import res_lang
+70
View File
@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from base64 import b64encode
from hashlib import sha512
from odoo import models, fields, api
from odoo.tools import html_escape, file_open
class AvatarMixin(models.AbstractModel):
_name = 'avatar.mixin'
_inherit = ['image.mixin']
_description = "Avatar Mixin"
_avatar_name_field = "name"
# all image fields are base64 encoded and PIL-supported
avatar_1920 = fields.Image("Avatar", max_width=1920, max_height=1920, compute="_compute_avatar_1920")
avatar_1024 = fields.Image("Avatar 1024", max_width=1024, max_height=1024, compute="_compute_avatar_1024")
avatar_512 = fields.Image("Avatar 512", max_width=512, max_height=512, compute="_compute_avatar_512")
avatar_256 = fields.Image("Avatar 256", max_width=256, max_height=256, compute="_compute_avatar_256")
avatar_128 = fields.Image("Avatar 128", max_width=128, max_height=128, compute="_compute_avatar_128")
def _compute_avatar(self, avatar_field, image_field):
for record in self:
avatar = record[image_field]
if not avatar:
if record[record._avatar_name_field]:
avatar = record._avatar_generate_svg()
else:
avatar = record._avatar_get_placeholder()
record[avatar_field] = avatar
@api.depends(lambda self: [self._avatar_name_field, 'image_1920'])
def _compute_avatar_1920(self):
self._compute_avatar('avatar_1920', 'image_1920')
@api.depends(lambda self: [self._avatar_name_field, 'image_1024'])
def _compute_avatar_1024(self):
self._compute_avatar('avatar_1024', 'image_1024')
@api.depends(lambda self: [self._avatar_name_field, 'image_512'])
def _compute_avatar_512(self):
self._compute_avatar('avatar_512', 'image_512')
@api.depends(lambda self: [self._avatar_name_field, 'image_256'])
def _compute_avatar_256(self):
self._compute_avatar('avatar_256', 'image_256')
@api.depends(lambda self: [self._avatar_name_field, 'image_128'])
def _compute_avatar_128(self):
self._compute_avatar('avatar_128', 'image_128')
def _avatar_generate_svg(self):
initial = html_escape(self[self._avatar_name_field][0].upper())
seed = self[self._avatar_name_field] + str(self.create_date.timestamp())
hashed_seed = sha512(seed.encode()).hexdigest()
hue = int(hashed_seed[0:2], 16) * 360 / 255
sat = int(hashed_seed[2:4], 16) * ((70 - 40) / 255) + 40
lig = 45
bgcolor = html_escape(f'hsl({hue}, {sat}%, {lig}%)')
return b64encode((
"<?xml version='1.0' encoding='UTF-8' ?>"
"<svg height='180' width='180' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>"
f"<rect fill='{bgcolor}' height='180' width='180'/>"
f"<text fill='#ffffff' font-size='96' text-anchor='middle' x='90' y='125' font-family='sans-serif'>{initial}</text>"
"</svg>"
).encode())
def _avatar_get_placeholder(self):
return b64encode(file_open("base/static/img/avatar_grey.png", 'rb').read())
+33 -6
View File
@@ -15,7 +15,6 @@ from random import randint
from werkzeug import urls
from odoo import api, fields, models, tools, SUPERUSER_ID, _, Command
from odoo.modules import get_module_resource
from odoo.osv.expression import get_unaccent_wrapper
from odoo.exceptions import RedirectWarning, UserError, ValidationError
@@ -131,7 +130,7 @@ class PartnerTitle(models.Model):
class Partner(models.Model):
_description = 'Contact'
_inherit = ['format.address.mixin', 'image.mixin']
_inherit = ['format.address.mixin', 'avatar.mixin']
_name = "res.partner"
_order = "display_name"
@@ -239,6 +238,38 @@ class Partner(models.Model):
('check_name', "CHECK( (type='contact' AND name IS NOT NULL) or (type!='contact') )", 'Contacts require a name'),
]
@api.depends('name', 'user_ids.share', 'image_1920', 'is_company')
def _compute_avatar_1920(self):
super()._compute_avatar_1920()
@api.depends('name', 'user_ids.share', 'image_1024', 'is_company')
def _compute_avatar_1024(self):
super()._compute_avatar_1024()
@api.depends('name', 'user_ids.share', 'image_512', 'is_company')
def _compute_avatar_512(self):
super()._compute_avatar_512()
@api.depends('name', 'user_ids.share', 'image_256', 'is_company')
def _compute_avatar_256(self):
super()._compute_avatar_256()
@api.depends('name', 'user_ids.share', 'image_128', 'is_company')
def _compute_avatar_128(self):
super()._compute_avatar_128()
def _compute_avatar(self, avatar_field, image_field):
partners_with_internal_user = self.filtered(lambda partner: partner.user_ids - partner.user_ids.filtered('share'))
super(Partner, partners_with_internal_user)._compute_avatar(avatar_field, image_field)
for partner in self - partners_with_internal_user:
partner[avatar_field] = partner[image_field] or partner._avatar_get_placeholder()
def _avatar_get_placeholder(self):
path = "base/static/img/avatar_grey.png"
if self.is_company:
path = "base/static/img/company_image.png"
return base64.b64encode(tools.file_open(path, 'rb').read())
@api.depends('is_company', 'name', 'parent_id.display_name', 'type', 'company_name')
def _compute_display_name(self):
diff = dict(show_address=None, show_address_only=None, show_email=None, html_format=None, show_vat=None)
@@ -1004,10 +1035,6 @@ class Partner(models.Model):
def _get_country_name(self):
return self.country_id.name or ''
def _get_placeholder_filename(self, field=None):
if self.is_company:
return '/base/static/img/company_image.png'
return super()._get_placeholder_filename(field)
class ResPartnerIndustry(models.Model):
+1 -17
View File
@@ -270,6 +270,7 @@ class Users(models.Model):
'signature', 'company_id', 'login', 'email', 'name', 'image_1920',
'image_1024', 'image_512', 'image_256', 'image_128', 'lang', 'tz',
'tz_offset', 'groups_id', 'partner_id', '__last_update', 'action_id',
'avatar_1920', 'avatar_1024', 'avatar_512', 'avatar_256', 'avatar_128',
]
@property
@@ -283,17 +284,6 @@ class Users(models.Model):
default_user_id = self.env['ir.model.data'].xmlid_to_res_id('base.default_user', raise_if_not_found=False)
return self.env['res.users'].browse(default_user_id).sudo().groups_id if default_user_id else []
@api.model
def _get_default_image(self):
""" Get a default image when the user is created without image
Inspired to _get_default_image method in
https://github.com/odoo/odoo/blob/11.0/odoo/addons/base/res/res_partner.py
"""
image_path = get_module_resource('base', 'static/img', 'avatar.png')
image = base64.b64encode(open(image_path, 'rb').read())
return image_process(image, colorize=True)
partner_id = fields.Many2one('res.partner', required=True, ondelete='restrict', auto_join=True,
string='Related Partner', help='Partner-related data of the user')
login = fields.Char(required=True, help="Used to log into the system")
@@ -338,7 +328,6 @@ class Users(models.Model):
compute='_compute_accesses_count', compute_sudo=True)
groups_count = fields.Integer('# Groups', help='Number of groups that apply to the current user',
compute='_compute_accesses_count', compute_sudo=True)
image_1920 = fields.Image(related='partner_id.image_1920', inherited=True, readonly=False, default=_get_default_image)
_sql_constraints = [
('login_key', 'UNIQUE (login)', 'You can not have two users with the same login !')
@@ -1036,11 +1025,6 @@ class Users(models.Model):
if hasattr(self, 'check_credentials'):
_logger.warning("The check_credentials method of res.users has been renamed _check_credentials. One of your installed modules defines one, but it will not be called anymore.")
def _get_placeholder_filename(self, field=None):
image_fields = ['image_%s' % size for size in [1920, 1024, 512, 256, 128]]
if field in image_fields and not self:
return 'base/static/img/user-slash.png'
return super()._get_placeholder_filename(field=field)
def _mfa_url(self):
""" If an MFA method is enabled, returns the URL for its second step. """
+1 -1
View File
@@ -7,7 +7,7 @@
<field name="arch" type="xml">
<form string="Company">
<sheet>
<field name="logo" widget="image" class="oe_avatar"/>
<field name="logo" widget="image" class="oe_avatar"/>
<div class="oe_title">
<label for="name"/>
<h1>
+14 -10
View File
@@ -77,7 +77,8 @@
<form string="Contact">
<field name="is_company" invisible="1"/>
<field name="type" invisible="1"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"preview_image": "image_128"}'/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"preview_image": "avatar_128"}'/>
<div class="oe_title">
<field name="company_type" options="{'horizontal': true}" widget="radio" groups="base.group_no_one"/>
<h1>
@@ -108,7 +109,8 @@
<field name="priority" eval="20"/>
<field name="arch" type="xml">
<form string="Partner">
<field name="image_1920" widget='image' class="oe_avatar" options='{"preview_image": "image_128"}' readonly="1"/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"preview_image": "avatar_128"}' readonly="1"/>
<div class="oe_title">
<h1>
<field name="name" readonly="1"/>
@@ -151,7 +153,8 @@
<sheet>
<div class="oe_button_box" name="button_box"/>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"preview_image": "image_128"}'/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"preview_image": "avatar_128"}'/>
<div class="oe_title">
<field name="is_company" invisible="1"/>
<field name="commercial_partner_id" invisible="1"/>
@@ -324,7 +327,8 @@
<field name="company_id" invisible="1"/>
</group>
<group colspan="1">
<field name="image_1920" widget="image" class="oe_avatar" nolabel="1" options="{'image_preview': 'image_128'}"/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" widget="image" class="oe_avatar" nolabel="1" options="{'image_preview': 'avatar_128'}"/>
</group>
</group>
<field name="lang" invisible="True"/>
@@ -453,7 +457,7 @@
<field name="mobile"/>
<field name="state_id"/>
<field name="category_id"/>
<field name="image_128"/>
<field name="avatar_128"/>
<field name="type"/>
<templates>
<t t-name="kanban-box">
@@ -462,16 +466,16 @@
<t t-if="record.type.raw_value === 'delivery'" t-set="placeholder" t-value="'/base/static/img/truck.png'"/>
<t t-elif="record.type.raw_value === 'invoice'" t-set="placeholder" t-value="'/base/static/img/money.png'"/>
<t t-else="" t-set="placeholder" t-value="'/base/static/img/avatar_grey.png'"/>
<div class="o_kanban_image_fill_left d-none d-md-block" t-attf-style="background-image:url('#{kanban_image('res.partner', 'image_128', record.id.raw_value, placeholder)}')">
<img class="o_kanban_image_inner_pic" t-if="record.parent_id.raw_value" t-att-alt="record.parent_id.value" t-att-src="kanban_image('res.partner', 'image_128', record.parent_id.raw_value)"/>
<div class="o_kanban_image_fill_left d-none d-md-block" t-attf-style="background-image:url('#{kanban_image('res.partner', 'avatar_128', record.id.raw_value, placeholder)}')">
<img class="o_kanban_image_inner_pic" t-if="record.parent_id.raw_value" t-att-alt="record.parent_id.value" t-att-src="kanban_image('res.partner', 'avatar_128', record.parent_id.raw_value)"/>
</div>
<div class="o_kanban_image d-md-none" t-attf-style="background-image:url('#{kanban_image('res.partner', 'image_128', record.id.raw_value, placeholder)}')">
<img class="o_kanban_image_inner_pic" t-if="record.parent_id.raw_value" t-att-alt="record.parent_id.value" t-att-src="kanban_image('res.partner', 'image_128', record.parent_id.raw_value)"/>
<div class="o_kanban_image d-md-none" t-attf-style="background-image:url('#{kanban_image('res.partner', 'avatar_128', record.id.raw_value, placeholder)}')">
<img class="o_kanban_image_inner_pic" t-if="record.parent_id.raw_value" t-att-alt="record.parent_id.value" t-att-src="kanban_image('res.partner', 'avatar_128', record.parent_id.raw_value)"/>
</div>
</t>
<t t-else="">
<t t-set="placeholder" t-value="'/base/static/img/company_image.png'"/>
<div class="o_kanban_image_fill_left o_kanban_image_full" t-attf-style="background-image: url(#{kanban_image('res.partner', 'image_128', record.id.raw_value, placeholder)})" role="img"/>
<div class="o_kanban_image_fill_left o_kanban_image_full" t-attf-style="background-image: url(#{kanban_image('res.partner', 'avatar_128', record.id.raw_value, placeholder)})" role="img"/>
</t>
<div class="oe_kanban_details d-flex flex-column">
<strong class="o_kanban_record_title oe_partner_heading"><field name="display_name"/></strong>
+7 -4
View File
@@ -148,7 +148,8 @@
<div class="alert alert-info text-center mb-3" attrs="{'invisible': [('id', '>', 0)]}" role="alert">
You are inviting a new user.
</div>
<field name="image_1920" widget='image' class="oe_avatar" options='{"zoom": true, "preview_image": "image_128"}'/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"zoom": true, "preview_image": "avatar_128"}'/>
<div class="oe_title">
<label for="name"/>
<h1><field name="name" placeholder="e.g. John Doe" required="1"/></h1>
@@ -212,7 +213,8 @@
<field name="partner_id" required="0" readonly="1"/>
</div>
</div>
<field name="image_1920" widget='image' class="oe_avatar" options='{"preview_image": "image_128"}'/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" widget='image' class="oe_avatar" options='{"preview_image": "avatar_128"}'/>
<div class="oe_title">
<label for="name"/>
<h1><field name="name" placeholder="e.g. John Doe" required="1"/></h1>
@@ -292,7 +294,7 @@
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click">
<div class="o_kanban_image">
<img alt="Avatar" t-att-src="kanban_image('res.users', 'image_128', record.id.raw_value)"/>
<img alt="Avatar" t-att-src="kanban_image('res.users', 'avatar_128', record.id.raw_value)"/>
</div>
<div class="oe_kanban_details">
<ul>
@@ -425,7 +427,8 @@
<field eval="18" name="priority"/>
<field name="arch" type="xml">
<form string="Users">
<field name="image_1920" readonly="0" widget='image' class="oe_right oe_avatar" options='{"preview_image": "image_128"}'/>
<field name="avatar_128" invisible="1"/>
<field name="image_1920" readonly="0" widget='image' class="oe_right oe_avatar" options='{"preview_image": "avatar_128"}'/>
<h1>
<field name="name" readonly="1" class="oe_inline"/>
</h1>
-7
View File
@@ -6354,13 +6354,6 @@ Fields:
return result
def _get_placeholder_filename(self, field=None):
""" Returns the filename of the placeholder to use,
set on web/static/img by default, or the
complete path to access it (eg: module/path/to/image.png).
"""
return 'placeholder.png'
def _populate_factories(self):
""" Generates a factory for the different fields of the model.