diff --git a/addons/portal/__openerp__.py b/addons/portal/__openerp__.py
index c7d9640d291..5d3bbc8a408 100644
--- a/addons/portal/__openerp__.py
+++ b/addons/portal/__openerp__.py
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
- 'name' : 'Portal',
+ 'name': 'Portal',
'version': '1.0',
'depends': [
'base',
diff --git a/addons/portal/data/portal_data.xml b/addons/portal/data/portal_data.xml
index bb38ca6156f..60da9bf2b63 100644
--- a/addons/portal/data/portal_data.xml
+++ b/addons/portal/data/portal_data.xml
@@ -1,5 +1,5 @@
-
+
@@ -40,4 +40,4 @@
${object.wizard_id.welcome_message or ''}]]>
-
+
diff --git a/addons/portal/data/portal_demo.xml b/addons/portal/data/portal_demo.xml
index c37b0915d5c..f70cc62b698 100644
--- a/addons/portal/data/portal_demo.xml
+++ b/addons/portal/data/portal_demo.xml
@@ -1,5 +1,5 @@
-
+
@@ -31,4 +31,4 @@ Mr Demo Portal]]>
-
+
diff --git a/addons/portal/models/res_groups.py b/addons/portal/models/res_groups.py
index aa30c818259..01897204bc5 100644
--- a/addons/portal/models/res_groups.py
+++ b/addons/portal/models/res_groups.py
@@ -1,14 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from openerp.osv import fields, osv
+from odoo import fields, models
-
-class portal(osv.osv):
+class PortalGroup(models.Model):
""" A portal is simply a group of users with the flag 'is_portal' set to True.
The flag 'is_portal' makes a user group usable as a portal.
"""
+
_inherit = 'res.groups'
- _columns = {
- 'is_portal': fields.boolean('Portal', help="If checked, this group is usable as a portal."),
- }
+
+ is_portal = fields.Boolean('Portal', help="If checked, this group is usable as a portal.")
diff --git a/addons/portal/tests/__init__.py b/addons/portal/tests/__init__.py
index e00314d2c88..3cc7401124d 100644
--- a/addons/portal/tests/__init__.py
+++ b/addons/portal/tests/__init__.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_portal
diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py
index 8d7cb10e3a1..6878b4c61be 100644
--- a/addons/portal/tests/test_portal.py
+++ b/addons/portal/tests/test_portal.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from openerp.addons.mail.tests.common import TestMail
-from openerp.tools.misc import mute_logger
+from odoo.addons.mail.tests.common import TestMail
+from odoo.tools.misc import mute_logger
-class test_portal(TestMail):
+class TestPortal(TestMail):
def test_mail_compose_access_rights(self):
self.group_pigs.write({'group_public_id': self.env.ref('base.group_portal').id})
diff --git a/addons/portal/views/portal_views.xml b/addons/portal/views/portal_views.xml
index a30edceba76..a48cc2c68f6 100644
--- a/addons/portal/views/portal_views.xml
+++ b/addons/portal/views/portal_views.xml
@@ -1,7 +1,5 @@
-
-
-
+
-
+
diff --git a/addons/portal/wizard/portal_wizard.py b/addons/portal/wizard/portal_wizard.py
index 670b563d99f..694186c0803 100644
--- a/addons/portal/wizard/portal_wizard.py
+++ b/addons/portal/wizard/portal_wizard.py
@@ -3,198 +3,187 @@
import logging
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-from openerp.tools import email_split
-from openerp import SUPERUSER_ID
-from openerp.exceptions import UserError
+from odoo.tools.translate import _
+from odoo.tools import email_split
+from odoo.exceptions import UserError
+
+from odoo import api, fields, models
+
_logger = logging.getLogger(__name__)
# welcome email sent to portal users
# (note that calling '_' has no effect except exporting those strings for translation)
-
def extract_email(email):
""" extract the email address from a user-friendly email address """
addresses = email_split(email)
return addresses[0] if addresses else ''
-
-class wizard(osv.osv_memory):
+class PortalWizard(models.TransientModel):
"""
A wizard to manage the creation/removal of portal users.
"""
+
_name = 'portal.wizard'
_description = 'Portal Access Management'
- _columns = {
- 'portal_id': fields.many2one('res.groups', domain=[('is_portal', '=', True)], required=True,
- string='Portal', help="The portal that users can be added in or removed from."),
- 'user_ids': fields.one2many('portal.wizard.user', 'wizard_id', string='Users'),
- 'welcome_message': fields.text(string='Invitation Message',
- help="This text is included in the email sent to new users of the portal."),
- }
+ def _default_portal(self):
+ return self.env['res.groups'].search([('is_portal', '=', True)], limit=1)
- def _default_portal(self, cr, uid, context):
- portal_ids = self.pool.get('res.groups').search(cr, uid, [('is_portal', '=', True)])
- return portal_ids and portal_ids[0] or False
+ portal_id = fields.Many2one('res.groups', domain=[('is_portal', '=', True)], required=True, string='Portal',
+ default=_default_portal, help="The portal that users can be added in or removed from.")
+ user_ids = fields.One2many('portal.wizard.user', 'wizard_id', string='Users')
+ welcome_message = fields.Text('Invitation Message', help="This text is included in the email sent to new users of the portal.")
- _defaults = {
- 'portal_id': _default_portal,
- }
-
- def onchange_portal_id(self, cr, uid, ids, portal_id, context=None):
+ @api.onchange('portal_id')
+ def onchange_portal_id(self):
# for each partner, determine corresponding portal.wizard.user records
- res_partner = self.pool.get('res.partner')
- partner_ids = context and context.get('active_ids') or []
+ partner_ids = self.env.context.get('active_ids', [])
contact_ids = set()
user_changes = []
- for partner in res_partner.browse(cr, SUPERUSER_ID, partner_ids, context):
- for contact in (partner.child_ids or [partner]):
+ for partner in self.env['res.partner'].sudo().browse(partner_ids):
+ contact_partners = partner.child_ids or [partner]
+ for contact in contact_partners:
# make sure that each contact appears at most once in the list
if contact.id not in contact_ids:
contact_ids.add(contact.id)
in_portal = False
if contact.user_ids:
- in_portal = portal_id in [g.id for g in contact.user_ids[0].groups_id]
+ in_portal = self.portal_id in contact.user_ids[0].groups_id
user_changes.append((0, 0, {
'partner_id': contact.id,
'email': contact.email,
'in_portal': in_portal,
}))
- return {'value': {'user_ids': user_changes}}
+ self.user_ids = user_changes
- def action_apply(self, cr, uid, ids, context=None):
- wizard = self.browse(cr, uid, ids[0], context)
- portal_user_ids = [user.id for user in wizard.user_ids]
- self.pool.get('portal.wizard.user').action_apply(cr, uid, portal_user_ids, context)
+ @api.multi
+ def action_apply(self):
+ self.ensure_one()
+ self.user_ids.action_apply()
return {'type': 'ir.actions.act_window_close'}
-class wizard_user(osv.osv_memory):
+
+class PortalWizardUser(models.TransientModel):
"""
A model to configure users in the portal wizard.
"""
+
_name = 'portal.wizard.user'
_description = 'Portal User Config'
- _columns = {
- 'wizard_id': fields.many2one('portal.wizard', string='Wizard', required=True, ondelete='cascade'),
- 'partner_id': fields.many2one('res.partner', string='Contact', required=True, readonly=True),
- 'email': fields.char(string='Email', size=240),
- 'in_portal': fields.boolean('In Portal'),
- 'user_id': fields.many2one('res.users', string='Login User'),
- }
+ wizard_id = fields.Many2one('portal.wizard', string='Wizard', required=True, ondelete='cascade')
+ partner_id = fields.Many2one('res.partner', string='Contact', required=True, readonly=True)
+ email = fields.Char('Email')
+ in_portal = fields.Boolean('In Portal')
+ user_id = fields.Many2one('res.users', string='Login User')
- def get_error_messages(self, cr, uid, ids, context=None):
- res_users = self.pool.get('res.users')
+ @api.multi
+ def get_error_messages(self):
emails = []
- error_empty = []
- error_emails = []
- error_user = []
- ctx = dict(context or {}, active_test=False)
- for wizard_user in self.browse(cr, SUPERUSER_ID, ids, ctx):
- if wizard_user.in_portal and not wizard_user.partner_id.user_ids:
- email = extract_email(wizard_user.email)
- if not email:
- error_empty.append(wizard_user.partner_id)
- elif email in emails and email not in error_emails:
- error_emails.append(wizard_user.partner_id)
- user = res_users.search(cr, SUPERUSER_ID, [('login', '=', email)], context=ctx)
- if user:
- error_user.append(wizard_user.partner_id)
- emails.append(email)
+ partners_error_empty = self.env['res.partner']
+ partners_error_emails = self.env['res.partner']
+ partners_error_user = self.env['res.partner']
+
+ for wizard_user in self.with_context(active_test=False).filtered(lambda w: w.in_portal and not w.partner_id.user_ids):
+ email = extract_email(wizard_user.email)
+ if not email:
+ partners_error_empty |= wizard_user.partner_id
+ elif email in emails:
+ partners_error_emails |= wizard_user.partner_id
+ user = self.env['res.users'].sudo().with_context(active_test=False).search([('login', '=', email)])
+ if user:
+ partners_error_user |= wizard_user.partner_id
+ emails.append(email)
error_msg = []
- if error_empty:
+ if partners_error_empty:
error_msg.append("%s\n- %s" % (_("Some contacts don't have a valid email: "),
- '\n- '.join(['%s' % (p.display_name,) for p in error_empty])))
- if error_emails:
+ '\n- '.join(partners_error_empty.mapped('display_name'))))
+ if partners_error_emails:
error_msg.append("%s\n- %s" % (_("Several contacts have the same email: "),
- '\n- '.join([p.email for p in error_emails])))
- if error_user:
+ '\n- '.join(partners_error_emails.mapped('email'))))
+ if partners_error_user:
error_msg.append("%s\n- %s" % (_("Some contacts have the same email as an existing portal user:"),
- '\n- '.join(['%s <%s>' % (p.display_name, p.email) for p in error_user])))
+ '\n- '.join(['%s <%s>' % (p.display_name, p.email) for p in partners_error_user])))
if error_msg:
error_msg.append(_("To resolve this error, you can: \n"
"- Correct the emails of the relevant contacts\n"
"- Grant access only to contacts with unique emails"))
return error_msg
- def action_apply(self, cr, uid, ids, context=None):
- error_msg = self.get_error_messages(cr, uid, ids, context=context)
+ @api.multi
+ def action_apply(self):
+ """ From selected partners, add corresponding users to chosen portal group. It either granted
+ existing user, or create new one (and add it to the group).
+ """
+ self.ensure_one()
+ error_msg = self.get_error_messages()
if error_msg:
- raise UserError( "\n\n".join(error_msg))
+ raise UserError("\n\n".join(error_msg))
- for wizard_user in self.browse(cr, SUPERUSER_ID, ids, dict(context, active_test=False)):
- portal = wizard_user.wizard_id.portal_id
- user = wizard_user.partner_id.user_ids and wizard_user.partner_id.user_ids[0] or False
+ for wizard_user in self.sudo().with_context(active_test=False):
+ group_portal = wizard_user.wizard_id.portal_id
+ user = wizard_user.partner_id.user_ids[0] if wizard_user.partner_id.user_ids else None
+ # update partner email, if a new one was introduced
if wizard_user.partner_id.email != wizard_user.email:
wizard_user.partner_id.write({'email': wizard_user.email})
+ # add portal group to relative user of selected partners
if wizard_user.in_portal:
- user_id = False
+ user_portal = None
# create a user if necessary, and make sure it is in the portal group
if not user:
- user_id = self._create_user(cr, SUPERUSER_ID, wizard_user.id, context)
+ user_portal = self.sudo()._create_user()
else:
- user_id = user.id
- wizard_user.write({'user_id': user_id})
- if (not wizard_user.user_id.active) or (portal not in wizard_user.user_id.groups_id):
- wizard_user.user_id.write({'active': True, 'groups_id': [(4, portal.id)]})
+ user_portal = user
+ wizard_user.write({'user_id': user_portal.id})
+ if not wizard_user.user_id.active or group_portal not in wizard_user.user_id.groups_id:
+ wizard_user.user_id.write({'active': True, 'groups_id': [(4, group_portal.id)]})
# prepare for the signup process
wizard_user.user_id.partner_id.signup_prepare()
- self._send_email(cr, uid, wizard_user.id, context)
+ wizard_user._send_email()
wizard_user.refresh()
else:
# remove the user (if it exists) from the portal group
- if user and (portal in user.groups_id):
+ if user and group_portal in user.groups_id:
# if user belongs to portal only, deactivate it
if len(user.groups_id) <= 1:
- user.write({'groups_id': [(3, portal.id)], 'active': False})
+ user.write({'groups_id': [(3, group_portal.id)], 'active': False})
else:
- user.write({'groups_id': [(3, portal.id)]})
+ user.write({'groups_id': [(3, group_portal.id)]})
- def _create_user(self, cr, uid, ids, context=None):
+ @api.multi
+ def _create_user(self):
""" create a new user for wizard_user.partner_id
- @param wizard_user: browse record of model portal.wizard.user
- @return: browse record of model res.users
+ :returns record of res.users
"""
- wizard_user = self.browse(cr, uid, ids, context=context)
- res_users = self.pool.get('res.users')
- create_context = dict(context or {}, noshortcut=True, no_reset_password=True) # to prevent shortcut creation
- values = {
- 'email': extract_email(wizard_user.email),
- 'login': extract_email(wizard_user.email),
- 'partner_id': wizard_user.partner_id.id,
+ return self.env['res.users'].with_context(no_reset_password=True).create({
+ 'email': extract_email(self.email),
+ 'login': extract_email(self.email),
+ 'partner_id': self.partner_id.id,
'groups_id': [(6, 0, [])],
- }
- return res_users.create(cr, uid, values, context=create_context)
+ })
- def _send_email(self, cr, uid, ids, context=None):
- """ send notification email to a new portal user
- @param wizard_user: browse record of model portal.wizard.user
- @return: the id of the created mail.mail record
- """
- wizard_user = self.browse(cr, uid, ids, context=context)
- res_partner = self.pool['res.partner']
- this_user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context)
- if not this_user.email:
+ @api.multi
+ def _send_email(self):
+ """ send notification email to a new portal user """
+ if not self.env.user.email:
raise UserError(_('You must have an email address in your User Preferences to send emails.'))
# determine subject and body in the portal user's language
- user = wizard_user.user_id
- context = dict(context or {}, lang=user.lang)
- ctx_portal_url = dict(context, signup_force_type_in_url='')
- portal_url = res_partner._get_signup_url_for_action(cr, uid,
- [user.partner_id.id],
- context=ctx_portal_url)[user.partner_id.id]
- res_partner.signup_prepare(cr, uid, [user.partner_id.id], context=context)
+ template = self.env.ref('portal.mail_template_data_portal_welcome')
+ for wizard_line in self:
+ lang = wizard_line.user_id.lang
+ partner = wizard_line.user_id.partner_id
+
+ portal_url = partner.with_context(signup_force_type_in_url='', lang=lang)._get_signup_url_for_action()[partner.id]
+ partner.signup_prepare()
+
+ if template:
+ template.with_context(dbname=self._cr.dbname, portal_url=portal_url, lang=lang).send_mail(wizard_line.id, force_send=True)
+ else:
+ _logger.warning("No email template found for sending email to the portal user")
- context.update({'dbname': cr.dbname, 'portal_url': portal_url})
- template_id = self.pool['ir.model.data'].xmlid_to_res_id(cr, uid, 'portal.mail_template_data_portal_welcome')
- if template_id:
- self.pool['mail.template'].send_mail(cr, uid, template_id, wizard_user.id, force_send=True, context=context)
- else:
- _logger.warning("No email template found for sending email to the portal user")
return True
diff --git a/addons/portal/wizard/portal_wizard_views.xml b/addons/portal/wizard/portal_wizard_views.xml
index 53e284108c8..b1cb31e63ba 100644
--- a/addons/portal/wizard/portal_wizard_views.xml
+++ b/addons/portal/wizard/portal_wizard_views.xml
@@ -1,6 +1,5 @@
-
-
+