[IMP] survey: refine access mode option on survey
Purpose of this merge is to improve access mode and authentication management
introduced at 9771fdfe8f . Purpose of this cleaning is to simplify options and make
them clearer. Instead of a selection with 4 entries we propose to use two
fields on survey model :
* access_mode: everyone with a public link can access and answer; or only
invited people;
* login required: whether people have to login to answer or not;
All main use cases are covered with combination of those two options.
This commit is linked to task ID 1932508 and PR #30508.
This commit is contained in:
@@ -63,8 +63,7 @@ class Survey(http.Controller):
|
||||
if not answer_sudo and survey_sudo.access_mode == 'token':
|
||||
return 'token_required'
|
||||
|
||||
# Public -> no auth required; Token -> token check hereabove
|
||||
if survey_sudo.access_mode not in ['public', 'token'] and request.env.user._is_public():
|
||||
if survey_sudo.users_login_required and request.env.user._is_public():
|
||||
return 'survey_auth'
|
||||
|
||||
if survey_sudo.is_closed or not survey_sudo.active:
|
||||
|
||||
@@ -45,12 +45,11 @@ class Survey(models.Model):
|
||||
user_input_ids = fields.One2many('survey.user_input', 'survey_id', string='User responses', readonly=True, groups='survey.group_survey_user')
|
||||
# security / access
|
||||
access_mode = fields.Selection([
|
||||
('public', 'Everyone'),
|
||||
('authentication', 'Login Required'),
|
||||
('internal', 'Employees Only'),
|
||||
('token', 'Invitation only')], string='Access Mode',
|
||||
default='authentication', required=True)
|
||||
('public', 'Anyone with the link'),
|
||||
('token', 'Invited people only')], string='Access Mode',
|
||||
default='public', required=True)
|
||||
access_token = fields.Char('Access Token', default=lambda self: self._get_default_access_token())
|
||||
users_login_required = fields.Boolean('Login required', help="If checked, users have to login before answering even with a valid token.")
|
||||
users_can_go_back = fields.Boolean('Users can go back', help="If checked, users can go back to previous pages.")
|
||||
users_can_signup = fields.Boolean('Users can signup', compute='_compute_users_can_signup')
|
||||
public_url = fields.Char("Public link", compute="_compute_survey_url")
|
||||
|
||||
@@ -64,7 +64,8 @@ class SurveyCase(common.SavepointCase):
|
||||
|
||||
self.survey = self.env['survey.survey'].sudo(self.survey_manager).create({
|
||||
'title': 'Test Survey',
|
||||
'access_mode': 'authentication',
|
||||
'access_mode': 'public',
|
||||
'users_login_required': True,
|
||||
'users_can_go_back': False,
|
||||
})
|
||||
self.page_0 = self.env['survey.page'].sudo(self.survey_manager).create({
|
||||
|
||||
@@ -48,6 +48,7 @@ class TestSurveyFlow(common.SurveyCase, HttpCase):
|
||||
survey = self.env['survey.survey'].create({
|
||||
'title': 'Public Survey for Tarte Al Djotte',
|
||||
'access_mode': 'public',
|
||||
'users_login_required': False,
|
||||
})
|
||||
|
||||
# First page is about customer data
|
||||
|
||||
@@ -47,7 +47,7 @@ class TestSurveyInvite(common.SurveyCase):
|
||||
Answer = self.env['survey.user_input']
|
||||
deadline = fields.Datetime.now() + relativedelta(months=1)
|
||||
|
||||
self.survey.write({'access_mode': 'public'})
|
||||
self.survey.write({'access_mode': 'public', 'users_login_required': False})
|
||||
action = self.survey.action_send_survey()
|
||||
invite_form = Form(self.env[action['res_model']].with_context(action['context']))
|
||||
|
||||
@@ -73,7 +73,7 @@ class TestSurveyInvite(common.SurveyCase):
|
||||
def test_survey_invite_authentication_nosignup(self):
|
||||
Answer = self.env['survey.user_input']
|
||||
|
||||
self.survey.write({'access_mode': 'authentication'})
|
||||
self.survey.write({'access_mode': 'public', 'users_login_required': True})
|
||||
action = self.survey.action_send_survey()
|
||||
invite_form = Form(self.env[action['res_model']].with_context(action['context']))
|
||||
|
||||
@@ -102,7 +102,7 @@ class TestSurveyInvite(common.SurveyCase):
|
||||
self.survey.invalidate_cache()
|
||||
Answer = self.env['survey.user_input']
|
||||
|
||||
self.survey.write({'access_mode': 'authentication'})
|
||||
self.survey.write({'access_mode': 'public', 'users_login_required': True})
|
||||
action = self.survey.action_send_survey()
|
||||
invite_form = Form(self.env[action['res_model']].with_context(action['context']))
|
||||
|
||||
@@ -123,10 +123,52 @@ class TestSurveyInvite(common.SurveyCase):
|
||||
self.assertEqual(answers.mapped('partner_id'), self.customer | self.user_emp.partner_id | self.user_portal.partner_id)
|
||||
|
||||
@users('survey_manager')
|
||||
def test_survey_invite_internal(self):
|
||||
def test_survey_invite_public(self):
|
||||
Answer = self.env['survey.user_input']
|
||||
|
||||
self.survey.write({'access_mode': 'internal'})
|
||||
self.survey.write({'access_mode': 'public', 'users_login_required': False})
|
||||
action = self.survey.action_send_survey()
|
||||
invite_form = Form(self.env[action['res_model']].with_context(action['context']))
|
||||
|
||||
invite_form.partner_ids.add(self.customer)
|
||||
invite_form.emails = 'test1@example.com, Raoulette Vignolette <test2@example.com>'
|
||||
|
||||
invite = invite_form.save()
|
||||
invite.action_invite()
|
||||
|
||||
answers = Answer.search([('survey_id', '=', self.survey.id)])
|
||||
self.assertEqual(len(answers), 3)
|
||||
self.assertEqual(
|
||||
set(answers.mapped('email')),
|
||||
set(['test1@example.com', 'Raoulette Vignolette <test2@example.com>', self.customer.email]))
|
||||
self.assertEqual(answers.mapped('partner_id'), self.customer)
|
||||
|
||||
@users('survey_manager')
|
||||
def test_survey_invite_token(self):
|
||||
Answer = self.env['survey.user_input']
|
||||
|
||||
self.survey.write({'access_mode': 'token', 'users_login_required': False})
|
||||
action = self.survey.action_send_survey()
|
||||
invite_form = Form(self.env[action['res_model']].with_context(action['context']))
|
||||
|
||||
invite_form.partner_ids.add(self.customer)
|
||||
invite_form.emails = 'test1@example.com, Raoulette Vignolette <test2@example.com>'
|
||||
|
||||
invite = invite_form.save()
|
||||
invite.action_invite()
|
||||
|
||||
answers = Answer.search([('survey_id', '=', self.survey.id)])
|
||||
self.assertEqual(len(answers), 3)
|
||||
self.assertEqual(
|
||||
set(answers.mapped('email')),
|
||||
set(['test1@example.com', 'Raoulette Vignolette <test2@example.com>', self.customer.email]))
|
||||
self.assertEqual(answers.mapped('partner_id'), self.customer)
|
||||
|
||||
@users('survey_manager')
|
||||
def test_survey_invite_token_internal(self):
|
||||
Answer = self.env['survey.user_input']
|
||||
|
||||
self.survey.write({'access_mode': 'token', 'users_login_required': True})
|
||||
action = self.survey.action_send_survey()
|
||||
invite_form = Form(self.env[action['res_model']].with_context(action['context']))
|
||||
|
||||
@@ -149,45 +191,3 @@ class TestSurveyInvite(common.SurveyCase):
|
||||
set(answers.mapped('email')),
|
||||
set([self.user_emp.email]))
|
||||
self.assertEqual(answers.mapped('partner_id'), self.user_emp.partner_id)
|
||||
|
||||
@users('survey_manager')
|
||||
def test_survey_invite_public(self):
|
||||
Answer = self.env['survey.user_input']
|
||||
|
||||
self.survey.write({'access_mode': 'public'})
|
||||
action = self.survey.action_send_survey()
|
||||
invite_form = Form(self.env[action['res_model']].with_context(action['context']))
|
||||
|
||||
invite_form.partner_ids.add(self.customer)
|
||||
invite_form.emails = 'test1@example.com, Raoulette Vignolette <test2@example.com>'
|
||||
|
||||
invite = invite_form.save()
|
||||
invite.action_invite()
|
||||
|
||||
answers = Answer.search([('survey_id', '=', self.survey.id)])
|
||||
self.assertEqual(len(answers), 3)
|
||||
self.assertEqual(
|
||||
set(answers.mapped('email')),
|
||||
set(['test1@example.com', 'Raoulette Vignolette <test2@example.com>', self.customer.email]))
|
||||
self.assertEqual(answers.mapped('partner_id'), self.customer)
|
||||
|
||||
@users('survey_manager')
|
||||
def test_survey_invite_token(self):
|
||||
Answer = self.env['survey.user_input']
|
||||
|
||||
self.survey.write({'access_mode': 'token'})
|
||||
action = self.survey.action_send_survey()
|
||||
invite_form = Form(self.env[action['res_model']].with_context(action['context']))
|
||||
|
||||
invite_form.partner_ids.add(self.customer)
|
||||
invite_form.emails = 'test1@example.com, Raoulette Vignolette <test2@example.com>'
|
||||
|
||||
invite = invite_form.save()
|
||||
invite.action_invite()
|
||||
|
||||
answers = Answer.search([('survey_id', '=', self.survey.id)])
|
||||
self.assertEqual(len(answers), 3)
|
||||
self.assertEqual(
|
||||
set(answers.mapped('email')),
|
||||
set(['test1@example.com', 'Raoulette Vignolette <test2@example.com>', self.customer.email]))
|
||||
self.assertEqual(answers.mapped('partner_id'), self.customer)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<header>
|
||||
<button name="action_test_survey" string="Test" type="object" attrs="{'invisible': [('id', '=', False)]}"/>
|
||||
<button name="action_print_survey" string="Print" type="object" attrs="{'invisible': [('id', '=', False)]}"/>
|
||||
<button name="action_send_survey" string="Share and invite" type="object" class="oe_highlight" attrs="{'invisible': [('id', '=', False)]}"/>
|
||||
<button name="action_send_survey" string="Invite" type="object" class="oe_highlight" attrs="{'invisible': [('id', '=', False)]}"/>
|
||||
<button name="action_result_survey" string="View results" type="object" class="oe_highlight" attrs="{'invisible': [('id', '=', False)]}"/>
|
||||
<field name="stage_id" widget="statusbar" options="{'clickable': '1'}"/>
|
||||
</header>
|
||||
@@ -35,22 +35,36 @@
|
||||
<group>
|
||||
<group>
|
||||
<field name="access_mode"/>
|
||||
<field name="users_can_go_back"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="public_url" readonly="1"
|
||||
attrs="{'invisible': [('access_mode', '!=', 'public')]}"
|
||||
widget="CopyClipboardText"/>
|
||||
<field name="category" invisible="1"/>
|
||||
<field name="quizz_mode"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Edit Pages and Questions">
|
||||
<field name="page_ids" mode="tree" context="{'default_survey_id': active_id}" nolabel="1">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="title"/>
|
||||
<field name="question_ids"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Edit Pages and Questions">
|
||||
<field name="page_ids" mode="tree" context="{'default_survey_id': active_id}" nolabel="1">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="title"/>
|
||||
<field name="question_ids"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Options">
|
||||
<group name="options">
|
||||
<group string="Candidates" name="candidates">
|
||||
<field name="users_login_required"/>
|
||||
</group>
|
||||
<group string="Questions" name="questions">
|
||||
<field name="users_can_go_back" string="Enable back button"/>
|
||||
</group>
|
||||
<group string="Scoring Informations" name="scoring">
|
||||
<field name="quizz_mode"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
|
||||
@@ -57,11 +57,12 @@ class SurveyInvite(models.TransientModel):
|
||||
survey_id = fields.Many2one('survey.survey', string='Survey', required=True)
|
||||
survey_url = fields.Char(related="survey_id.public_url", readonly=True)
|
||||
survey_access_mode = fields.Selection(related="survey_id.access_mode", readonly=True)
|
||||
survey_users_login_required = fields.Boolean(related="survey_id.users_login_required", readonly=True)
|
||||
deadline = fields.Datetime(string="Answer deadline")
|
||||
|
||||
@api.onchange('emails')
|
||||
def _onchange_emails(self):
|
||||
if self.emails and (self.survey_access_mode == 'internal' or (self.survey_access_mode == 'authentication' and not self.survey_id.users_can_signup)):
|
||||
if self.emails and (self.survey_users_login_required and not self.survey_id.users_can_signup):
|
||||
raise UserError(_('This survey does not allow external people to participate. You should create user accounts or update survey access mode accordingly.'))
|
||||
if not self.emails:
|
||||
return
|
||||
@@ -77,27 +78,19 @@ class SurveyInvite(models.TransientModel):
|
||||
raise UserError(_("Some emails you just entered are incorrect: %s") % (', '.join(error)))
|
||||
self.emails = '\n'.join(valid)
|
||||
|
||||
@api.onchange('survey_access_mode')
|
||||
def _onchange_survey_access_mode(self):
|
||||
if self.survey_access_mode == 'internal':
|
||||
@api.onchange('survey_users_login_required')
|
||||
def _onchange_survey_users_login_required(self):
|
||||
if self.survey_users_login_required and not self.survey_id.users_can_signup:
|
||||
return {'domain': {
|
||||
'partner_ids': [('user_ids.groups_id', 'in', [self.env.ref('base.group_user').id])]
|
||||
}}
|
||||
elif self.survey_access_mode == 'authentication':
|
||||
if not self.survey_id.users_can_signup:
|
||||
return {'domain': {
|
||||
'partner_ids': [('user_ids.groups_id', 'in', [self.env.ref('base.group_user').id, self.env.ref('base.group_portal').id])]
|
||||
'partner_ids': [('user_ids', '!=', False)]
|
||||
}}
|
||||
return {'domain': {
|
||||
'partner_ids': []
|
||||
}}
|
||||
|
||||
@api.onchange('partner_ids')
|
||||
def _onchange_partner_ids(self):
|
||||
if self.survey_access_mode == 'internal' and self.partner_ids:
|
||||
invalid_partners = self.partner_ids.filtered(lambda partner: not partner.user_ids or not all(user.has_group('base.group_user') for user in partner.user_ids))
|
||||
if invalid_partners:
|
||||
raise UserError(
|
||||
_('The following recipients are not valid users belonging to the employee group: %s. You should create user accounts for them.' %
|
||||
(','.join(invalid_partners.mapped('name')))))
|
||||
elif self.survey_access_mode == 'authentication' and self.partner_ids:
|
||||
if self.survey_users_login_required and self.partner_ids:
|
||||
if not self.survey_id.users_can_signup:
|
||||
invalid_partners = self.env['res.partner'].search([
|
||||
('user_ids', '=', False),
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<group col="1">
|
||||
<group col="2">
|
||||
<field name="survey_access_mode" invisible="1"/>
|
||||
<field name="survey_users_login_required" invisible="1"/>
|
||||
<field name="survey_id" readonly="context.get('default_survey_id')"/>
|
||||
<field name="existing_mode" widget="radio" invisible="1" />
|
||||
<field name="survey_url" label="Public share URL" readonly="1" widget="CopyClipboardText"
|
||||
@@ -21,7 +22,7 @@
|
||||
context="{'force_email':True, 'show_email':True, 'no_create_edit': True}"/>
|
||||
<field name="emails"
|
||||
attrs="{
|
||||
'invisible': [('survey_access_mode', 'in', ['authentication', 'internal'])],
|
||||
'invisible': [('survey_users_login_required', '=', True)],
|
||||
'readonly': [('existing_mode', '=', 'resend')]
|
||||
}"
|
||||
placeholder="Add a list of email of recipients (will not be converted into contacts). Separated by commas, semicolons or newline..."/>
|
||||
|
||||
Reference in New Issue
Block a user