[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:
Thibault Delavallée
2019-01-25 13:12:22 +00:00
parent c801eecf23
commit e65bcb24fd
8 changed files with 95 additions and 87 deletions
+1 -2
View File
@@ -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:
+4 -5
View File
@@ -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")
+2 -1
View File
@@ -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({
+1
View File
@@ -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 -47
View File
@@ -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)
+28 -14
View File
@@ -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"/>
+10 -17
View File
@@ -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),
+2 -1
View File
@@ -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..."/>