diff options
| author | Aurélien Warnon <awa@odoo.com> | 2019-02-11 13:18:44 +0000 |
|---|---|---|
| committer | Thibault Delavallée <tde@odoo.com> | 2019-02-11 17:47:22 +0000 |
| commit | e5536fc07b24292cffb61c7b84fff0de8d92409a (patch) | |
| tree | 298fa2ae2878caa833d78957c4b59c38747d1f53 /addons/website_slides | |
| parent | 779d9c9b90d05c96a301af60034a58ddda05f381 (diff) | |
[IMP] website_slides: add invite wizard for 'invite' slide_channel
Purpose
=======
Currently, the slide_channels having a visibility configured as 'invite' could
only add members manually.
This commit adds an 'Invite' button on top of the slide channel form that opens
a wizard allowing to send emails to contacts and also make them members of the
related slide channel.
Diffstat (limited to 'addons/website_slides')
| -rw-r--r-- | addons/website_slides/__manifest__.py | 1 | ||||
| -rw-r--r-- | addons/website_slides/data/mail_data.xml | 24 | ||||
| -rw-r--r-- | addons/website_slides/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/website_slides/models/slide_channel.py | 26 | ||||
| -rw-r--r-- | addons/website_slides/models/slide_channel_invite.py | 131 | ||||
| -rw-r--r-- | addons/website_slides/views/slide_channel_invite_views.xml | 37 | ||||
| -rw-r--r-- | addons/website_slides/views/slide_channel_views.xml | 3 |
7 files changed, 223 insertions, 0 deletions
diff --git a/addons/website_slides/__manifest__.py b/addons/website_slides/__manifest__.py index 28189db040a..fd307f967a5 100644 --- a/addons/website_slides/__manifest__.py +++ b/addons/website_slides/__manifest__.py @@ -31,6 +31,7 @@ Share and Publish Videos, Presentations and Documents' 'views/slide_channel_partner_views.xml', 'views/slide_channel_views.xml', 'views/slide_channel_tag_views.xml', + 'views/slide_channel_invite_views.xml', 'views/website_slides_menu_views.xml', 'data/website_slides_ir_data.xml', 'data/mail_data.xml', diff --git a/addons/website_slides/data/mail_data.xml b/addons/website_slides/data/mail_data.xml index db042d4f313..3b397545940 100644 --- a/addons/website_slides/data/mail_data.xml +++ b/addons/website_slides/data/mail_data.xml @@ -60,5 +60,29 @@ <field name="default" eval="True"/> <field name="description">Presentation Published</field> </record> + + <!-- Slide channel invite feature --> + <record id="mail_template_slide_channel_invite" model="mail.template"> + <field name="name">Channel: Invite by email</field> + <field name="model_id" ref="model_slide_channel_partner" /> + <field name="subject">You have been invited to join ${object.channel_id.name}</field> + <field name="body_html" type="html"> +<div style="margin: 0px; padding: 0px; font-size: 13px;"> + <p style="margin: 0px; padding: 0px; font-size: 13px;"> + Dear ${object.partner_id.name or 'participant'}<br/><br/> + You have been invited to join a new course: ${object.channel_id.name}. + <div style="margin: 16px 0px 16px 0px;"> + <a href="${(object.channel_id.website_url) | safe}" + style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;"> + Click here to start the course. + </a> + </div> + Enjoy this exclusive content ! + </p> +</div> + </field> + <field name="auto_delete" eval="True"/> + <field name="user_signature" eval="False"/> + </record> </data> </odoo> diff --git a/addons/website_slides/models/__init__.py b/addons/website_slides/models/__init__.py index 3f6a3e49ddf..4872648aced 100644 --- a/addons/website_slides/models/__init__.py +++ b/addons/website_slides/models/__init__.py @@ -4,5 +4,6 @@ from . import gamification_challenge from . import slide_slide from . import slide_channel from . import slide_channel_tag +from . import slide_channel_invite from . import res_config_settings from . import website diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 41c42984af2..7a02b4f21ab 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -7,6 +7,7 @@ import uuid from odoo import api, fields, models, tools, _ from odoo.addons.http_routing.models.ir_http import slug from odoo.tools.translate import html_translate +from odoo.exceptions import UserError from odoo.osv import expression @@ -205,6 +206,31 @@ class Channel(models.Model): return action + @api.multi + def action_channel_invite(self): + self.ensure_one() + + if self.visibility != 'invite': + raise UserError(_("You cannot send invitations for channels that are not set as 'invite'.")) + + template = self.env.ref('website_slides.mail_template_slide_channel_invite', raise_if_not_found=False) + + local_context = dict( + self.env.context, + default_channel_id=self.id, + default_use_template=bool(template), + default_template_id=template and template.id or False, + notif_layout='mail.mail_notification_light', + ) + return { + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'slide.channel.invite', + 'target': 'new', + 'context': local_context, + } + # --------------------------------------------------------- # ORM Overrides # --------------------------------------------------------- diff --git a/addons/website_slides/models/slide_channel_invite.py b/addons/website_slides/models/slide_channel_invite.py new file mode 100644 index 00000000000..a1af975b297 --- /dev/null +++ b/addons/website_slides/models/slide_channel_invite.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging +import re + +from email.utils import formataddr + +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + +_logger = logging.getLogger(__name__) + +emails_split = re.compile(r"[;,\n\r]+") + + +class SlideChannelInvite(models.TransientModel): + _name = 'slide.channel.invite' + _description = 'Channel Invitation Wizard' + + @api.model + def _default_email_from(self): + if self.env.user.email: + return formataddr((self.env.user.name, self.env.user.email)) + raise UserError(_("Unable to post message, please configure the sender's email address.")) + + @api.model + def _default_author_id(self): + return self.env.user.partner_id + + # composer content + subject = fields.Char('Subject') + body = fields.Html('Contents', default='', sanitize_style=True) + attachment_ids = fields.Many2many('ir.attachment', string='Attachments') + template_id = fields.Many2one( + 'mail.template', 'Use template', + domain="[('model', '=', 'slide.channel.partner')]") + # origin + email_from = fields.Char('From', default=_default_email_from) + author_id = fields.Many2one( + 'res.partner', 'Author', + ondelete='set null', default=_default_author_id) + # recipients + partner_ids = fields.Many2many('res.partner', string='Recipients') + # slide channel + channel_id = fields.Many2one('slide.channel', string='Slide channel', required=True) + channel_url = fields.Char(related="channel_id.website_url", readonly=True) + + @api.onchange('template_id') + def _onchange_template_id(self): + """ Make the 'subject' and 'body' field match the selected template_id """ + if self.template_id: + self.subject = self.template_id.subject + self.body = self.template_id.body_html + + @api.onchange('partner_ids') + def _onchange_partner_ids(self): + if self.partner_ids: + signup_allowed = self.env['res.users'].sudo()._get_signup_invitation_scope() == 'b2c' + if not signup_allowed: + invalid_partners = self.env['res.partner'].search([ + ('user_ids', '=', False), + ('id', 'in', self.partner_ids.ids) + ]) + if invalid_partners: + raise UserError( + _('The following recipients have no user account: %s. You should create user accounts for them or allow external sign up in configuration.' % + (','.join(invalid_partners.mapped('name'))))) + + @api.model + def create(self, values): + if values.get('template_id') and not (values.get('body') or values.get('subject')): + template = self.env['mail.template'].browse(values['template_id']) + if not values.get('subject'): + values['subject'] = template.subject + if not values.get('body'): + values['body'] = template.body_html + return super(SlideChannelInvite, self).create(values) + + @api.multi + def action_invite(self): + """ Process the wizard content and proceed with sending the related + email(s), rendering any template patterns on the fly if needed """ + self.ensure_one() + + mail_values = [] + for partner_id in self.partner_ids: + slide_channel_partner = self.channel_id._action_add_member(target_partner=partner_id) + if slide_channel_partner: + mail_values.append(self._prepare_mail_values(slide_channel_partner)) + + # TODO awa: change me to create multi when mail.mail supports it + for mail_value in mail_values: + self.env['mail.mail'].sudo().create(mail_value) + + return {'type': 'ir.actions.act_window_close'} + + def _prepare_mail_values(self, slide_channel_partner): + """ Create mail specific for recipient """ + subject = self.env['mail.template']._render_template(self.subject, 'slide.channel.partner', slide_channel_partner.id, post_process=True) + body = self.env['mail.template']._render_template(self.body, 'slide.channel.partner', slide_channel_partner.id, post_process=True) + # post the message + mail_values = { + 'email_from': self.email_from, + 'author_id': self.author_id.id, + 'model': None, + 'res_id': None, + 'subject': subject, + 'body_html': body, + 'attachment_ids': [(4, att.id) for att in self.attachment_ids], + 'auto_delete': True, + 'recipient_ids': [(4, slide_channel_partner.partner_id.id)] + } + + # optional support of notif_layout in context + notif_layout = self.env.context.get('notif_layout', self.env.context.get('custom_layout')) + if notif_layout: + try: + template = self.env.ref(notif_layout, raise_if_not_found=True) + except ValueError: + _logger.warning('QWeb template %s not found when sending slide channel mails. Sending without layouting.' % (notif_layout)) + else: + template_ctx = { + 'message': self.env['mail.message'].sudo().new(dict(body=mail_values['body_html'], record_name=self.channel_id.name)), + 'model_description': self.env['ir.model']._get('website_slides.slide_channel').display_name, + 'company': self.env.user.company_id, + } + body = template.render(template_ctx, engine='ir.qweb', minimal_qcontext=True) + mail_values['body_html'] = self.env['mail.thread']._replace_local_links(body) + + return mail_values diff --git a/addons/website_slides/views/slide_channel_invite_views.xml b/addons/website_slides/views/slide_channel_invite_views.xml new file mode 100644 index 00000000000..cdb9ddd79bf --- /dev/null +++ b/addons/website_slides/views/slide_channel_invite_views.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <record id="slide_channel_invite_view_form" model="ir.ui.view"> + <field name="name">slide.channel.invite.view.form</field> + <field name="model">slide.channel.invite</field> + <field name="arch" type="xml"> + <form string="Compose Email"> + <group col="1"> + <group col="2"> + <field name="partner_ids" + widget="many2many_tags_email" + placeholder="Add existing contacts..." + context="{'force_email':True, 'show_email':True, 'no_create_edit': True}"/> + </group> + <group col="2"> + <field name="subject" placeholder="Subject..."/> + </group> + <field name="body" options="{'style-inline': true}"/> + <group> + <group> + <field name="attachment_ids" widget="many2many_binary"/> + </group> + <group> + <field name="template_id" label="Use template"/> + </group> + </group> + </group> + <footer> + <button string="Send" name="action_invite" type="object" class="btn-primary"/> + <button string="Cancel" class="btn-secondary" special="cancel"/> + </footer> + </form> + </field> + </record> + </data> +</odoo> diff --git a/addons/website_slides/views/slide_channel_views.xml b/addons/website_slides/views/slide_channel_views.xml index 8c0cb04d138..fa128ecfd89 100644 --- a/addons/website_slides/views/slide_channel_views.xml +++ b/addons/website_slides/views/slide_channel_views.xml @@ -47,6 +47,9 @@ <field name="model">slide.channel</field> <field name="arch" type="xml"> <form string="Channels"> + <header> + <button name="action_channel_invite" string="Invite" type="object" class="oe_highlight" attrs="{'invisible': [('visibility', '!=', 'invite')]}"/> + </header> <sheet> <div class="oe_button_box" name="button_box"> <button name="action_redirect_to_members" |
