[MERGE] mail: move website part of message and rating to mail / portal
PURPOSE
Clean posting process and improve mail.message definition and comprehension.
Move as much possible content from website_* modules to portal_* modules
to ease their use in customer portal without having to depend on website.
SPECIFICATIONS: MAIL.MESSAGE DESCRIPTION
Move description from website_mail directly to mail. Anyway it is a
non stored computed field and therefore having it available directly
in mail does not cost anything.
SPECIFICATIONS: MAIL.MESSAGE WEBSITE PUBLISHED
Website mail defines a website_published field allowing to publish / unpublish
comments on the frontend of some modules. This field has several drawbacks :
* it is used only for front-end people (portal, public) and has no real
effect in chatter / classic discussions;
* it is used only in some advanced front-end module and is not available
in portal by default;
* its naming is not really correct as it is not linked to fields coming
from the website_published mixin and its behavior is not really
the same;
* its use is a bit duplicated with internal flag coming from subtype
allowing to hide messages related to an internal subtype;
* there are overrides of standard mail.message methods just to handle
this flag;
In this merge we change that field by an is_internal flag directly on
mail.message model itself.It tells if share people (customers, share users)
are allowed to read the message. This field can be given through posting
API or set manually using widgets. It is also used in access rights custom
methods and managed like the internal flag of subtypes.
Mailgateway was already using an internal flag for internal note replies. It
is renamed to is_internal and propagated as it is now a standard field. It
also eases code understanding.
Portal is updated to allow managing the flag directly. It means customer portal
now natively allows to moderate customer comments without any need of website
modules.
Rating is updated accordingly. An is_internal field is added, replacing the
related on website published.
Also rename subtype parameter of message_post to subtype_xmlid. In
order to be more explicit subtype parameter is renamed to subtype_xmlid.
It therefore clearly indicates it should be a valid subtype Xml ID. Support
of ill formatted Xml IDs is removed because there is no reason to try to
add some random prefix. Give something that exists or go to hell, punk !
Finally mail.message and rating.rating form views are updated to include
is_internal flag and perform some view cleaning. An entry of ratings in
Technical/Discuss is added for debugging / management purpose.
SPECIFICATIONS: MOVE WEBSITE RATING TO PORTAL RATING
Now that publish feature is replaced by internal directly in mail and portal
rating portal inclusion can be done safely in a portal_rating module instead
of website_rating. It allows to remove the dependency and give the rating
feature of customer chatter directly in portal applications.
LINKS
Task ID 2071556
PR #38692
Related: odoo/enterprise#6410
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
+3
-3
@@ -1002,9 +1002,9 @@ file_filter = addons/website_profile/i18n/<lang>.po
|
||||
source_file = addons/website_profile/i18n/website_profile.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.website_rating]
|
||||
file_filter = addons/website_rating/i18n/<lang>.po
|
||||
source_file = addons/website_rating/i18n/website_rating.pot
|
||||
[odoo-master.portal_rating]
|
||||
file_filter = addons/portal_rating/i18n/<lang>.po
|
||||
source_file = addons/portal_rating/i18n/portal_rating.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.website_sale]
|
||||
|
||||
@@ -226,7 +226,7 @@ class Attendee(models.Model):
|
||||
result = self.write({'state': 'accepted'})
|
||||
for attendee in self:
|
||||
if attendee.event_id:
|
||||
attendee.event_id.message_post(body=_("%s has accepted invitation") % (attendee.common_name), subtype="calendar.subtype_invitation")
|
||||
attendee.event_id.message_post(body=_("%s has accepted invitation") % (attendee.common_name), subtype_xmlid="calendar.subtype_invitation")
|
||||
return result
|
||||
|
||||
def do_decline(self):
|
||||
@@ -234,7 +234,7 @@ class Attendee(models.Model):
|
||||
res = self.write({'state': 'declined'})
|
||||
for attendee in self:
|
||||
if attendee.event_id:
|
||||
attendee.event_id.message_post(body=_("%s has declined invitation") % (attendee.common_name), subtype="calendar.subtype_invitation")
|
||||
attendee.event_id.message_post(body=_("%s has declined invitation") % (attendee.common_name), subtype_xmlid="calendar.subtype_invitation")
|
||||
return res
|
||||
|
||||
|
||||
|
||||
@@ -562,13 +562,13 @@ class Challenge(models.Model):
|
||||
challenge.message_post(
|
||||
body=body_html,
|
||||
partner_ids=challenge.mapped('user_ids.partner_id.id'),
|
||||
subtype='mail.mt_comment',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
)
|
||||
if challenge.report_message_group_id:
|
||||
challenge.report_message_group_id.message_post(
|
||||
body=body_html,
|
||||
subtype='mail.mt_comment')
|
||||
subtype_xmlid='mail.mt_comment')
|
||||
|
||||
else:
|
||||
# generate individual reports
|
||||
@@ -586,13 +586,13 @@ class Challenge(models.Model):
|
||||
challenge.message_notify(
|
||||
body=body_html,
|
||||
partner_ids=[user.partner_id.id],
|
||||
subtype='mail.mt_comment',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
)
|
||||
if challenge.report_message_group_id:
|
||||
challenge.report_message_group_id.message_post(
|
||||
body=body_html,
|
||||
subtype='mail.mt_comment',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
)
|
||||
return challenge.write({'last_report_date': fields.Date.today()})
|
||||
|
||||
@@ -219,7 +219,7 @@ class Goal(models.Model):
|
||||
self.message_notify(
|
||||
body=body_html,
|
||||
partner_ids=[self.user_id.partner_id.id],
|
||||
subtype='mail.mt_comment',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
)
|
||||
|
||||
|
||||
@@ -666,7 +666,7 @@ class HolidaysRequest(models.Model):
|
||||
# Automatic validation should be done in sudo, because user might not have the rights to do it by himself
|
||||
holiday_sudo.action_validate()
|
||||
holiday_sudo.message_subscribe(partner_ids=[holiday._get_responsible_for_approval().partner_id.id])
|
||||
holiday_sudo.message_post(body=_("The time off has been automatically approved"), subtype="mt_comment") # Message from OdooBot (sudo)
|
||||
holiday_sudo.message_post(body=_("The time off has been automatically approved"), subtype_xmlid="mail.mt_comment") # Message from OdooBot (sudo)
|
||||
elif not self._context.get('import_file'):
|
||||
holiday_sudo.activity_update()
|
||||
return holiday
|
||||
|
||||
@@ -608,7 +608,7 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
||||
""" User may communicate on its own leaves, even if validated """
|
||||
self.employee_leave.with_user(self.user_employee_id).message_post(
|
||||
body='I haz messaging',
|
||||
subtype='mail.mt_comment',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
message_type='comment'
|
||||
)
|
||||
|
||||
@@ -616,7 +616,7 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
||||
|
||||
self.employee_leave.with_user(self.user_employee_id).message_post(
|
||||
body='I still haz messaging',
|
||||
subtype='mail.mt_comment',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
message_type='comment'
|
||||
)
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ class Applicant(models.Model):
|
||||
applicant.job_id.write({'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1})
|
||||
applicant.job_id.message_post(
|
||||
body=_('New Employee %s Hired') % applicant.partner_name if applicant.partner_name else applicant.name,
|
||||
subtype="hr_recruitment.mt_job_applicant_hired")
|
||||
subtype_xmlid="hr_recruitment.mt_job_applicant_hired")
|
||||
applicant.message_post_with_view(
|
||||
'hr_recruitment.applicant_hired_template',
|
||||
values={'applicant': applicant},
|
||||
|
||||
@@ -112,6 +112,7 @@ class LivechatController(http.Controller):
|
||||
'rating': rate,
|
||||
'consumed': True,
|
||||
'feedback': reason,
|
||||
'is_internal': False,
|
||||
}
|
||||
if not channel.rating_ids:
|
||||
res_model_id = request.env['ir.model'].sudo().search([('model', '=', channel._name)], limit=1).id
|
||||
|
||||
@@ -53,7 +53,7 @@ class MailChatController(BusController):
|
||||
message = mail_channel.with_context(mail_create_nosubscribe=True).message_post(author_id=author_id,
|
||||
email_from=email_from, body=body,
|
||||
message_type='comment',
|
||||
subtype='mail.mt_comment')
|
||||
subtype_xmlid='mail.mt_comment')
|
||||
return message and message.id or False
|
||||
|
||||
@route(['/mail/chat_history'], type="json", auth="public", cors="*")
|
||||
|
||||
@@ -27,7 +27,7 @@ def _admin_password_warn(uid):
|
||||
.message_post(
|
||||
body=_("Your password is the default (admin)! If this system is exposed to untrusted users it is important to change it immediately for security reasons. I will keep nagging you about it!"),
|
||||
message_type='comment',
|
||||
subtype='mail.mt_comment'
|
||||
subtype_xmlid='mail.mt_comment'
|
||||
)
|
||||
|
||||
class Home(web.Home):
|
||||
|
||||
@@ -280,7 +280,7 @@ class Channel(models.Model):
|
||||
if not self.email_send:
|
||||
notification = _('<div class="o_mail_notification">left <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (self.id, self.name,)
|
||||
# post 'channel left' message as root since the partner just unsubscribed from the channel
|
||||
self.sudo().message_post(body=notification, subtype="mail.mt_comment", author_id=partner.id)
|
||||
self.sudo().message_post(body=notification, subtype_xmlid="mail.mt_comment", author_id=partner.id)
|
||||
return result
|
||||
|
||||
def _notify_get_groups(self):
|
||||
@@ -780,7 +780,7 @@ class Channel(models.Model):
|
||||
}
|
||||
else:
|
||||
notification = _('<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (channel.id, channel.name,)
|
||||
self.message_post(body=notification, message_type="notification", subtype="mail.mt_comment", author_id=partner.id)
|
||||
self.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment", author_id=partner.id)
|
||||
|
||||
# broadcast the channel header to the added partner
|
||||
self._broadcast(partner_ids)
|
||||
@@ -861,7 +861,7 @@ class Channel(models.Model):
|
||||
added = self.action_follow()
|
||||
if added and self.channel_type == 'channel' and not self.email_send:
|
||||
notification = _('<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (self.id, self.name,)
|
||||
self.message_post(body=notification, message_type="notification", subtype="mail.mt_comment")
|
||||
self.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
|
||||
if added and self.moderation_guidelines:
|
||||
self._send_guidelines(self.env.user.partner_id)
|
||||
@@ -886,7 +886,7 @@ class Channel(models.Model):
|
||||
'channel_partner_ids': [(4, self.env.user.partner_id.id)]
|
||||
})
|
||||
notification = _('<div class="o_mail_notification">created <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (new_channel.id, new_channel.name,)
|
||||
new_channel.message_post(body=notification, message_type="notification", subtype="mail.mt_comment")
|
||||
new_channel.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment")
|
||||
channel_info = new_channel.channel_info('creation')[0]
|
||||
self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', self.env.user.partner_id.id), channel_info)
|
||||
return channel_info
|
||||
|
||||
@@ -12,7 +12,6 @@ from odoo import _, api, fields, models, modules, tools
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import groupby
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
_image_dataurl = re.compile(r'(data:image/[a-z]+?);base64,([a-z0-9+/\n]{3,}=*)\n*([\'"])(?: data-filename="([^"]*)")?', re.I)
|
||||
@@ -43,6 +42,9 @@ class Message(models.Model):
|
||||
subject = fields.Char('Subject')
|
||||
date = fields.Datetime('Date', default=fields.Datetime.now)
|
||||
body = fields.Html('Contents', default='', sanitize_style=True)
|
||||
description = fields.Char(
|
||||
'Short description', compute="_compute_description",
|
||||
help='Message description: either the subject, or the beginning of the body')
|
||||
attachment_ids = fields.Many2many(
|
||||
'ir.attachment', 'message_attachment_rel',
|
||||
'message_id', 'attachment_id',
|
||||
@@ -71,6 +73,7 @@ class Message(models.Model):
|
||||
mail_activity_type_id = fields.Many2one(
|
||||
'mail.activity.type', 'Mail Activity Type',
|
||||
index=True, ondelete='set null')
|
||||
is_internal = fields.Boolean('Employee Only', help='Hide to public / portal users, independently from subtype configuration.')
|
||||
# origin
|
||||
email_from = fields.Char('From', help="Email address of the sender. This field is set when no matching partner is found and replaces the author_id field in the chatter.")
|
||||
author_id = fields.Many2one(
|
||||
@@ -124,7 +127,7 @@ class Message(models.Model):
|
||||
('rejected', 'Rejected')], string="Moderation Status", index=True)
|
||||
moderator_id = fields.Many2one('res.users', string="Moderated By", index=True)
|
||||
need_moderation = fields.Boolean('Need moderation', compute='_compute_need_moderation', search='_search_need_moderation')
|
||||
#keep notification layout informations to be able to generate mail again
|
||||
# keep notification layout informations to be able to generate mail again
|
||||
email_layout_xmlid = fields.Char('Layout', copy=False) # xml id of layout
|
||||
add_sign = fields.Boolean(default=True)
|
||||
# `test_adv_activity`, `test_adv_activity_full`, `test_message_assignation_inbox`,...
|
||||
@@ -137,6 +140,14 @@ class Message(models.Model):
|
||||
mail_ids = fields.One2many('mail.mail', 'mail_message_id', string='Mails', groups="base.group_system")
|
||||
canned_response_ids = fields.One2many('mail.shortcode', 'message_ids', string="Canned Responses", store=False)
|
||||
|
||||
def _compute_description(self):
|
||||
for message in self:
|
||||
if message.subject:
|
||||
message.description = message.subject
|
||||
else:
|
||||
plaintext_ct = '' if not message.body else tools.html2plaintext(message.body)
|
||||
message.description = plaintext_ct[:30] + '%s' % (' [...]' if len(plaintext_ct) >= 30 else '')
|
||||
|
||||
def _get_needaction(self):
|
||||
""" Need action on a mail.message = notified on my channel """
|
||||
my_messages = self.env['mail.notification'].sudo().search([
|
||||
@@ -224,9 +235,9 @@ class Message(models.Model):
|
||||
return super(Message, self)._search(
|
||||
args, offset=offset, limit=limit, order=order,
|
||||
count=count, access_rights_uid=access_rights_uid)
|
||||
# Non-employee see only messages with a subtype (aka, no internal logs)
|
||||
# Non-employee see only messages with a subtype and not internal
|
||||
if not self.env['res.users'].has_group('base.group_user'):
|
||||
args = ['&', '&', ('subtype_id', '!=', False), ('subtype_id.internal', '=', False)] + list(args)
|
||||
args = expression.AND([self._get_search_domain_share(), args])
|
||||
# Perform a super with count as False, to have the ids, not a counter
|
||||
ids = super(Message, self)._search(
|
||||
args, offset=offset, limit=limit, order=order,
|
||||
@@ -349,7 +360,9 @@ class Message(models.Model):
|
||||
FROM "%s" AS message
|
||||
LEFT JOIN "mail_message_subtype" as subtype
|
||||
ON message.subtype_id = subtype.id
|
||||
WHERE message.message_type = %%s AND (message.subtype_id IS NULL OR subtype.internal IS TRUE) AND message.id = ANY (%%s)''' % (self._table), ('comment', self.ids,))
|
||||
WHERE message.message_type = %%s AND
|
||||
(message.is_internal IS TRUE OR message.subtype_id IS NULL OR subtype.internal IS TRUE) AND
|
||||
message.id = ANY (%%s)''' % (self._table), ('comment', self.ids,))
|
||||
if self._cr.fetchall():
|
||||
raise AccessError(
|
||||
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % (self._description, operation)
|
||||
@@ -1200,7 +1213,7 @@ class Message(models.Model):
|
||||
else:
|
||||
messages |= message
|
||||
|
||||
for author, author_messages in groupby(messages, itemgetter('author_id')):
|
||||
for author, author_messages in tools.groupby(messages, itemgetter('author_id')):
|
||||
self.env['bus.bus'].sendone(
|
||||
(self._cr.dbname, 'res.partner', author.id),
|
||||
{'type': 'mail_failure', 'elements': self.env['mail.message'].concat(*author_messages)._format_mail_failures()}
|
||||
@@ -1269,3 +1282,6 @@ class Message(models.Model):
|
||||
'message_needaction',
|
||||
'message_needaction_counter',
|
||||
], ids=[res_id])
|
||||
|
||||
def _get_search_domain_share(self):
|
||||
return ['&', '&', ('is_internal', '=', False), ('subtype_id', '!=', False), ('subtype_id.internal', '=', False)]
|
||||
|
||||
@@ -1056,7 +1056,7 @@ class MailThread(models.AbstractModel):
|
||||
parent_message = self.env['mail.message'].sudo().browse(message_dict['parent_id'])
|
||||
partner_ids = []
|
||||
if not subtype_id:
|
||||
if message_dict.pop('internal', False):
|
||||
if message_dict.get('is_internal'):
|
||||
subtype_id = self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note')
|
||||
if parent_message and parent_message.author_id:
|
||||
partner_ids = [parent_message.author_id.id]
|
||||
@@ -1367,7 +1367,7 @@ class MailThread(models.AbstractModel):
|
||||
'references': references,
|
||||
'in_reply_to': in-reply-to,
|
||||
'parent_id': parent mail.message based on in_reply_to or references,
|
||||
'internal': answer to an internal message (note),
|
||||
'is_internal': answer to an internal message (note),
|
||||
'date': date,
|
||||
'attachments': [('file1', 'bytes'),
|
||||
('file2', 'bytes')}
|
||||
@@ -1437,18 +1437,15 @@ class MailThread(models.AbstractModel):
|
||||
stored_date = datetime.datetime.now()
|
||||
msg_dict['date'] = stored_date.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
parent_ids = False
|
||||
if msg_dict['in_reply_to']:
|
||||
parent_ids = self.env['mail.message'].search([('message_id', '=', msg_dict['in_reply_to'])], limit=1)
|
||||
if parent_ids:
|
||||
msg_dict['parent_id'] = parent_ids.id
|
||||
msg_dict['internal'] = parent_ids.subtype_id and parent_ids.subtype_id.internal or False
|
||||
|
||||
if msg_dict['references'] and 'parent_id' not in msg_dict:
|
||||
if msg_dict['references'] and not parent_ids:
|
||||
references_msg_id_list = tools.mail_header_msgid_re.findall(msg_dict['references'])
|
||||
parent_ids = self.env['mail.message'].search([('message_id', 'in', [x.strip() for x in references_msg_id_list])], limit=1)
|
||||
if parent_ids:
|
||||
msg_dict['parent_id'] = parent_ids.id
|
||||
msg_dict['internal'] = parent_ids.subtype_id and parent_ids.subtype_id.internal or False
|
||||
if parent_ids:
|
||||
msg_dict['parent_id'] = parent_ids.id
|
||||
msg_dict['is_internal'] = parent_ids.subtype_id and parent_ids.subtype_id.internal or False
|
||||
|
||||
msg_dict.update(self._message_parse_extract_payload(message, save_original=save_original))
|
||||
msg_dict.update(self._message_parse_extract_bounce(message, msg_dict))
|
||||
@@ -1777,7 +1774,7 @@ class MailThread(models.AbstractModel):
|
||||
def message_post(self, *,
|
||||
body='', subject=None, message_type='notification',
|
||||
email_from=None, author_id=None, parent_id=False,
|
||||
subtype_id=False, subtype=None, partner_ids=None, channel_ids=None,
|
||||
subtype_xmlid=None, subtype_id=False, partner_ids=None, channel_ids=None,
|
||||
attachments=None, attachment_ids=None,
|
||||
add_sign=True, record_name=False,
|
||||
**kwargs):
|
||||
@@ -1791,8 +1788,6 @@ class MailThread(models.AbstractModel):
|
||||
:param int parent_id: handle thread formation
|
||||
:param int subtype_id: subtype_id of the message, mainly use fore
|
||||
followers mechanism
|
||||
:param int subtype: xmlid that will be used to compute subtype_id
|
||||
if subtype_id is not given.
|
||||
:param list(int) partner_ids: partner_ids to notify
|
||||
:param list(int) channel_ids: channel_ids to notify
|
||||
:param list(tuple(str,str), tuple(str,str, dict) or int) attachments : list of attachment tuples in the form
|
||||
@@ -1814,7 +1809,9 @@ class MailThread(models.AbstractModel):
|
||||
raise ValueError('message_post should only be call to post message on record. Use message_notify instead')
|
||||
|
||||
if 'model' in msg_kwargs or 'res_id' in msg_kwargs:
|
||||
raise ValueError("message_post doesn't support model and res_id parameters anymore. Please call message_post on record")
|
||||
raise ValueError("message_post doesn't support model and res_id parameters anymore. Please call message_post on record.")
|
||||
if 'subtype' in kwargs:
|
||||
raise ValueError("message_post doesn't support subtype parameter anymore. Please give a valid subtype_id or subtype_xmlid value instead.")
|
||||
|
||||
self = self.with_lang() # add lang to context imediatly since it will be usefull in various flows latter.
|
||||
|
||||
@@ -1832,11 +1829,10 @@ class MailThread(models.AbstractModel):
|
||||
# Find the message's author
|
||||
author_id, email_from = self._message_compute_author(author_id, email_from, raise_exception=True)
|
||||
|
||||
if subtype_xmlid:
|
||||
subtype_id = self.env['ir.model.data'].xmlid_to_res_id(subtype_xmlid)
|
||||
if not subtype_id:
|
||||
subtype = subtype or 'mt_note'
|
||||
if '.' not in subtype:
|
||||
subtype = 'mail.%s' % subtype
|
||||
subtype_id = self.env['ir.model.data'].xmlid_to_res_id(subtype)
|
||||
subtype_id = self.env['ir.model.data'].xmlid_to_res_id('mail_mt_note')
|
||||
|
||||
# automatically subscribe recipients if asked to
|
||||
if self._context.get('mail_post_autofollow') and partner_ids:
|
||||
@@ -1997,6 +1993,7 @@ class MailThread(models.AbstractModel):
|
||||
'email_from': email_from,
|
||||
'partner_ids': partner_ids,
|
||||
'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'),
|
||||
'is_internal': True,
|
||||
'record_name': False,
|
||||
'reply_to': MailThread._notify_get_reply_to(default=email_from, records=None)[False],
|
||||
'message_id': tools.generate_tracking_message_id('message-notify'),
|
||||
@@ -2025,6 +2022,7 @@ class MailThread(models.AbstractModel):
|
||||
'model': kwargs.get('model', self._name),
|
||||
'res_id': self.ids[0] if self.ids else False,
|
||||
'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'),
|
||||
'is_internal': True,
|
||||
'record_name': False,
|
||||
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from, records=None)[False],
|
||||
'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify
|
||||
@@ -2047,6 +2045,7 @@ class MailThread(models.AbstractModel):
|
||||
'message_type': message_type,
|
||||
'model': self._name,
|
||||
'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'),
|
||||
'is_internal': True,
|
||||
'record_name': False,
|
||||
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from, records=None)[False],
|
||||
'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify
|
||||
@@ -2402,7 +2401,7 @@ class MailThread(models.AbstractModel):
|
||||
if pid:
|
||||
if active is False:
|
||||
continue
|
||||
pdata = {'id': pid, 'active': active, 'share': pshare, 'groups': groups}
|
||||
pdata = {'id': pid, 'active': active, 'share': pshare, 'groups': groups or []}
|
||||
if notif == 'inbox':
|
||||
recipient_data['partners'].append(dict(pdata, notif=notif, type='user'))
|
||||
elif not pshare and notif: # has an user and is not shared, is therefore user
|
||||
|
||||
@@ -104,7 +104,7 @@ class PublisherWarrantyContract(AbstractModel):
|
||||
poster = user
|
||||
for message in result["messages"]:
|
||||
try:
|
||||
poster.message_post(body=message, subtype='mt_comment', partner_ids=[user.partner_id.id])
|
||||
poster.message_post(body=message, subtype_xmlid='mail.mt_comment', partner_ids=[user.partner_id.id])
|
||||
except Exception:
|
||||
pass
|
||||
if result.get('enterprise_info'):
|
||||
|
||||
@@ -205,14 +205,14 @@ var ChatterComposer = BasicComposer.extend({
|
||||
return new Promise(function (resolve, reject) {
|
||||
self._super().then(function (message) {
|
||||
message = _.extend(message, {
|
||||
subtype: 'mail.mt_comment',
|
||||
subtype_xmlid: 'mail.mt_comment',
|
||||
message_type: 'comment',
|
||||
context: _.defaults({}, self.context, session.user_context),
|
||||
});
|
||||
|
||||
// Subtype
|
||||
if (self.options.isLog) {
|
||||
message.subtype = 'mail.mt_note';
|
||||
message.subtype_xmlid = 'mail.mt_note';
|
||||
}
|
||||
|
||||
// Partner_ids
|
||||
|
||||
@@ -1496,7 +1496,7 @@ var Discuss = AbstractAction.extend({
|
||||
var self = this;
|
||||
var options = {};
|
||||
if (this._selectedMessage) {
|
||||
messageData.subtype = this._selectedMessage.isNote() ? 'mail.mt_note': 'mail.mt_comment';
|
||||
messageData.subtype_xmlid = this._selectedMessage.isNote() ? 'mail.mt_note': 'mail.mt_comment';
|
||||
messageData.subtype_id = false;
|
||||
messageData.message_type = 'comment';
|
||||
|
||||
|
||||
@@ -443,7 +443,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, {
|
||||
return this._super.apply(this, arguments).then(function (messageData) {
|
||||
_.extend(messageData, {
|
||||
message_type: 'comment',
|
||||
subtype: 'mail.mt_comment',
|
||||
subtype_xmlid: 'mail.mt_comment',
|
||||
command: data.command,
|
||||
});
|
||||
return self._rpc({
|
||||
|
||||
@@ -331,7 +331,7 @@ var DocumentThread = Thread.extend({
|
||||
_.extend(messageData, {
|
||||
context: data.context,
|
||||
message_type: data.message_type,
|
||||
subtype: data.subtype || "mail.mt_comment",
|
||||
subtype_xmlid: data.subtype_xmlid || "mail.mt_comment",
|
||||
subtype_id: data.subtype_id,
|
||||
});
|
||||
return self._rpc({
|
||||
|
||||
@@ -1008,8 +1008,8 @@ QUnit.test('chatter: post, receive and star messages', async function (assert) {
|
||||
body: args.kwargs.body,
|
||||
date: "2016-12-20 10:35:40",
|
||||
id: messageID,
|
||||
is_note: args.kwargs.subtype === 'mail.mt_note',
|
||||
is_discussion: args.kwargs.subtype === 'mail.mt_comment',
|
||||
is_note: args.kwargs.subtype_xmlid === 'mail.mt_note',
|
||||
is_discussion: args.kwargs.subtype_xmlid === 'mail.mt_comment',
|
||||
is_notification: false,
|
||||
is_starred: false,
|
||||
model: 'partner',
|
||||
@@ -1403,8 +1403,8 @@ QUnit.test('chatter: post a message and switch in edit mode', async function (as
|
||||
body: args.kwargs.body,
|
||||
date: "2016-12-20 10:35:40",
|
||||
id: 42,
|
||||
is_note: args.kwargs.subtype === 'mail.mt_note',
|
||||
is_discussion: args.kwargs.subtype === 'mail.mt_comment',
|
||||
is_note: args.kwargs.subtype_xmlid === 'mail.mt_note',
|
||||
is_discussion: args.kwargs.subtype_xmlid === 'mail.mt_comment',
|
||||
is_starred: false,
|
||||
model: 'partner',
|
||||
res_id: 2,
|
||||
@@ -3061,8 +3061,8 @@ QUnit.test('chatter: suggested partner auto-follow on message post', async funct
|
||||
body: args.kwargs.body,
|
||||
date: "2016-12-20 10:35:40",
|
||||
id: messageID,
|
||||
is_note: args.kwargs.subtype === 'mail.mt_note',
|
||||
is_discussion: args.kwargs.subtype === 'mail.mt_comment',
|
||||
is_note: args.kwargs.subtype_xmlid === 'mail.mt_note',
|
||||
is_discussion: args.kwargs.subtype_xmlid === 'mail.mt_comment',
|
||||
is_notification: false,
|
||||
is_starred: false,
|
||||
model: 'partner',
|
||||
|
||||
@@ -62,6 +62,6 @@
|
||||
name="Email Blacklist"
|
||||
action="mail_blacklist_action"
|
||||
parent="mail.mail_menu_technical"
|
||||
sequence="20"/>
|
||||
sequence="22"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
id="menu_email_followers"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_followers"
|
||||
sequence="20"
|
||||
sequence="21"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
</record>
|
||||
|
||||
<!-- mail.message form: short view !-->
|
||||
<record model="ir.ui.view" id="view_message_form">
|
||||
<field name="name">mail.message.form</field>
|
||||
<record id="mail_message_view_form" model="ir.ui.view">
|
||||
<field name="name">mail.message.view.form</field>
|
||||
<field name="model">mail.message</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="arch" type="xml">
|
||||
@@ -31,16 +31,17 @@
|
||||
<field name="date"/>
|
||||
<field name="email_from"/>
|
||||
<field name="author_id"/>
|
||||
<field name="record_name"/>
|
||||
<field name="moderator_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="parent_id"/>
|
||||
<field name="model"/>
|
||||
<field name="res_id" widget="integer"/>
|
||||
<field name="message_type"/>
|
||||
<field name="subtype_id"/>
|
||||
<field name="is_internal"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="model"/>
|
||||
<field name="res_id" widget="integer"/>
|
||||
<field name="record_name"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="moderation_status"/>
|
||||
<field name="moderator_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
@@ -81,7 +82,7 @@
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Tracking">
|
||||
<page string="Tracking" name="page_tracking">
|
||||
<field name="tracking_value_ids"/>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
id="mail_notification_menu"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="mail_notification_action"
|
||||
sequence="30"
|
||||
sequence="20"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
</data></odoo>
|
||||
|
||||
@@ -24,6 +24,6 @@ class Channel(models.Model):
|
||||
'name': 'OdooBot'
|
||||
})
|
||||
message = _("Hello,<br/>Odoo's chat helps employees collaborate efficiently. I'm here to help you discover its features.<br/><b>Try to send me an emoji :)</b>")
|
||||
channel.sudo().message_post(body=message, author_id=odoobot_id, message_type="comment", subtype="mail.mt_comment")
|
||||
channel.sudo().message_post(body=message, author_id=odoobot_id, message_type="comment", subtype_xmlid="mail.mt_comment")
|
||||
self.env.user.odoobot_state = 'onboarding_emoji'
|
||||
return channel
|
||||
|
||||
@@ -86,7 +86,7 @@ def _message_post_helper(res_model, res_id, message, token='', _hash=False, pid=
|
||||
message_post_args = dict(
|
||||
body=message,
|
||||
message_type=kw.pop('message_type', "comment"),
|
||||
subtype=kw.pop('subtype', "mt_comment"),
|
||||
subtype_xmlid=kw.pop('subtype_xmlid', "mail.mt_comment"),
|
||||
author_id=author_id,
|
||||
**kw
|
||||
)
|
||||
@@ -158,6 +158,7 @@ class PortalChatter(http.Controller):
|
||||
'options': {
|
||||
'message_count': message_data['message_count'],
|
||||
'is_user_public': is_user_public,
|
||||
'is_user_employee': request.env.user.has_group('base.group_user'),
|
||||
'is_user_publisher': request.env.user.has_group('website.group_website_publisher'),
|
||||
'display_composer': display_composer,
|
||||
'partner_id': request.env.user.partner_id.id
|
||||
@@ -183,13 +184,19 @@ class PortalChatter(http.Controller):
|
||||
raise Forbidden()
|
||||
# Non-employee see only messages with not internal subtype (aka, no internal logs)
|
||||
if not request.env['res.users'].has_group('base.group_user'):
|
||||
domain = expression.AND([Message._non_employee_message_domain(), domain])
|
||||
domain = expression.AND([Message._get_search_domain_share(), domain])
|
||||
Message = request.env['mail.message'].sudo()
|
||||
return {
|
||||
'messages': Message.search(domain, limit=limit, offset=offset).portal_message_format(),
|
||||
'message_count': Message.search_count(domain)
|
||||
}
|
||||
|
||||
@http.route(['/mail/update_is_internal'], type='json', auth="user", website=True)
|
||||
def portal_message_update_is_internal(self, message_id, is_internal):
|
||||
message = request.env['mail.message'].browse(int(message_id))
|
||||
message.write({'is_internal': is_internal})
|
||||
return message.is_internal
|
||||
|
||||
|
||||
class MailController(MailController):
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class MailMessage(models.Model):
|
||||
def portal_message_format(self):
|
||||
return self._portal_message_format([
|
||||
'id', 'body', 'date', 'author_id', 'email_from', # base message fields
|
||||
'message_type', 'subtype_id', 'subject', # message specific
|
||||
'message_type', 'subtype_id', 'is_internal', 'subject', # message specific
|
||||
'model', 'res_id', 'record_name', # document related
|
||||
])
|
||||
|
||||
@@ -24,7 +24,3 @@ class MailMessage(models.Model):
|
||||
if not attachment.get('access_token'):
|
||||
attachment['access_token'] = IrAttachmentSudo.browse(attachment['id']).generate_access_token()[0]
|
||||
return message_values
|
||||
|
||||
@api.model
|
||||
def _non_employee_message_domain(self):
|
||||
return ['&', ('subtype_id', '!=', False), ('subtype_id.internal', '=', False)]
|
||||
|
||||
@@ -20,7 +20,8 @@ var PortalChatter = publicWidget.Widget.extend({
|
||||
template: 'portal.Chatter',
|
||||
xmlDependencies: ['/portal/static/src/xml/portal_chatter.xml'],
|
||||
events: {
|
||||
"click .o_portal_chatter_pager_btn": '_onClickPager',
|
||||
'click .o_portal_chatter_pager_btn': '_onClickPager',
|
||||
'click .o_portal_chatter_js_is_internal': 'async _onClickUpdateIsInternal',
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -45,6 +46,7 @@ var PortalChatter = publicWidget.Widget.extend({
|
||||
'pager_scope': 5,
|
||||
'pager_start': 1,
|
||||
'is_user_public': true,
|
||||
'is_user_employee': false,
|
||||
'is_user_publisher': false,
|
||||
'hash': false,
|
||||
'pid': false,
|
||||
@@ -254,6 +256,35 @@ var PortalChatter = publicWidget.Widget.extend({
|
||||
var page = $(ev.currentTarget).data('page');
|
||||
this._changeCurrentPage(page);
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle is_internal state of message. Update both node data and
|
||||
* classes to ensure DOM is updated accordingly to RPC call result.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onClickUpdateIsInternal: function (ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var $elem = $(ev.currentTarget);
|
||||
return this._rpc({
|
||||
route: '/mail/update_is_internal',
|
||||
params: {
|
||||
message_id: $elem.data('message-id'),
|
||||
is_internal: ! $elem.data('is-internal'),
|
||||
},
|
||||
}).then(function (result) {
|
||||
$elem.data('is-internal', result);
|
||||
if (result === true) {
|
||||
$elem.addClass('o_portal_message_internal_on');
|
||||
$elem.removeClass('o_portal_message_internal_off');
|
||||
}
|
||||
else {
|
||||
$elem.addClass('o_portal_message_internal_off');
|
||||
$elem.removeClass('o_portal_message_internal_on');
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
publicWidget.registry.portalChatter = publicWidget.Widget.extend({
|
||||
|
||||
@@ -524,4 +524,16 @@ form label {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.o_portal_message_internal_off {
|
||||
.btn-danger {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.o_portal_message_internal_on {
|
||||
.btn-success {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
<div class="media o_portal_chatter_message" t-att-id="'message-' + message.id">
|
||||
<img class="o_portal_chatter_avatar" t-att-src="message.author_avatar_url" alt="avatar"/>
|
||||
<div class="media-body">
|
||||
<t t-call="portal.chatter_internal_toggle" t-if="widget.options['is_user_employee']"/>
|
||||
|
||||
<div class="o_portal_chatter_message_title">
|
||||
<h5><t t-esc="message.author_id[1]"/></h5>
|
||||
@@ -110,6 +111,18 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Chatter: internal toggle widget -->
|
||||
<t t-name="portal.chatter_internal_toggle">
|
||||
<div t-attf-class="float-right o_portal_chatter_js_is_internal #{message.is_internal and 'o_portal_message_internal_on' or 'o_portal_message_internal_off'}"
|
||||
t-att-data-message-id="message.id"
|
||||
t-att-data-is-internal="message.is_internal">
|
||||
<button class="btn btn-danger"
|
||||
title="Currently restricted to internal employees, click to make it available to everyone viewing this document.">Employees Only</button>
|
||||
<button class="btn btn-success"
|
||||
title="Currently available to everyone viewing this document, click to restrict to internal employees.">Visible</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="portal.pager">
|
||||
<div class="o_portal_chatter_pager">
|
||||
<t t-if="!_.isEmpty(widget.get('pager'))">
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Website Rating',
|
||||
'name': 'Portal Rating',
|
||||
'category': 'Hidden',
|
||||
'version': '1.0',
|
||||
'description': """
|
||||
Bridge module for rating on website stuff.
|
||||
Bridge module adding rating capabilities on portal. It includes notably
|
||||
inclusion of rating directly within the customer portal discuss widget.
|
||||
""",
|
||||
'depends': ['rating', 'website_mail'],
|
||||
'depends': [
|
||||
'portal',
|
||||
'rating',
|
||||
],
|
||||
'data': [
|
||||
'views/assets.xml',
|
||||
'views/rating_views.xml',
|
||||
@@ -0,0 +1,251 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * portal_rating
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server saas~12.5\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-08-26 08:17+0000\n"
|
||||
"PO-Revision-Date: 2019-08-26 08:17+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"#{widget.options['force_submit_url'] ? widget.options['force_submit_url'] : "
|
||||
"'/mail/chatter_post'}"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_rating_composer.xml:0
|
||||
#, python-format
|
||||
msgid "×"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_rating_composer.xml:0
|
||||
#: code:addons/portal_rating/static/src/xml/portal_rating_composer.xml:0
|
||||
#, python-format
|
||||
msgid "Add a review"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_tools.xml:0
|
||||
#, python-format
|
||||
msgid "Average"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: model:ir.model.fields,field_description:portal_rating.field_rating_rating__publisher_id
|
||||
msgid "Commented by"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: model:ir.model.fields,field_description:portal_rating.field_rating_rating__publisher_datetime
|
||||
msgid "Commented on"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_tools.xml:0
|
||||
#, python-format
|
||||
msgid "Details"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_tools.xml:0
|
||||
#: code:addons/portal_rating/static/src/xml/portal_tools.xml:0
|
||||
#, python-format
|
||||
msgid "Half a star"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/js/portal_composer.js:0
|
||||
#, python-format
|
||||
msgid "I don't like it"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/js/portal_composer.js:0
|
||||
#, python-format
|
||||
msgid "I hate it"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/js/portal_composer.js:0
|
||||
#, python-format
|
||||
msgid "I like it"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/js/portal_composer.js:0
|
||||
#, python-format
|
||||
msgid "I love it"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: code:addons/portal_rating/controllers/portal_rating.py:0
|
||||
#, python-format
|
||||
msgid "Invalid rating"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/js/portal_composer.js:0
|
||||
#, python-format
|
||||
msgid "It's okay"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: model:ir.model,name:portal_rating.model_mail_message
|
||||
msgid "Message"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_rating_composer.xml:0
|
||||
#: code:addons/portal_rating/static/src/xml/portal_rating_composer.xml:0
|
||||
#, python-format
|
||||
msgid "Modify your review"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_tools.xml:0
|
||||
#: code:addons/portal_rating/static/src/xml/portal_tools.xml:0
|
||||
#, python-format
|
||||
msgid "One star"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: code:addons/portal_rating/models/rating.py:0
|
||||
#, python-format
|
||||
msgid "Only the publisher of the website can change the rating comment"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid "Post comment"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: model:ir.model.fields,field_description:portal_rating.field_rating_rating__website_published
|
||||
msgid "Published"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid "Published on"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: model:ir.model.fields,field_description:portal_rating.field_rating_rating__publisher_comment
|
||||
msgid "Publisher Comment"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: model_terms:ir.ui.view,arch_db:portal_rating.rating_rating_view_form
|
||||
msgid "Publisher comment"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: model:ir.model,name:portal_rating.model_rating_rating
|
||||
msgid "Rating"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_tools.xml:0
|
||||
#, python-format
|
||||
msgid "Remove Selection"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid "Update comment"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#: model:ir.model.fields,help:portal_rating.field_rating_rating__website_published
|
||||
msgid "Visible on the website as a comment"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/js/portal_chatter.js:0
|
||||
#, python-format
|
||||
msgid "Write your comment"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#: code:addons/portal_rating/static/src/xml/portal_chatter.xml:0
|
||||
#, python-format
|
||||
msgid "avatar"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_rating_composer.xml:0
|
||||
#, python-format
|
||||
msgid "ratings)"
|
||||
msgstr ""
|
||||
|
||||
#. module: portal_rating
|
||||
#. openerp-web
|
||||
#: code:addons/portal_rating/static/src/xml/portal_tools.xml:0
|
||||
#, python-format
|
||||
msgid "stars"
|
||||
msgstr ""
|
||||
@@ -6,8 +6,6 @@ from odoo import fields, models, exceptions, _
|
||||
class Rating(models.Model):
|
||||
_inherit = 'rating.rating'
|
||||
|
||||
# Add this related field to mail.message for performance reason
|
||||
website_published = fields.Boolean(related='message_id.website_published', store=True, readonly=False)
|
||||
# Adding information for comment a rating message
|
||||
publisher_comment = fields.Text("Publisher Comment")
|
||||
publisher_id = fields.Many2one('res.partner', 'Commented by',
|
||||
+5
-5
@@ -30,8 +30,8 @@ PortalChatter.include({
|
||||
}),
|
||||
xmlDependencies: (PortalChatter.prototype.xmlDependencies || [])
|
||||
.concat([
|
||||
'/website_rating/static/src/xml/portal_tools.xml',
|
||||
'/website_rating/static/src/xml/portal_chatter.xml'
|
||||
'/portal_rating/static/src/xml/portal_tools.xml',
|
||||
'/portal_rating/static/src/xml/portal_chatter.xml'
|
||||
]),
|
||||
|
||||
/**
|
||||
@@ -256,7 +256,7 @@ PortalChatter.include({
|
||||
|
||||
var oldRating = this.messages[messageIndex].rating;
|
||||
data.rating.publisher_comment = oldRating.publisher_comment ? oldRating.publisher_comment : '';
|
||||
this._getCommentContainer($source).html($(qweb.render("website_rating.chatter_rating_publisher_form", data)));
|
||||
this._getCommentContainer($source).html($(qweb.render("portal_rating.chatter_rating_publisher_form", data)));
|
||||
this._focusTextComment($source);
|
||||
},
|
||||
|
||||
@@ -309,7 +309,7 @@ PortalChatter.include({
|
||||
if (self.messages[messageIndex].rating.publisher_comment !== '') {
|
||||
// Remove the button comment if exist and render the comment
|
||||
self._getCommentButton($source).addClass('d-none');
|
||||
self._getCommentContainer($source).html($(qweb.render("website_rating.chatter_rating_publisher_comment", {
|
||||
self._getCommentContainer($source).html($(qweb.render("portal_rating.chatter_rating_publisher_comment", {
|
||||
rating: self.messages[messageIndex].rating,
|
||||
is_publisher: self.options.is_user_publisher
|
||||
})));
|
||||
@@ -335,7 +335,7 @@ PortalChatter.include({
|
||||
rating: this.messages[messageIndex].rating,
|
||||
is_publisher: this.options.is_user_publisher
|
||||
};
|
||||
this._getCommentContainer($source).html($(qweb.render("website_rating.chatter_rating_publisher_comment", data)));
|
||||
this._getCommentContainer($source).html($(qweb.render("portal_rating.chatter_rating_publisher_comment", data)));
|
||||
} else {
|
||||
this._getCommentContainer($source).empty();
|
||||
}
|
||||
+3
-3
@@ -16,11 +16,11 @@ var STAR_RATING_RATIO = 2; // conversion factor from the star (1-5) to the db r
|
||||
* a popup with the portal composer when clicking on it.
|
||||
**/
|
||||
var RatingPopupComposer = publicWidget.Widget.extend({
|
||||
template: 'website_rating.PopupComposer',
|
||||
template: 'portal_rating.PopupComposer',
|
||||
xmlDependencies: [
|
||||
'/portal/static/src/xml/portal_chatter.xml',
|
||||
'/website_rating/static/src/xml/portal_tools.xml',
|
||||
'/website_rating/static/src/xml/portal_rating_composer.xml',
|
||||
'/portal_rating/static/src/xml/portal_tools.xml',
|
||||
'/portal_rating/static/src/xml/portal_rating_composer.xml',
|
||||
],
|
||||
|
||||
init: function (parent, options) {
|
||||
+8
-8
@@ -9,7 +9,7 @@
|
||||
<attribute name="t-attf-action">#{widget.options['force_submit_url'] ? widget.options['force_submit_url'] : '/mail/chatter_post'}</attribute>
|
||||
</t>
|
||||
<t t-jquery=".o_portal_chatter_composer_form input[name='csrf_token']" t-operation="after">
|
||||
<t t-call="website_rating.rating_star_input">
|
||||
<t t-call="portal_rating.rating_star_input">
|
||||
<t t-set="default_rating" t-value="widget.options['default_rating_value']"/>
|
||||
</t>
|
||||
<input type="hidden" name="message_id" t-att-value="widget.options['default_message_id']" t-if="widget.options['default_message_id']"/>
|
||||
@@ -19,7 +19,7 @@
|
||||
<t t-extend="portal.chatter_messages">
|
||||
<t t-jquery="t[t-raw='message.body']" t-operation="before">
|
||||
<t t-if="message['rating_value']">
|
||||
<t t-call="website_rating.rating_stars_static">
|
||||
<t t-call="portal_rating.rating_stars_static">
|
||||
<t t-set="val" t-value="message.rating_value"/>
|
||||
</t>
|
||||
</t>
|
||||
@@ -28,7 +28,7 @@
|
||||
<!--Only possible if a rating is link to the message, for now we can't comment if no rating
|
||||
is link to the message (because publisher comment data
|
||||
is on the rating.rating model - one comment max) -->
|
||||
<t t-if="message.rating and message.rating.id" t-call="website_rating.chatter_rating_publisher">
|
||||
<t t-if="message.rating and message.rating.id" t-call="portal_rating.chatter_rating_publisher">
|
||||
<t t-set="is_publisher" t-value="widget.options['is_user_publisher']"/>
|
||||
<t t-set="rating" t-value="message.rating"/>
|
||||
</t>
|
||||
@@ -38,7 +38,7 @@
|
||||
<t t-extend="portal.Chatter">
|
||||
<t t-jquery="t[t-call='portal.chatter_message_count']" t-operation="replace">
|
||||
<t t-if="widget.options['display_rating']">
|
||||
<t t-call="website_rating.rating_card"/>
|
||||
<t t-call="portal_rating.rating_card"/>
|
||||
</t>
|
||||
<t t-if="!widget.options['display_rating']">
|
||||
<t t-call="portal.chatter_message_count"/>
|
||||
@@ -49,7 +49,7 @@
|
||||
<!--
|
||||
New templates specific of rating in Chatter
|
||||
-->
|
||||
<t t-name="website_rating.chatter_rating_publisher">
|
||||
<t t-name="portal_rating.chatter_rating_publisher">
|
||||
<div class="o_wrating_publisher_container">
|
||||
<button t-if="is_publisher"
|
||||
t-attf-class="btn px-2 btn-sm border o_wrating_js_publisher_comment_btn {{ rating.publisher_comment !== '' ? 'd-none' : '' }}"
|
||||
@@ -57,12 +57,12 @@
|
||||
<i class="fa fa-comment text-muted mr-1"/>Comment
|
||||
</button>
|
||||
<div class="o_wrating_publisher_comment mt-2 mb-2">
|
||||
<t t-if="rating.publisher_comment" t-call="website_rating.chatter_rating_publisher_comment"/>
|
||||
<t t-if="rating.publisher_comment" t-call="portal_rating.chatter_rating_publisher_comment"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website_rating.chatter_rating_publisher_comment">
|
||||
<t t-name="portal_rating.chatter_rating_publisher_comment">
|
||||
<div class="media o_portal_chatter_message">
|
||||
<img class="o_portal_chatter_avatar" t-att-src="rating.publisher_avatar" alt="avatar"/>
|
||||
<div class="media-body">
|
||||
@@ -89,7 +89,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="website_rating.chatter_rating_publisher_form">
|
||||
<t t-name="portal_rating.chatter_rating_publisher_form">
|
||||
<div t-if="is_publisher" class="media o_portal_chatter_message shadow bg-white rounded px-3 py-3 my-1">
|
||||
<img class="o_portal_chatter_avatar" t-att-src="rating.publisher_avatar" alt="avatar"/>
|
||||
<div class="media-body">
|
||||
+2
-2
@@ -4,10 +4,10 @@
|
||||
Popup Rating Composer Widget
|
||||
It can alse be used to modify a message
|
||||
-->
|
||||
<t t-name="website_rating.PopupComposer">
|
||||
<t t-name="portal_rating.PopupComposer">
|
||||
<div class="d-flex flex-wrap align-items-center">
|
||||
<div class="text-nowrap">
|
||||
<t t-call="website_rating.rating_stars_static">
|
||||
<t t-call="portal_rating.rating_stars_static">
|
||||
<t t-set="val" t-value="widget.rating_avg || 0"/>
|
||||
<t t-set="inline_mode" t-value="true"/>
|
||||
</t>
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="website_rating.rating_stars_static">
|
||||
<t t-name="portal_rating.rating_stars_static">
|
||||
<t t-set="val_integer" t-value="Math.floor(val)"/>
|
||||
<t t-set="val_decimal" t-value="val - val_integer"/>
|
||||
<t t-set="empty_star" t-value="5 - (val_integer+Math.ceil(val_decimal))"/>
|
||||
@@ -17,13 +17,13 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website_rating.rating_card">
|
||||
<t t-name="portal_rating.rating_card">
|
||||
<div class="row o_website_rating_card_container">
|
||||
<div class="col-lg-3 offset-lg-2" t-if="!_.isEmpty(widget.get('rating_card_values'))">
|
||||
<p><strong>Average</strong></p>
|
||||
<div class="o_website_rating_avg text-center">
|
||||
<h1><t t-esc="widget.get('rating_card_values')['avg']"/></h1>
|
||||
<t t-call="website_rating.rating_stars_static">
|
||||
<t t-call="portal_rating.rating_stars_static">
|
||||
<t t-set="val" t-value="widget.get('rating_card_values')['avg'] || 0"/>
|
||||
</t>
|
||||
<t t-call="portal.chatter_message_count"/>
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website_rating.rating_star_input">
|
||||
<t t-name="portal_rating.rating_star_input">
|
||||
<div class="o_rating_star_card" t-if="widget.options['display_rating']">
|
||||
<t t-set="val_integer" t-value="Math.floor(default_rating)"/>
|
||||
<t t-set="val_decimal" t-value="default_rating - val_integer"/>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="assets_frontend" inherit_id="web_editor.assets_frontend" name="Portal Rating Assets">
|
||||
<xpath expr="//link[last()]" position="after">
|
||||
<link rel="stylesheet" type="text/scss" href="/portal_rating/static/src/scss/portal_rating.scss"/>
|
||||
</xpath>
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/portal_rating/static/src/js/portal_chatter.js"></script>
|
||||
<script type="text/javascript" src="/portal_rating/static/src/js/portal_composer.js"></script>
|
||||
<script type="text/javascript" src="/portal_rating/static/src/js/portal_rating_composer.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<field name="model">rating.rating</field>
|
||||
<field name="inherit_id" ref="rating.rating_rating_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='feedback']" position="after">
|
||||
<xpath expr="//field[@name='is_internal']" position="after">
|
||||
<field name="publisher_comment" string="Publisher comment"/>
|
||||
</xpath>
|
||||
</field>
|
||||
@@ -930,8 +930,8 @@ class Task(models.Model):
|
||||
return self.project_id.partner_id
|
||||
return res
|
||||
|
||||
def rating_apply(self, rate, token=None, feedback=None, subtype=None):
|
||||
return super(Task, self).rating_apply(rate, token=token, feedback=feedback, subtype="project.mt_task_rating")
|
||||
def rating_apply(self, rate, token=None, feedback=None, subtype_xmlid=None):
|
||||
return super(Task, self).rating_apply(rate, token=token, feedback=feedback, subtype_xmlid="project.mt_task_rating")
|
||||
|
||||
def _rating_get_parent_field_name(self):
|
||||
return 'project_id'
|
||||
|
||||
@@ -10,11 +10,12 @@ This module allows a customer to give rating.
|
||||
'mail',
|
||||
],
|
||||
'data': [
|
||||
'views/rating_view.xml',
|
||||
'views/rating_rating_views.xml',
|
||||
'views/rating_template.xml',
|
||||
'views/mail_message_views.xml',
|
||||
'views/assets.xml',
|
||||
'security/ir.model.access.csv'
|
||||
],
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ class Rating(models.Model):
|
||||
'mail.message', string="Linked message",
|
||||
index=True, ondelete='cascade',
|
||||
help="Associated message when posting a review. Mainly used in website addons.")
|
||||
is_internal = fields.Boolean('Employee Only', readonly=False, related='message_id.is_internal', store=True)
|
||||
access_token = fields.Char('Security Token', default=_default_access_token, help="Access token to set the rating of the value")
|
||||
consumed = fields.Boolean(string="Filled Rating", help="Enabled if the rating has been filled.")
|
||||
|
||||
|
||||
@@ -134,7 +134,8 @@ class RatingMixin(models.AbstractModel):
|
||||
'partner_id': partner.id,
|
||||
'rated_partner_id': rated_partner.id,
|
||||
'res_model_id': record_model_id,
|
||||
'res_id': self.id
|
||||
'res_id': self.id,
|
||||
'is_internal': False,
|
||||
})
|
||||
else:
|
||||
rating = ratings[0]
|
||||
@@ -168,7 +169,7 @@ class RatingMixin(models.AbstractModel):
|
||||
subtype_id=subtype_id
|
||||
)
|
||||
|
||||
def rating_apply(self, rate, token=None, feedback=None, subtype=None):
|
||||
def rating_apply(self, rate, token=None, feedback=None, subtype_xmlid=None):
|
||||
""" Apply a rating given a token. If the current model inherits from
|
||||
mail.thread mixin, a message is posted on its chatter. User going through
|
||||
this method should have at least employee rights because of rating
|
||||
@@ -178,7 +179,7 @@ class RatingMixin(models.AbstractModel):
|
||||
:param float rate : the rating value to apply
|
||||
:param string token : access token
|
||||
:param string feedback : additional feedback
|
||||
:param string subtype : xml id of a valid mail.message.subtype
|
||||
:param string subtype_xmlid : xml id of a valid mail.message.subtype
|
||||
|
||||
:returns rating.rating record
|
||||
"""
|
||||
@@ -194,7 +195,7 @@ class RatingMixin(models.AbstractModel):
|
||||
self.message_post(
|
||||
body="<img src='/rating/static/src/img/rating_%s.png' alt=':%s/10' style='width:18px;height:18px;float:left;margin-right: 5px;'/>%s"
|
||||
% (rate, rate, feedback),
|
||||
subtype=subtype or "mail.mt_comment",
|
||||
subtype_xmlid=subtype_xmlid or "mail.mt_comment",
|
||||
author_id=rating.partner_id and rating.partner_id.id or None # None will set the default author in mail_thread.py
|
||||
)
|
||||
if hasattr(self, 'stage_id') and self.stage_id and hasattr(self.stage_id, 'auto_validation_kanban_state') and self.stage_id.auto_validation_kanban_state:
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo><data>
|
||||
<record id="mail_message_view_form" model="ir.ui.view">
|
||||
<field name="name">mail.message.view.form.inherit.rating</field>
|
||||
<field name="model">mail.message</field>
|
||||
<field name="inherit_id" ref="mail.mail_message_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='page_tracking']" position="after">
|
||||
<page string="Ratings" name="page_rating">
|
||||
<field name="rating_ids"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data></odoo>
|
||||
@@ -32,22 +32,21 @@
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="res_name"/>
|
||||
<field name="parent_res_name"/>
|
||||
<field name="res_name" string="Document"/>
|
||||
<field name="parent_res_name" string="Parent Holder"/>
|
||||
<field name="rated_partner_id"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="rating" invisible="1"/>
|
||||
<field name="create_date" string="Submitted on"/>
|
||||
<field name="is_internal"/>
|
||||
</group>
|
||||
<group>
|
||||
<div colspan="2" class="text-center">
|
||||
<field name="partner_id"/>
|
||||
<div colspan="2" class="text-center" name="rating_image_container">
|
||||
<field name="rating_image" widget='image'/>
|
||||
<div class="mt4">
|
||||
<strong><field name="rating_text"/></strong>
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<field name="create_date" string="Submitted on"/>
|
||||
<field name="feedback" attrs="{'invisible': [('feedback','=',False)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
@@ -169,4 +168,17 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="rating_rating_view" model="ir.actions.act_window">
|
||||
<field name="name">Ratings</field>
|
||||
<field name="res_model">rating.rating</field>
|
||||
<field name="view_mode">kanban,tree,graph,pivot,form</field>
|
||||
</record>
|
||||
|
||||
<!-- Add menu entry in Technical/Discuss -->
|
||||
<menuitem name="Ratings"
|
||||
id="rating_rating_menu_technical"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="rating_rating_view"
|
||||
sequence="30"/>
|
||||
|
||||
</odoo>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user