diff --git a/addons/auth_signup/auth_signup_data.xml b/addons/auth_signup/auth_signup_data.xml index 4a6bf9ae4b9..39b62148494 100644 --- a/addons/auth_signup/auth_signup_data.xml +++ b/addons/auth_signup/auth_signup_data.xml @@ -13,7 +13,7 @@ - + auth_signup.template_user_id diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index a196f3d3f5f..135f4237f1c 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -25,7 +25,7 @@ 'version': '1.0', 'category': 'Customer Relationship Management', 'sequence': 2, - 'summary': 'Leads, Opportunities, Agenda', + 'summary': 'Leads, Opportunities, Phone Calls', 'description': """ The generic OpenERP Customer Relationship Management ===================================================== diff --git a/addons/portal/mail_mail.py b/addons/portal/mail_mail.py index 72c8469c042..a41c28f15e2 100644 --- a/addons/portal/mail_mail.py +++ b/addons/portal/mail_mail.py @@ -43,7 +43,7 @@ class mail_mail_portal(osv.Model): :param partner: browse_record of the specific recipient partner """ if partner: - portal_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'portal') + portal_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal') portal_id = portal_ref and portal_ref[1] or False url = self._generate_signin_url(cr, uid, partner.id, portal_id, 1234, context=context) body = tools.append_content_to_html(mail.body_html, url) diff --git a/addons/portal/portal.py b/addons/portal/portal.py index 3672d87cf30..13cde80f849 100644 --- a/addons/portal/portal.py +++ b/addons/portal/portal.py @@ -20,196 +20,14 @@ ############################################################################## from osv import osv, fields -from tools.translate import _ - class portal(osv.osv): + """ 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. """ - A portal is a group of users with specific menu, widgets, and typically - restricted access rights. - """ - _name = 'res.portal' - _description = 'Portal' - _inherits = {'res.groups': 'group_id'} - + _inherit = 'res.groups' _columns = { - 'group_id': fields.many2one('res.groups', required=True, ondelete='cascade', - string='Group', - help='The group corresponding to this portal'), - 'url': fields.char('URL', - help="The url where portal users can connect to the server"), - 'home_action_id': fields.many2one('ir.actions.actions', - string='Home Action', - help="if set, replaces the standard home action (first screen after loggin) for the portal's users"), - 'menu_action_id': fields.many2one('ir.actions.act_window', readonly=True, - # ISSUE: 'ondelete' constraints do not seem effective on this field... - string='Menu Action', - help="If set, replaces the standard menu for the portal's users"), - 'parent_menu_id': fields.many2one('ir.ui.menu', ondelete='restrict', - string='Parent Menu', - help='The menu action opens the submenus of this menu item'), - 'widget_ids': fields.one2many('res.portal.widget', 'portal_id', - string='Widgets', - help='Widgets assigned to portal users'), + 'is_portal': fields.boolean('Portal', help="If checked, this group is usable as a portal."), } - def copy(self, cr, uid, id, values, context=None): - """ override copy(): menu_action_id must be different """ - values['menu_action_id'] = None - return super(portal, self).copy(cr, uid, id, values, context) - - def create(self, cr, uid, values, context=None): - """ extend create() to assign the portal menu to users """ - if context is None: - context = {} - - # create portal (admin should not be included) - context['noadmin'] = True - portal_id = super(portal, self).create(cr, uid, values, context) - - # assign menu action and widgets to users - if values.get('users') or values.get('menu_action_id'): - self._assign_menu(cr, uid, [portal_id], context) - if values.get('users') or values.get('widget_ids'): - self._assign_widgets(cr, uid, [portal_id], context) - - return portal_id - - def write(self, cr, uid, ids, values, context=None): - """ extend write() to reflect changes on users """ - # first apply portal changes - super(portal, self).write(cr, uid, ids, values, context) - - # assign menu action and widgets to users - if values.get('users') or values.get('menu_action_id'): - self._assign_menu(cr, uid, ids, context) - if values.get('users') or values.get('widget_ids'): - self._assign_widgets(cr, uid, ids, context) - - # if parent_menu_id has changed, apply the change on menu_action_id - if 'parent_menu_id' in values: - act_window_obj = self.pool.get('ir.actions.act_window') - portals = self.browse(cr, uid, ids, context) - action_ids = [p.menu_action_id.id for p in portals if p.menu_action_id] - if action_ids: - action_values = {'domain': [('parent_id', '=', values['parent_menu_id'])]} - act_window_obj.write(cr, uid, action_ids, action_values, context) - - return True - - def _assign_menu(self, cr, uid, ids, context=None): - """ assign portal_menu_settings to users of portals (ids) """ - user_obj = self.pool.get('res.users') - for p in self.browse(cr, uid, ids, context): - # user menu action = portal menu action if set in portal - if p.menu_action_id: - user_ids = [u.id for u in p.users if u.id != 1] - user_values = {'menu_id': p.menu_action_id.id} - user_obj.write(cr, uid, user_ids, user_values, context) - - def _assign_widgets(self, cr, uid, ids, context=None): - """ assign portal widgets to users of portals (ids) """ - widget_user_obj = self.pool.get('res.widget.user') - for p in self.browse(cr, uid, ids, context): - for w in p.widget_ids: - values = {'sequence': w.sequence, 'widget_id': w.widget_id.id} - for u in p.users: - if u.id == 1: continue - values['user_id'] = u.id - widget_user_obj.create(cr, uid, values, context) - - def _res_xml_id(self, cr, uid, module, xml_id): - """ return the resource id associated to the given xml_id """ - data_obj = self.pool.get('ir.model.data') - data_id = data_obj._get_id(cr, uid, module, xml_id) - return data_obj.browse(cr, uid, data_id).res_id - -portal() - - - -class portal_override_menu(osv.osv): - """ - Extend res.portal with a boolean field 'Override Users Menu', that - triggers the creation or removal of menu_action_id - """ - _name = 'res.portal' - _inherit = 'res.portal' - - def _get_override_menu(self, cr, uid, ids, field_name, arg, context=None): - assert field_name == 'override_menu' - result = {} - for p in self.browse(cr, uid, ids, context): - result[p.id] = bool(p.menu_action_id) - return result - - def _set_override_menu(self, cr, uid, id, field_name, field_value, arg, context=None): - assert field_name == 'override_menu' - if field_value: - self.create_menu_action(cr, uid, id, context) - else: - self.write(cr, uid, [id], {'menu_action_id': False}, context) - - def create_menu_action(self, cr, uid, id, context=None): - """ create, if necessary, a menu action that opens the menu items below - parent_menu_id """ - p = self.browse(cr, uid, id, context) - if not p.menu_action_id: - actions_obj = self.pool.get('ir.actions.act_window') - parent_id = p.parent_menu_id.id if p.parent_menu_id else False - action_values = { - 'name': _('%s Menu') % p.name, - 'type': 'ir.actions.act_window', - 'usage': 'menu', - 'res_model': 'ir.ui.menu', - 'view_type': 'tree', - 'view_id': self._res_xml_id(cr, uid, 'base', 'view_menu'), - 'domain': [('parent_id', '=', parent_id)], - } - action_id = actions_obj.create(cr, uid, action_values, context) - self.write(cr, uid, [id], {'menu_action_id': action_id}, context) - - _columns = { - 'override_menu': fields.function( - _get_override_menu, fnct_inv=_set_override_menu, - type='boolean', string='Override Menu Action of Users', - help='Enable this option to override the Menu Action of portal users'), - } - -portal_override_menu() - - - -class portal_widget(osv.osv): - """ - Similar to res.widget.user (res_widget.py), but with a portal instead. - New users in a portal are assigned the portal's widgets. - """ - _name='res.portal.widget' - _description = 'Portal Widgets' - _order = 'sequence' - _columns = { - 'sequence': fields.integer('Sequence'), - 'portal_id': fields.many2one('res.portal', select=1, ondelete='cascade', - string='Portal'), - 'widget_id': fields.many2one('res.widget', required=True, ondelete='cascade', - string='Widget'), - } - - def create(self, cr, uid, values, context=None): - domain = [('portal_id', '=', values.get('portal_id')), - ('widget_id', '=', values.get('widget_id'))] - existing = self.search(cr, uid, domain, context=context) - if existing: - res = existing[0] - else: - res = super(portal_widget, self).create(cr, uid, values, context=context) - return res - -portal_widget() - - - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/portal/portal_data.xml b/addons/portal/portal_data.xml index eb06ca656d6..a980552a854 100644 --- a/addons/portal/portal_data.xml +++ b/addons/portal/portal_data.xml @@ -2,14 +2,6 @@ - - Portal - - - - - - Company's news diff --git a/addons/portal/portal_demo.xml b/addons/portal/portal_demo.xml index 1ace02f3d7d..f4ba8fe2192 100644 --- a/addons/portal/portal_demo.xml +++ b/addons/portal/portal_demo.xml @@ -13,7 +13,7 @@ - + diff --git a/addons/portal/portal_view.xml b/addons/portal/portal_view.xml index fd15a842405..16e8a86b27e 100644 --- a/addons/portal/portal_view.xml +++ b/addons/portal/portal_view.xml @@ -5,7 +5,7 @@ @@ -21,71 +21,31 @@ - - - Portal List - res.portal + + + Group Search + res.groups + - - - + + + + + - - + + Portal Form - res.portal + res.groups - - - - - - - - - - - - - - - - - - - - - + + + - - - Portals - res.portal - form - tree,form - - {'form_view_ref': 'portal.portal_form_view'} - -

- Click to create a new portal. -

- A portal helps defining specific views and rules for a group of - users (the portal group). A portal menu, widgets and specific - groups may be assigned to the portal's users. -

-
-
- - - - -
diff --git a/addons/portal/security/ir.model.access.csv b/addons/portal/security/ir.model.access.csv index 56b3ae5f269..2d7a17e0016 100644 --- a/addons/portal/security/ir.model.access.csv +++ b/addons/portal/security/ir.model.access.csv @@ -1,12 +1,8 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_all,access.portal.all,model_res_portal,,1,0,0,0 -access_widget_all,access.portal.widget.all,model_res_portal_widget,,1,0,0,0 -access_manager,access.portal.manager,model_res_portal,group_portal_manager,1,1,1,1 -access_widget_manager,access.portal.widget.manager,model_res_portal_widget,group_portal_manager,1,1,1,1 -access_mail_message,mail.message,mail.model_mail_message,group_portal_member,1,0,1,1 -access_mail_message_all,mail.message.all,mail.model_mail_message,group_portal_member,1,0,0,0 -access_mail_thread,mail.thread,mail.model_mail_thread,group_portal_member,1,0,0,0 -access_mail_followers,mail.followers,mail.model_mail_followers,group_portal_member,1,0,1,1 -access_mail_notification,mail.notification,mail.model_mail_notification,group_portal_member,1,0,1,0 -access_mail_group,mail.group,mail.model_mail_group,group_portal_member,1,0,0,0 -access_mail_alias,mail.alias,mail.model_mail_alias,group_portal_member,1,0,0,0 +access_mail_message,mail.message,mail.model_mail_message,group_portal,1,0,1,1 +access_mail_message_all,mail.message.all,mail.model_mail_message,group_portal,1,0,0,0 +access_mail_thread,mail.thread,mail.model_mail_thread,group_portal,1,0,0,0 +access_mail_followers,mail.followers,mail.model_mail_followers,group_portal,1,0,1,1 +access_mail_notification,mail.notification,mail.model_mail_notification,group_portal,1,0,1,0 +access_mail_group,mail.group,mail.model_mail_group,group_portal,1,0,0,0 +access_mail_alias,mail.alias,mail.model_mail_alias,group_portal,1,0,0,0 diff --git a/addons/portal/security/portal_security.xml b/addons/portal/security/portal_security.xml index 4a493204d67..ee0af785599 100644 --- a/addons/portal/security/portal_security.xml +++ b/addons/portal/security/portal_security.xml @@ -2,31 +2,14 @@ - - - Portal Member - Portal members can access information through the portal menu. Also, they are chrooted in this specific menu. - - - + + Portal - - 25 - - - - - Officer - Portal officers can create new portal users with the portal wizard. - - - - - Manager - Portal managers have access to the portal definitions, and can easily configure the users, access rights and menus of portal users. - - - + Portal members have specific access rights (such as record rules and restricted menus). + They usually do not belong to the usual OpenERP groups. + + + diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py index 2c301228859..5e992fa06ea 100644 --- a/addons/portal/tests/test_portal.py +++ b/addons/portal/tests/test_portal.py @@ -42,7 +42,7 @@ class test_portal(test_mail.TestMailMockups): user_admin = self.res_users.browse(cr, uid, uid) self.mail_invite = self.registry('mail.wizard.invite') base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='') - portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'portal') + portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal') portal_id = portal_ref and portal_ref[1] or False # 0 - Admin diff --git a/addons/portal/wizard/portal_wizard.py b/addons/portal/wizard/portal_wizard.py index 85f6566eba6..3cc706c2129 100644 --- a/addons/portal/wizard/portal_wizard.py +++ b/addons/portal/wizard/portal_wizard.py @@ -23,33 +23,43 @@ import logging import random from osv import osv, fields -from tools.misc import email_re from tools.translate import _ +from tools.misc import email_re +from openerp import SUPERUSER_ID from base.res.res_partner import _lang_get _logger = logging.getLogger(__name__) - -# welcome email sent to new portal users (note that calling tools.translate._ -# has no effect except exporting those strings for translation) +# welcome/goodbye email sent to portal users +# (note that calling '_' has no effect except exporting those strings for translation) WELCOME_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s") WELCOME_EMAIL_BODY = _("""Dear %(name)s, -You have been created an OpenERP account at %(url)s. +You have been given access to %(portal)s at %(url)s. Your login account data is: Database: %(db)s User: %(login)s Password: %(password)s -%(message)s +%(welcome_message)s -- OpenERP - Open Source Business Applications http://www.openerp.com """) -ROOT_UID = 1 +GOODBYE_EMAIL_SUBJECT = _("Your OpenERP account at %(company)s") +GOODBYE_EMAIL_BODY = _("""Dear %(name)s, + +Your access to %(portal)s has been withdrawn. + +%(goodbye_message)s + +-- +OpenERP - Open Source Business Applications +http://www.openerp.com +""") # character sets for passwords, excluding 0, O, o, 1, I, l _PASSU = 'ABCDEFGHIJKLMNPQRSTUVWXYZ' @@ -71,169 +81,173 @@ def extract_email(email): class wizard(osv.osv_memory): """ - A wizard to create portal users from instances of 'res.partner'. The purpose - is to provide an OpenERP database access to customers or suppliers. + A wizard to manage the creation/removal of portal users. """ - _name = 'res.portal.wizard' - _description = 'Portal Wizard' - + _name = 'portal.wizard' + _description = 'Portal Access Management' + _columns = { - 'portal_id': fields.many2one('res.portal', required=True, - string='Portal', - help="The portal in which new users must be added"), - 'user_ids': fields.one2many('res.portal.wizard.user', 'wizard_id', - string='Users'), - 'message': fields.text(string='Invitation message', - help="This text is included in the welcome email sent to the users"), + '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."), + 'goodbye_message': fields.text(string='Withdrawal Message', + help="This text is included in the email sent to users withdrawn from the portal."), } - def _default_user_ids(self, cr, uid, context): - """ determine default user_ids from the active records """ - def create_user_from_address(address): - if isinstance(address, int): - res_partner_obj = self.pool.get('res.partner') - address = res_partner_obj.browse(cr, uid, address, context=context) - lang = address.id and address.lang or 'en_US' - partner_id = address.id - - else: - lang = address.parent_id and address.parent_id.lang or 'en_US' - partner_id = address.parent_id and address.parent_id.id - - return{ - 'name': address.name, - 'email': extract_email(address.email), - 'lang': lang, - 'partner_id': partner_id, - } - - user_ids = [] - if context.get('active_model') == 'res.partner': - partner_obj = self.pool.get('res.partner') - partner_ids = context.get('active_ids', []) - partners = partner_obj.browse(cr, uid, partner_ids, context) - for p in partners: - # add one user per contact, or one user if no contact - if p.child_ids: - user_ids.extend(map(create_user_from_address, p.child_ids)) - elif p.is_company == False and p.customer == True: - user_ids.extend(map(create_user_from_address, [p.id])) - else: - user_ids.append({'lang': p.lang or 'en_US', 'parent_id': p.id}) - - return user_ids + 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 _defaults = { - 'user_ids': _default_user_ids + 'portal_id': _default_portal, } - def action_create(self, cr, uid, ids, context=None): - """ create new users in portal(s), and notify them by email """ - # we copy the context to change the language for translating emails - context0 = context or {} - context0['noshortcut'] = True # prevent shortcut creation - context = context0.copy() - - user_obj = self.pool.get('res.users') - user = user_obj.browse(cr, ROOT_UID, uid, context0) - if not user.email: - raise osv.except_osv(_('Email required'), - _('You must have an email address in your User Preferences' - ' to send emails.')) - - portal_obj = self.pool.get('res.portal') - for wiz in self.browse(cr, uid, ids, context): - # determine existing users - login_cond = [('login', 'in', [u.email for u in wiz.user_ids])] - existing_uids = user_obj.search(cr, ROOT_UID, login_cond) - existing_users = user_obj.browse(cr, ROOT_UID, existing_uids) - existing_logins = [u.login for u in existing_users] - - # create new users in portal (skip existing logins) - new_users_data = [ { - 'name': u.name, - 'login': u.email, - 'password': random_password(), - 'email': u.email, - 'lang': u.lang, - 'share': True, - 'action_id': wiz.portal_id.home_action_id and wiz.portal_id.home_action_id.id or False, - 'partner_id': u.partner_id and u.partner_id.id, - 'groups_id': [(6, 0, [])], - } for u in wiz.user_ids if u.email not in existing_logins ] - portal_obj.write(cr, ROOT_UID, [wiz.portal_id.id], - {'users': [(0, 0, data) for data in new_users_data]}, context0) - - # send email to all users (translated in their language) - data = { - 'company': user.company_id.name, - 'message': wiz.message or "", - 'url': wiz.portal_id.url or _("(missing url)"), - 'db': cr.dbname, - } - mail_mail_obj = self.pool.get('mail.mail') - dest_uids = user_obj.search(cr, ROOT_UID, login_cond) - dest_users = user_obj.browse(cr, ROOT_UID, dest_uids) - for dest_user in dest_users: - context['lang'] = dest_user.lang - data['login'] = dest_user.login - data['password'] = dest_user.password - data['name'] = dest_user.name - - email_from = user.email - email_to = dest_user.email - subject = _(WELCOME_EMAIL_SUBJECT) % data - body = _(WELCOME_EMAIL_BODY) % data - mail_id = mail_mail_obj.create(cr, uid, { - 'email_from': email_from , - 'email_to': email_to, - 'subject': subject, - 'state': 'outgoing', - 'body_html': '
%s
' % body}, context=context) - + def onchange_portal_id(self, cr, uid, ids, portal_id, context=None): + # 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 [] + 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]): + # 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] + user_changes.append((0, 0, { + 'partner_id': contact.id, + 'email': contact.email, + 'in_portal': in_portal, + })) + return {'value': {'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) return {'type': 'ir.actions.act_window_close'} -wizard() - - - class wizard_user(osv.osv_memory): """ A model to configure users in the portal wizard. """ - _name = 'res.portal.wizard.user' + _name = 'portal.wizard.user' _description = 'Portal User Config' _columns = { - 'wizard_id': fields.many2one('res.portal.wizard', required=True, - string='Wizard'), - 'name': fields.char(size=64, required=True, - string='User Name', - help="The user's real name"), - 'email': fields.char(size=64, required=True, - string='Email', - help="Will be used as user login. " - "Also necessary to send the account information to new users"), - 'lang': fields.selection(_lang_get, required=True, - string='Language', - help="The language for the user's user interface"), - 'partner_id': fields.many2one('res.partner', - string='Partner'), + 'wizard_id': fields.many2one('portal.wizard', string='Wizard', required=True), + 'partner_id': fields.many2one('res.partner', string='Contact', required=True, readonly=True), + 'email': fields.char(size=240, string='Email'), + 'in_portal': fields.boolean('In Portal'), } - def _check_email(self, cr, uid, ids): - """ check syntax of email address """ - for wuser in self.browse(cr, uid, ids): - if not email_re.match(wuser.email): return False - return True + def create(self, cr, uid, values, context=None): + """ overridden to update the partner's email (if necessary) """ + id = super(wizard_user, self).create(cr, uid, values, context) + wuser = self.browse(cr, uid, id, context) + if wuser.partner_id.email != wuser.email: + wuser.partner_id.write({'email': wuser.email}) + return id - _constraints = [ - (_check_email, 'Invalid email address', ['email']), - ] + def action_apply(self, cr, uid, ids, context=None): + res_users = self.pool.get('res.users') + for wizard_user in self.browse(cr, SUPERUSER_ID, ids, context): + portal = wizard_user.wizard_id.portal_id + user = self._retrieve_user(cr, SUPERUSER_ID, wizard_user, context) + if wizard_user.in_portal: + # create a user if necessary, and make sure it is in the portal group + if not user: + user = self._create_user(cr, SUPERUSER_ID, wizard_user, context) + if (not user.active) or (portal not in user.groups_id): + user.write({'active': True, 'groups_id': [(4, portal.id)]}) + wizard_user = self.browse(cr, SUPERUSER_ID, wizard_user.id, context) + self._send_email(cr, uid, wizard_user, context) + else: + # remove the user (if it exists) from the portal group + if user and (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}) + else: + user.write({'groups_id': [(3, portal.id)]}) + wizard_user = self.browse(cr, SUPERUSER_ID, wizard_user.id, context) + self._send_email(cr, uid, wizard_user, context) -wizard_user() + def _retrieve_user(self, cr, uid, wizard_user, context=None): + """ retrieve the (possibly inactive) user corresponding to wizard_user.partner_id + @param wizard_user: browse record of model portal.wizard.user + @return: browse record of model res.users + """ + if wizard_user.partner_id.user_ids: + return wizard_user.partner_id.user_ids[0] + # the user may be inactive, search for it + res_users = self.pool.get('res.users') + domain = [('partner_id', '=', wizard_user.partner_id.id), ('active', '=', False)] + user_ids = res_users.search(cr, uid, domain) + return user_ids and res_users.browse(cr, uid, user_ids[0], context) or False + def _create_user(self, cr, uid, wizard_user, context=None): + """ 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 + """ + res_users = self.pool.get('res.users') + create_context = dict(context or {}, noshortcut=True) # to prevent shortcut creation + values = { + 'login': extract_email(wizard_user.email), + 'password': random_password(), + 'partner_id': wizard_user.partner_id.id, + 'groups_id': [(6, 0, [])], + 'share': True, + } + user_id = res_users.create(cr, uid, values, context=create_context) + return res_users.browse(cr, uid, user_id, context) + def _send_email(self, cr, uid, wizard_user, context=None): + """ send notification email to a new/former portal user + @param wizard_user: browse record of model portal.wizard.user + @return: the id of the created mail.mail record + """ + this_context = context + this_user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context) + if not this_user.email: + raise osv.except_osv(_('Email required'), + _('You must have an email address in your User Preferences to send emails.')) + # determine subject and body in the portal user's language + url = self.pool.get('ir.config_parameter').get_param(cr, SUPERUSER_ID, 'web.base.url', context=this_context) + user = self._retrieve_user(cr, SUPERUSER_ID, wizard_user, context) + context = dict(this_context or {}, lang=user.lang) + data = { + 'company': this_user.company_id.name, + 'portal': wizard_user.wizard_id.portal_id.name, + 'welcome_message': wizard_user.wizard_id.welcome_message or "", + 'goodbye_message': wizard_user.wizard_id.goodbye_message or "", + 'url': url or _("(missing url)"), + 'db': cr.dbname, + 'login': user.login, + 'password': user.password, + 'name': user.name + } + if wizard_user.in_portal: + subject = _(WELCOME_EMAIL_SUBJECT) % data + body = _(WELCOME_EMAIL_BODY) % data + else: + subject = _(GOODBYE_EMAIL_SUBJECT) % data + body = _(GOODBYE_EMAIL_BODY) % data + + mail_mail = self.pool.get('mail.mail') + mail_values = { + 'email_from': this_user.email, + 'email_to': user.email, + 'subject': subject, + 'body_html': '
%s
' % body, + 'state': 'outgoing', + } + return mail_mail.create(cr, uid, mail_values, context=this_context) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/portal/wizard/portal_wizard_view.xml b/addons/portal/wizard/portal_wizard_view.xml index f3cd5c98bb4..c8e3f30b128 100644 --- a/addons/portal/wizard/portal_wizard_view.xml +++ b/addons/portal/wizard/portal_wizard_view.xml @@ -3,28 +3,34 @@ + groups="base.group_partner_manager"/> - Add Portal Access - res.portal.wizard + Portal Access Management + portal.wizard -
- - + + + +
+ Select which contacts should belong to the portal in the list below. + The email address of each selected contact must be valid and unique. + If necessary, you can fix any contact's email address directly in the list. +
-
diff --git a/addons/portal/wizard/share_wizard.py b/addons/portal/wizard/share_wizard.py index ca950a61b08..b1b6e980dec 100644 --- a/addons/portal/wizard/share_wizard.py +++ b/addons/portal/wizard/share_wizard.py @@ -44,9 +44,6 @@ class share_wizard_portal(osv.TransientModel): 'group_ids': fields.many2many('res.groups', 'share_wizard_res_group_rel', 'share_id', 'group_id', 'Existing groups', domain=[('share', '=', False)]), } - def is_portal_manager(self, cr, uid, context=None): - return self.has_group(cr, uid, module='portal', group_xml_id='group_portal_manager', context=context) - def _check_preconditions(self, cr, uid, wizard_data, context=None): if wizard_data.user_type == 'existing': self._assert(wizard_data.user_ids, @@ -122,16 +119,14 @@ class share_wizard_portal(osv.TransientModel): # alter the rules of the groups so they can see the shared data if wizard_data.group_ids: # get the list of portals and the related groups to install their menus. - Portals = self.pool.get('res.portal') - all_portals = Portals.browse(cr, UID_ROOT, Portals.search(cr, UID_ROOT, [])) #no context! - all_portal_group_ids = [p.group_id.id for p in all_portals] + res_groups = self.pool.get('res.groups') + all_portal_group_ids = res_groups.search(cr, UID_ROOT, [('is_portal', '=', True)]) # populate result lines with the users of each group and # setup the menu for portal groups for group in wizard_data.group_ids: if group.id in all_portal_group_ids: - portal = all_portals[all_portal_group_ids.index(group.id)] - self._create_shared_data_menu(cr, uid, wizard_data, portal, context=context) + self._create_shared_data_menu(cr, uid, wizard_data, group.id, context=context) for user in group.users: new_line = {'user_id': user.id, diff --git a/addons/portal/wizard/share_wizard_view.xml b/addons/portal/wizard/share_wizard_view.xml index 72ae857eaaf..065a7aa317e 100644 --- a/addons/portal/wizard/share_wizard_view.xml +++ b/addons/portal/wizard/share_wizard_view.xml @@ -36,12 +36,12 @@ - + - - + + diff --git a/addons/portal_claim/security/ir.model.access.csv b/addons/portal_claim/security/ir.model.access.csv index 554c66cc998..ab2a2f66770 100644 --- a/addons/portal_claim/security/ir.model.access.csv +++ b/addons/portal_claim/security/ir.model.access.csv @@ -1,3 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_crm_claim,crm.claim,crm_claim.model_crm_claim,portal.group_portal_member,1,0,0,0 -access_crm_claim_stage,crm.claim.stage,crm_claim.model_crm_claim_stage,portal.group_portal_member,1,0,0,0 +access_crm_claim,crm.claim,crm_claim.model_crm_claim,portal.group_portal,1,0,0,0 +access_crm_claim_stage,crm.claim.stage,crm_claim.model_crm_claim_stage,portal.group_portal,1,0,0,0 diff --git a/addons/portal_claim/security/portal_security.xml b/addons/portal_claim/security/portal_security.xml index e902bd043ad..9fc47c4cfb0 100644 --- a/addons/portal_claim/security/portal_security.xml +++ b/addons/portal_claim/security/portal_security.xml @@ -6,7 +6,7 @@ Portal Personal Claims [('partner_id','child_of',user.partner_id.id)] - +
diff --git a/addons/portal_event/security/ir.model.access.csv b/addons/portal_event/security/ir.model.access.csv index b418acbb9c4..7418b08bb4b 100644 --- a/addons/portal_event/security/ir.model.access.csv +++ b/addons/portal_event/security/ir.model.access.csv @@ -1,3 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_event,event,event.model_event_event,portal.group_portal_member,1,0,0,0 -access_registration,registration,event.model_event_registration,portal.group_portal_member,1,0,0,0 +access_event,event,event.model_event_event,portal.group_portal,1,0,0,0 +access_registration,registration,event.model_event_registration,portal.group_portal,1,0,0,0 diff --git a/addons/portal_event/security/portal_security.xml b/addons/portal_event/security/portal_security.xml index 6a3ed0ac786..74b2372c147 100644 --- a/addons/portal_event/security/portal_security.xml +++ b/addons/portal_event/security/portal_security.xml @@ -6,14 +6,14 @@ Portal Visible Events [('visibility', '=', 'public')] - + Portal Personal Registrations [('partner_id','child_of',user.partner_id.id)] - + diff --git a/addons/portal_hr_employees/security/ir.model.access.csv b/addons/portal_hr_employees/security/ir.model.access.csv index 6131f49d749..d18aeb90e1c 100644 --- a/addons/portal_hr_employees/security/ir.model.access.csv +++ b/addons/portal_hr_employees/security/ir.model.access.csv @@ -1,6 +1,6 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_hr_employee_user,hr.employee user,hr.model_hr_employee,portal.group_portal_member,1,0,0,0 -access_res_partner,res.partner,base.model_res_partner,portal.group_portal_member,1,0,0,0 -access_res_partner_address,res.partner_address,base.model_res_partner_address,portal.group_portal_member,1,0,0,0 -access_res_partner_category,res.partner_category,base.model_res_partner_category,portal.group_portal_member,1,0,0,0 -access_res_partner_title,res.partner_title,base.model_res_partner_title,portal.group_portal_member,1,0,0,0 +access_hr_employee_user,hr.employee user,hr.model_hr_employee,portal.group_portal,1,0,0,0 +access_res_partner,res.partner,base.model_res_partner,portal.group_portal,1,0,0,0 +access_res_partner_address,res.partner_address,base.model_res_partner_address,portal.group_portal,1,0,0,0 +access_res_partner_category,res.partner_category,base.model_res_partner_category,portal.group_portal,1,0,0,0 +access_res_partner_title,res.partner_title,base.model_res_partner_title,portal.group_portal,1,0,0,0 diff --git a/addons/portal_project/security/ir.model.access.csv b/addons/portal_project/security/ir.model.access.csv index dceca5ba8bb..85463868ee1 100644 --- a/addons/portal_project/security/ir.model.access.csv +++ b/addons/portal_project/security/ir.model.access.csv @@ -1,5 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_task,tasks,project.model_project_task,portal.group_portal_member,1,0,0,0 -access_task_type,task_type,project.model_project_task_type,portal.group_portal_member,1,0,0,0 -access_task_work,task_work,project.model_project_task_work,portal.group_portal_member,1,0,0,0 -access_project_category,project_category,project.model_project_category,portal.group_portal_member,1,0,0,0 +access_task,tasks,project.model_project_task,portal.group_portal,1,0,0,0 +access_task_type,task_type,project.model_project_task_type,portal.group_portal,1,0,0,0 +access_task_work,task_work,project.model_project_task_work,portal.group_portal,1,0,0,0 +access_project_category,project_category,project.model_project_category,portal.group_portal,1,0,0,0 diff --git a/addons/portal_project/security/portal_security.xml b/addons/portal_project/security/portal_security.xml index b995c79bbbc..b9d37cec099 100644 --- a/addons/portal_project/security/portal_security.xml +++ b/addons/portal_project/security/portal_security.xml @@ -6,7 +6,7 @@ Portal Personal Task [('partner_id','child_of',user.partner_id.id)] - + diff --git a/addons/portal_project_issue/security/ir.model.access.csv b/addons/portal_project_issue/security/ir.model.access.csv index 5257f9b6d02..80a38366b51 100644 --- a/addons/portal_project_issue/security/ir.model.access.csv +++ b/addons/portal_project_issue/security/ir.model.access.csv @@ -1,3 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_issues,project_issue,project_issue.model_project_issue,portal.group_portal_member,1,0,0,0 -access_case_section,crm_case_section,crm.model_crm_case_section,portal.group_portal_member,1,0,0,0 +access_issues,project_issue,project_issue.model_project_issue,portal.group_portal,1,0,0,0 +access_case_section,crm_case_section,crm.model_crm_case_section,portal.group_portal,1,0,0,0 diff --git a/addons/portal_project_issue/security/portal_security.xml b/addons/portal_project_issue/security/portal_security.xml index 4ef8da5e4b6..358b5e6a0ad 100644 --- a/addons/portal_project_issue/security/portal_security.xml +++ b/addons/portal_project_issue/security/portal_security.xml @@ -6,7 +6,7 @@ Portal Personal Issues [('partner_id','child_of',user.partner_id.id)] - + diff --git a/addons/portal_sale/security/ir.model.access.csv b/addons/portal_sale/security/ir.model.access.csv index 32266499af7..751c0b9baff 100644 --- a/addons/portal_sale/security/ir.model.access.csv +++ b/addons/portal_sale/security/ir.model.access.csv @@ -1,33 +1,33 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_sale_order,sale.order,sale.model_sale_order,portal.group_portal_member,1,0,0,0 -access_sale_order_line,sale.order.line,sale.model_sale_order_line,portal.group_portal_member,1,0,0,0 -access_stock_picking,stock.picking,stock.model_stock_picking,portal.group_portal_member,1,0,0,0 -access_stock_picking.out,stock.picking.out,stock.model_stock_picking_out,portal.group_portal_member,1,0,0,0 -access_stock_move,stock.move,stock.model_stock_move,portal.group_portal_member,1,0,0,0 -access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,procurement.model_stock_warehouse_orderpoint,portal.group_portal_member,1,0,0,0 -access_account_invoice,account.invoice,account.model_account_invoice,portal.group_portal_member,1,0,0,0 -access_account_invoice_tax,account.invoice.tax,account.model_account_invoice_tax,portal.group_portal_member,1,0,0,0 -access_account_invoice_line,account.invoice.line,account.model_account_invoice_line,portal.group_portal_member,1,0,0,0 -access_account_journal,account.journal,account.model_account_journal,portal.group_portal_member,1,0,0,0 -access_account_voucher,account.voucher,account_voucher.model_account_voucher,portal.group_portal_member,1,0,0,0 -access_account_voucher_line,account.voucher.line,account_voucher.model_account_voucher_line,portal.group_portal_member,1,0,0,0 -access_account_move,account.move,account.model_account_move,portal.group_portal_member,1,0,0,0 -access_account_move_line,account.move.line,account.model_account_move_line,portal.group_portal_member,1,0,0,0 -access_account_move_reconcile,account.move.reconcile,account.model_account_move_reconcile,portal.group_portal_member,1,0,0,0 -access_account_fiscalyear,account.sequence.fiscalyear,account.model_account_sequence_fiscalyear,portal.group_portal_member,1,0,0,0 -access_sale_shop,sale.shop,sale.model_sale_shop,portal.group_portal_member,1,0,0,0 -access_product_list,product.pricelist,product.model_product_pricelist,portal.group_portal_member,1,0,0,0 -access_product,product.product,product.model_product_product,portal.group_portal_member,1,0,0,0 -access_res_partner,res.partner,base.model_res_partner,portal.group_portal_member,1,0,0,0 -access_product_uom,product.uom,product.model_product_uom,portal.group_portal_member,1,0,0,0 -access_account_tax,account.tax,account.model_account_tax,portal.group_portal_member,1,0,0,0 -access_mrp_property,mrp.property,procurement.model_mrp_property,portal.group_portal_member,1,0,0,0 -access_product_template,product.template,product.model_product_template,portal.group_portal_member,1,0,0,0 -access_stock_warehouse,stock.warehouse,stock.model_stock_warehouse,portal.group_portal_member,1,0,0,0 -access_stock_location,stock.location,stock.model_stock_location,portal.group_portal_member,1,0,0,0 -access_account_fiscalyear,account.fiscalyear,account.model_account_fiscalyear,portal.group_portal_member,1,0,0,0 -access_res_partner_category,res.partner.category,base.model_res_partner_category,portal.group_portal_member,1,0,0,0 -access_product_supplierinfo,product.supplierinfo,product.model_product_supplierinfo,portal.group_portal_member,1,0,0,0 -access_product_packaging,product.packaging,product.model_product_packaging,portal.group_portal_member,1,0,0,0 -access_account_period,account.period,account.model_account_period,portal.group_portal_member,1,0,0,0 -access_account_account,account.account,account.model_account_account,portal.group_portal_member,1,0,0,0 +access_sale_order,sale.order,sale.model_sale_order,portal.group_portal,1,0,0,0 +access_sale_order_line,sale.order.line,sale.model_sale_order_line,portal.group_portal,1,0,0,0 +access_stock_picking,stock.picking,stock.model_stock_picking,portal.group_portal,1,0,0,0 +access_stock_picking.out,stock.picking.out,stock.model_stock_picking_out,portal.group_portal,1,0,0,0 +access_stock_move,stock.move,stock.model_stock_move,portal.group_portal,1,0,0,0 +access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,procurement.model_stock_warehouse_orderpoint,portal.group_portal,1,0,0,0 +access_account_invoice,account.invoice,account.model_account_invoice,portal.group_portal,1,0,0,0 +access_account_invoice_tax,account.invoice.tax,account.model_account_invoice_tax,portal.group_portal,1,0,0,0 +access_account_invoice_line,account.invoice.line,account.model_account_invoice_line,portal.group_portal,1,0,0,0 +access_account_journal,account.journal,account.model_account_journal,portal.group_portal,1,0,0,0 +access_account_voucher,account.voucher,account_voucher.model_account_voucher,portal.group_portal,1,0,0,0 +access_account_voucher_line,account.voucher.line,account_voucher.model_account_voucher_line,portal.group_portal,1,0,0,0 +access_account_move,account.move,account.model_account_move,portal.group_portal,1,0,0,0 +access_account_move_line,account.move.line,account.model_account_move_line,portal.group_portal,1,0,0,0 +access_account_move_reconcile,account.move.reconcile,account.model_account_move_reconcile,portal.group_portal,1,0,0,0 +access_account_fiscalyear,account.sequence.fiscalyear,account.model_account_sequence_fiscalyear,portal.group_portal,1,0,0,0 +access_sale_shop,sale.shop,sale.model_sale_shop,portal.group_portal,1,0,0,0 +access_product_list,product.pricelist,product.model_product_pricelist,portal.group_portal,1,0,0,0 +access_product,product.product,product.model_product_product,portal.group_portal,1,0,0,0 +access_res_partner,res.partner,base.model_res_partner,portal.group_portal,1,0,0,0 +access_product_uom,product.uom,product.model_product_uom,portal.group_portal,1,0,0,0 +access_account_tax,account.tax,account.model_account_tax,portal.group_portal,1,0,0,0 +access_mrp_property,mrp.property,procurement.model_mrp_property,portal.group_portal,1,0,0,0 +access_product_template,product.template,product.model_product_template,portal.group_portal,1,0,0,0 +access_stock_warehouse,stock.warehouse,stock.model_stock_warehouse,portal.group_portal,1,0,0,0 +access_stock_location,stock.location,stock.model_stock_location,portal.group_portal,1,0,0,0 +access_account_fiscalyear,account.fiscalyear,account.model_account_fiscalyear,portal.group_portal,1,0,0,0 +access_res_partner_category,res.partner.category,base.model_res_partner_category,portal.group_portal,1,0,0,0 +access_product_supplierinfo,product.supplierinfo,product.model_product_supplierinfo,portal.group_portal,1,0,0,0 +access_product_packaging,product.packaging,product.model_product_packaging,portal.group_portal,1,0,0,0 +access_account_period,account.period,account.model_account_period,portal.group_portal,1,0,0,0 +access_account_account,account.account,account.model_account_account,portal.group_portal,1,0,0,0 diff --git a/addons/portal_sale/security/portal_security.xml b/addons/portal_sale/security/portal_security.xml index f69fd283db6..8028d5360c4 100644 --- a/addons/portal_sale/security/portal_security.xml +++ b/addons/portal_sale/security/portal_security.xml @@ -7,42 +7,42 @@ Portal Personal Quotations/Sales Orders [('partner_id','child_of',user.partner_id.id)] - + Portal Personal Delivery Orders [('partner_id','child_of',[user.partner_id.id])] - + Portal Personal Delivery Orders Out [('partner_id','child_of',[user.partner_id.id])] - + Portal Personal Account Invoices [('partner_id','child_of',user.partner_id.id)] - + Portal Personal Payments [('partner_id','child_of',user.partner_id.id)] - + Portal Personal Contacts [('id','child_of',user.partner_id.id)] - + diff --git a/addons/share/res_users_view.xml b/addons/share/res_users_view.xml index 9a657ffdbb6..cd9f7ad4f4c 100644 --- a/addons/share/res_users_view.xml +++ b/addons/share/res_users_view.xml @@ -25,13 +25,13 @@ res.groups.search.share res.groups - + - - - - - + + + + +