# Part of Odoo. See LICENSE file for full copyright and licensing details. import operator from odoo import Command, api, fields, models, _ class ProjectShareWizard(models.TransientModel): _name = 'project.share.wizard' _inherit = 'portal.share' _description = 'Project Sharing' @api.model def default_get(self, fields): # The project share action could be called in `project.collaborator` # and so we have to check the active_model and active_id to use # the right project. active_model = self._context.get('active_model', '') active_id = self._context.get('active_id', False) if active_model == 'project.collaborator': active_model = 'project.project' active_id = self._context.get('default_project_id', False) result = super(ProjectShareWizard, self.with_context(active_model=active_model, active_id=active_id)).default_get(fields) if result['res_model'] and result['res_id']: project = self.env[result['res_model']].browse(result['res_id']) collaborator_vals_list = [] collaborator_ids = [] for collaborator in project.collaborator_ids: collaborator_ids.append(collaborator.partner_id.id) collaborator_vals_list.append({ 'partner_id': collaborator.partner_id.id, 'partner_name': collaborator.partner_id.display_name, 'access_mode': 'edit_limited' if collaborator.limited_access else 'edit', }) for follower in project.message_partner_ids: if follower.partner_share and follower.id not in collaborator_ids: collaborator_vals_list.append({ 'partner_id': follower.id, 'partner_name': follower.display_name, 'access_mode': 'read', }) if collaborator_vals_list: collaborator_vals_list.sort(key=operator.itemgetter('partner_name')) result['collaborator_ids'] = [ Command.create({'partner_id': collaborator['partner_id'], 'access_mode': collaborator['access_mode'], 'send_invitation': False}) for collaborator in collaborator_vals_list ] return result @api.model def _selection_target_model(self): project_model = self.env['ir.model']._get('project.project') return [(project_model.model, project_model.name)] share_link = fields.Char("Public Link", help="Anyone with this link can access the project in read mode.") collaborator_ids = fields.One2many('project.share.collaborator.wizard', 'parent_wizard_id', string='Collaborators') existing_partner_ids = fields.Many2many('res.partner', compute='_compute_existing_partner_ids', export_string_translation=False) @api.depends('res_model', 'res_id') def _compute_resource_ref(self): for wizard in self: if wizard.res_model and wizard.res_model == 'project.project': wizard.resource_ref = '%s,%s' % (wizard.res_model, wizard.res_id or 0) else: wizard.resource_ref = None @api.depends('collaborator_ids') def _compute_existing_partner_ids(self): for wizard in self: wizard.existing_partner_ids = wizard.collaborator_ids.partner_id @api.model_create_multi def create(self, vals_list): wizards = super().create(vals_list) for wizard in wizards: collaborator_ids_to_add = [] collaborator_ids_to_add_with_limited_access = [] collaborator_ids_vals_list = [] project = wizard.resource_ref project_collaborator_ids_to_remove = [ c.id for c in project.collaborator_ids if c.partner_id not in wizard.collaborator_ids.partner_id ] project_followers = project.message_partner_ids project_followers_to_add = [] project_followers_to_remove = [ partner.id for partner in project_followers if partner not in wizard.collaborator_ids.partner_id ] project_collaborator_per_partner_id = {c.partner_id.id: c for c in project.collaborator_ids} for collaborator in wizard.collaborator_ids: partner_id = collaborator.partner_id.id project_collaborator = project_collaborator_per_partner_id.get(partner_id, self.env['project.collaborator']) if collaborator.access_mode in ("edit", "edit_limited"): limited_access = collaborator.access_mode == "edit_limited" if not project_collaborator: if limited_access: collaborator_ids_to_add_with_limited_access.append(partner_id) else: collaborator_ids_to_add.append(partner_id) elif project_collaborator.limited_access != limited_access: collaborator_ids_vals_list.append( Command.update( project_collaborator.id, {'limited_access': limited_access}, ) ) elif project_collaborator: project_collaborator_ids_to_remove.append(project_collaborator.id) if partner_id not in project_followers.ids: project_followers_to_add.append(partner_id) if collaborator_ids_to_add: partners = project._get_new_collaborators(self.env['res.partner'].browse(collaborator_ids_to_add)) collaborator_ids_vals_list.extend(Command.create({'partner_id': partner_id}) for partner_id in partners.ids) if collaborator_ids_to_add_with_limited_access: partners = project._get_new_collaborators(self.env['res.partner'].browse(collaborator_ids_to_add_with_limited_access)) collaborator_ids_vals_list.extend( Command.create({'partner_id': partner_id, 'limited_access': True}) for partner_id in partners.ids ) if project_collaborator_ids_to_remove: collaborator_ids_vals_list.extend(Command.delete(collaborator_id) for collaborator_id in project_collaborator_ids_to_remove) project_vals = {} if collaborator_ids_vals_list: project_vals['collaborator_ids'] = collaborator_ids_vals_list if project_vals: project.write(project_vals) if project_followers_to_add: project._add_followers(self.env['res.partner'].browse(project_followers_to_add)) if project_followers_to_remove: project.message_unsubscribe(project_followers_to_remove) return wizards def action_share_record(self): # Confirmation dialog is only opened if new portal user(s) need to be created in a 'on invitation' website self.ensure_one() if not self.collaborator_ids: return on_invite = self.env['res.users']._get_signup_invitation_scope() == 'b2b' new_portal_user = self.collaborator_ids.filtered(lambda c: c.send_invitation and not c.partner_id.user_ids) and on_invite if not new_portal_user: return self.action_send_mail() return { 'name': _('Confirmation'), 'type': 'ir.actions.act_window', 'view_mode': 'form', 'views': [(self.env.ref('project.project_share_wizard_confirm_form').id, 'form')], 'res_model': 'project.share.wizard', 'res_id': self.id, 'target': 'new', 'context': self.env.context, } def action_send_mail(self): self.env['project.project'].browse(self.res_id).privacy_visibility = 'portal' result = { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'type': 'success', 'message': _("Project shared with your collaborators."), 'next': {'type': 'ir.actions.act_window_close'}, }} partner_ids_in_readonly_mode = [] partner_ids_in_edit_mode = [] for collaborator in self.collaborator_ids: if not collaborator.send_invitation: continue if collaborator.access_mode == 'read': partner_ids_in_readonly_mode.append(collaborator.partner_id.id) else: partner_ids_in_edit_mode.append(collaborator.partner_id.id) if partner_ids_in_edit_mode: new_collaborators = self.env['res.partner'].browse(partner_ids_in_edit_mode) portal_partners = new_collaborators.filtered('user_ids') # send mail to users self._send_public_link(portal_partners) self._send_signup_link(partners=new_collaborators.with_context({'signup_valid': True}) - portal_partners) if partner_ids_in_readonly_mode: self.partner_ids = self.env['res.partner'].browse(partner_ids_in_readonly_mode) super().action_send_mail() return result