| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- # -*- coding: utf-8 -*-
- # Part of Odoo. See LICENSE file for full copyright and licensing details.
- import operator
- from odoo import Command, api, fields, models, _
- from odoo.exceptions import ValidationError
- class HelpdeskTeamShareWizard(models.TransientModel):
- _name = 'helpdesk.team.share.wizard'
- _inherit = 'portal.share'
- _description = 'Helpdesk Team Sharing'
- @api.model
- def default_get(self, fields):
- # The helpdesk team share action could be called in `helpdesk.team.collaborator`
- # and so we have to check the active_model and active_id to use
- # the right team.
- active_model = self._context.get('active_model', '')
- active_id = self._context.get('active_id', False)
- if active_model == 'helpdesk.team.collaborator':
- active_model = 'helpdesk.team'
- active_id = self._context.get('default_team_id', False)
-
- # Call parent - portal.share will handle _get_share_url
- # If the model doesn't have portal.mixin, we'll handle it in the wizard
- result = {}
- result['res_model'] = active_model or self._context.get('active_model', False)
- result['res_id'] = active_id or self._context.get('active_id', False)
-
- # Try to get share_link from portal.share, but handle if _get_share_url doesn't exist
- if result['res_model'] and result['res_id']:
- record = self.env[result['res_model']].browse(result['res_id'])
- base_url = record.get_base_url()
- # Check if record has _get_share_url method
- if hasattr(record, '_get_share_url'):
- try:
- share_url = record._get_share_url(redirect=True)
- result['share_link'] = base_url + share_url
- except:
- result['share_link'] = f"{base_url}/helpdesk/team/{result['res_id']}"
- else:
- # Fallback: generate a simple share URL
- result['share_link'] = f"{base_url}/helpdesk/team/{result['res_id']}"
- else:
- result['share_link'] = ''
-
- # Get other default values from portal.share if available
- try:
- portal_defaults = super(HelpdeskTeamShareWizard, self.with_context(active_model=active_model, active_id=active_id)).default_get(fields)
- # Merge portal defaults but keep our share_link
- portal_defaults['share_link'] = result.get('share_link', portal_defaults.get('share_link', ''))
- result.update(portal_defaults)
- except:
- pass
-
- # Continue with the rest of the logic
- if result.get('res_model') and result.get('res_id'):
- # Use sudo() to access team data to avoid security rule issues
- team = self.env[result['res_model']].sudo().browse(result['res_id'])
-
- # Check if we're editing a specific collaborator
- specific_collaborator_id = self._context.get('default_collaborator_id')
- specific_collaborator = None
- if specific_collaborator_id:
- try:
- specific_collaborator = team.collaborator_ids.filtered(lambda c: c.id == specific_collaborator_id)
- if not specific_collaborator:
- specific_collaborator = None
- else:
- specific_collaborator = specific_collaborator[0]
- except (ValueError, TypeError):
- specific_collaborator = None
-
- collaborator_vals_list = []
- collaborator_ids = []
-
- # If editing a specific collaborator, only include that one
- if specific_collaborator:
- collaborator_ids.append(specific_collaborator.partner_id.id)
- collaborator_vals_list.append({
- 'partner_id': specific_collaborator.partner_id.id,
- 'partner_name': specific_collaborator.partner_id.display_name,
- 'access_mode': specific_collaborator.access_mode,
- })
- else:
- # Include all collaborators
- for collaborator in team.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': collaborator.access_mode,
- })
- # Also include followers if not editing a specific collaborator
- # Use sudo() to access message_partner_ids
- for follower in team.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': 'user_own',
- })
-
- if collaborator_vals_list:
- # Only sort if not editing a specific collaborator
- if not specific_collaborator:
- 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):
- team_model = self.env['ir.model']._get('helpdesk.team')
- return [(team_model.model, team_model.name)]
- share_link = fields.Char(
- "Public Link",
- help="Anyone with this link can access the helpdesk team."
- )
- collaborator_ids = fields.One2many(
- 'helpdesk.team.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 == 'helpdesk.team':
- 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:
- if not wizard.resource_ref:
- continue
- # Use sudo() to access team data to avoid security rule issues
- team = wizard.resource_ref.sudo()
- if not team:
- continue
- collaborator_ids_vals_list = []
- team_collaborator_ids_to_remove = [
- c.id
- for c in team.collaborator_ids
- if c.partner_id not in wizard.collaborator_ids.partner_id
- ]
- # Use sudo() to access message_partner_ids
- team_followers = team.message_partner_ids
- team_followers_to_add = []
- team_followers_to_remove = [
- partner.id
- for partner in team_followers
- if partner not in wizard.collaborator_ids.partner_id and partner.partner_share
- ]
- team_collaborator_per_partner_id = {c.partner_id.id: c for c in team.collaborator_ids}
- collaborator_ids_to_add = []
- collaborator_ids_vals_list = []
- for collaborator in wizard.collaborator_ids:
- partner_id = collaborator.partner_id.id
- team_collaborator = team_collaborator_per_partner_id.get(partner_id, self.env['helpdesk.team.collaborator'])
- if collaborator.access_mode in ("admin", "user_all", "user_own"):
- if not team_collaborator:
- collaborator_ids_to_add.append((partner_id, collaborator.access_mode))
- elif team_collaborator.access_mode != collaborator.access_mode:
- collaborator_ids_vals_list.append(
- Command.update(
- team_collaborator.id,
- {'access_mode': collaborator.access_mode},
- )
- )
- elif team_collaborator:
- team_collaborator_ids_to_remove.append(team_collaborator.id)
- if partner_id not in team_followers.ids:
- team_followers_to_add.append(partner_id)
- if collaborator_ids_to_add:
- partners_to_add = self.env['res.partner'].browse([pid for pid, _ in collaborator_ids_to_add])
- # Validate partners before adding
- invalid_partners = partners_to_add.filtered(lambda p: not p.partner_share)
- if invalid_partners:
- raise ValidationError(_(
- "The following partners are internal users and cannot be added as collaborators: %s"
- ) % ', '.join(invalid_partners.mapped('display_name')))
-
- partners = team._get_new_collaborators(partners_to_add)
- collaborator_per_partner = {pid: mode for pid, mode in collaborator_ids_to_add}
- collaborator_ids_vals_list.extend(
- Command.create({
- 'partner_id': partner_id,
- 'access_mode': collaborator_per_partner[partner_id],
- }) for partner_id in partners.ids
- )
- if team_collaborator_ids_to_remove:
- collaborator_ids_vals_list.extend(
- Command.delete(collaborator_id) for collaborator_id in team_collaborator_ids_to_remove
- )
- team_vals = {}
- if collaborator_ids_vals_list:
- team_vals['collaborator_ids'] = collaborator_ids_vals_list
- if team_vals:
- team.write(team_vals)
- if team_followers_to_add:
- team.message_subscribe(partner_ids=team_followers_to_add)
- if team_followers_to_remove:
- team.message_unsubscribe(team_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',
- 'res_model': 'helpdesk.team.share.wizard',
- 'res_id': self.id,
- 'target': 'new',
- 'context': self.env.context,
- }
- def action_send_mail(self):
- result = {
- 'type': 'ir.actions.client',
- 'tag': 'display_notification',
- 'params': {
- 'type': 'success',
- 'message': _("Helpdesk team shared with your collaborators."),
- 'next': {'type': 'ir.actions.act_window_close'},
- }
- }
- partner_ids_to_notify = []
- for collaborator in self.collaborator_ids:
- if collaborator.send_invitation:
- partner_ids_to_notify.append(collaborator.partner_id.id)
- if partner_ids_to_notify:
- partners = self.env['res.partner'].browse(partner_ids_to_notify)
- # Validate that partners have email addresses
- partners_without_email = partners.filtered(lambda p: not p.email)
- if partners_without_email:
- raise ValidationError(_(
- "The following partners do not have email addresses and cannot receive invitations: %s"
- ) % ', '.join(partners_without_email.mapped('display_name')))
-
- portal_partners = partners.filtered('user_ids')
- # send mail to users
- self._send_public_link(portal_partners)
- self._send_signup_link(partners=partners.with_context({'signup_valid': True}) - portal_partners)
- return result
|