[IMP] survey: access survey through access tokens
Purpose of this commit is to use an access token to access survey instead of their ID. Routes are updated to use that token instead of the survey ID. Purpose is to have less discoverable routes and be able to control a bit more survey access even if it is not really a security parameter. Some renaming is performed to avoid confusion in terms. Survey token and answer token are now clearly labeled differently in the code to know which model we are working on. Some templates are updated to directly use the answer as base record to have access to token and test_entry fields instead of giving those parameter values directly in the controller. It makes some more diff but also allow easier edition and improvement in templates when having directly survey and answer record available. This commit is linked to task ID 1932508 and PR #30508.
This commit is contained in:
@@ -18,21 +18,21 @@ _logger = logging.getLogger(__name__)
|
||||
|
||||
class Survey(http.Controller):
|
||||
|
||||
def _fetch_from_access_token(self, survey_id, access_token):
|
||||
def _fetch_from_access_token(self, survey_token, answer_token):
|
||||
""" Check that given token matches an answer from the given survey_id.
|
||||
Returns a sudo-ed browse record of survey in order to avoid access rights
|
||||
issues now that access is granted through token. """
|
||||
survey_sudo = request.env['survey.survey'].with_context(active_test=False).sudo().browse(survey_id)
|
||||
if not access_token:
|
||||
survey_sudo = request.env['survey.survey'].with_context(active_test=False).sudo().search([('access_token', '=', survey_token)])
|
||||
if not answer_token:
|
||||
answer_sudo = request.env['survey.user_input'].sudo()
|
||||
else:
|
||||
answer_sudo = request.env['survey.user_input'].sudo().search([
|
||||
('survey_id', '=', survey_sudo.id),
|
||||
('token', '=', access_token)
|
||||
('token', '=', answer_token)
|
||||
], limit=1)
|
||||
return survey_sudo, answer_sudo
|
||||
|
||||
def _check_validity(self, survey_id, access_token, ensure_token=True):
|
||||
def _check_validity(self, survey_token, answer_token, ensure_token=True):
|
||||
""" Check survey is open and can be taken. This does not checks for
|
||||
security rules, only functional / business rules. It returns a string key
|
||||
allowing further manipulation of validity issues
|
||||
@@ -50,12 +50,12 @@ class Survey(http.Controller):
|
||||
should be enforced or not, depending on the route requesting a token or
|
||||
allowing external world calls;
|
||||
"""
|
||||
survey_sudo, answer_sudo = self._fetch_from_access_token(survey_id, access_token)
|
||||
survey_sudo, answer_sudo = self._fetch_from_access_token(survey_token, answer_token)
|
||||
|
||||
if not survey_sudo.exists():
|
||||
return 'survey_wrong'
|
||||
|
||||
if access_token and not answer_sudo:
|
||||
if answer_token and not answer_sudo:
|
||||
return 'token_wrong'
|
||||
|
||||
if not answer_sudo and ensure_token:
|
||||
@@ -81,7 +81,7 @@ class Survey(http.Controller):
|
||||
|
||||
return True
|
||||
|
||||
def _get_access_data(self, survey_id, access_token, ensure_token=True):
|
||||
def _get_access_data(self, survey_token, answer_token, ensure_token=True):
|
||||
""" Get back data related to survey and user input, given the ID and access
|
||||
token provided by the route.
|
||||
|
||||
@@ -90,9 +90,9 @@ class Survey(http.Controller):
|
||||
survey_sudo, answer_sudo = request.env['survey.survey'].sudo(), request.env['survey.user_input'].sudo()
|
||||
has_survey_access, can_answer = False, False
|
||||
|
||||
validity_code = self._check_validity(survey_id, access_token, ensure_token=ensure_token)
|
||||
validity_code = self._check_validity(survey_token, answer_token, ensure_token=ensure_token)
|
||||
if validity_code != 'survey_wrong':
|
||||
survey_sudo, answer_sudo = self._fetch_from_access_token(survey_id, access_token)
|
||||
survey_sudo, answer_sudo = self._fetch_from_access_token(survey_token, answer_token)
|
||||
try:
|
||||
survey_user = survey_sudo.sudo(request.env.user)
|
||||
survey_user.check_access_rights(self, 'read', raise_exception=True)
|
||||
@@ -128,22 +128,24 @@ class Survey(http.Controller):
|
||||
|
||||
return werkzeug.utils.redirect("/")
|
||||
|
||||
@http.route('/survey/test/<int:survey_id>', type='http', auth='user', website=True)
|
||||
def survey_test(self, survey_id, token=None, **kwargs):
|
||||
@http.route('/survey/test/<string:survey_token>', type='http', auth='user', website=True)
|
||||
def survey_test(self, survey_token, **kwargs):
|
||||
""" Test mode for surveys: create a test answer, only for managers or officers
|
||||
testing their surveys """
|
||||
survey_sudo = request.env['survey.survey'].sudo().browse(survey_id)
|
||||
survey_sudo, dummy = self._fetch_from_access_token(survey_token, False)
|
||||
try:
|
||||
answer_sudo = survey_sudo._create_answer(user=request.env.user, test_entry=True)
|
||||
except:
|
||||
return werkzeug.utils.redirect('/')
|
||||
return request.redirect('/survey/start/%s?%s' % (survey_sudo.id, keep_query('*', token=answer_sudo.token)))
|
||||
return request.redirect('/survey/start/%s?%s' % (survey_sudo.access_token, keep_query('*', answer_token=answer_sudo.token)))
|
||||
|
||||
@http.route('/survey/start/<int:survey_id>', type='http', auth='public', website=True)
|
||||
def survey_start(self, survey_id, token=None, email=False, **post):
|
||||
""" Start a survey by providing a token linked to an answer or generate
|
||||
a new token if access is allowed """
|
||||
access_data = self._get_access_data(survey_id, token, ensure_token=False)
|
||||
@http.route('/survey/start/<string:survey_token>', type='http', auth='public', website=True)
|
||||
def survey_start(self, survey_token, answer_token=None, email=False, **post):
|
||||
""" Start a survey by providing
|
||||
* a token linked to a survey;
|
||||
* a token linked to an answer or generate a new token if access is allowed;
|
||||
"""
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=False)
|
||||
if access_data['validity_code'] is not True:
|
||||
return self._redirect_with_error(access_data, access_data['validity_code'])
|
||||
|
||||
@@ -165,14 +167,14 @@ class Survey(http.Controller):
|
||||
|
||||
# Select the right page
|
||||
if answer_sudo.state == 'new': # Intro page
|
||||
data = {'survey': survey_sudo, 'page': None, 'token': answer_sudo.token, 'test_entry': answer_sudo.test_entry}
|
||||
data = {'survey': survey_sudo, 'page': None, 'answer': answer_sudo}
|
||||
return request.render('survey.survey_init', data)
|
||||
else:
|
||||
return request.redirect('/survey/fill/%s/%s' % (survey_sudo.id, answer_sudo.token))
|
||||
return request.redirect('/survey/fill/%s/%s' % (survey_sudo.access_token, answer_sudo.token))
|
||||
|
||||
@http.route('/survey/fill/<int:survey_id>/<string:token>', type='http', auth='public', website=True)
|
||||
def survey_display_page(self, survey_id, token, prev=None, **post):
|
||||
access_data = self._get_access_data(survey_id, token, ensure_token=True)
|
||||
@http.route('/survey/fill/<string:survey_token>/<string:answer_token>', type='http', auth='public', website=True)
|
||||
def survey_display_page(self, survey_token, answer_token, prev=None, **post):
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return self._redirect_with_error(access_data, access_data['validity_code'])
|
||||
|
||||
@@ -181,13 +183,12 @@ class Survey(http.Controller):
|
||||
# Select the right page
|
||||
if answer_sudo.state == 'new': # First page
|
||||
page, page_nr, last = survey_sudo.next_page(answer_sudo, 0, go_back=False)
|
||||
data = {'survey': survey_sudo, 'page': page, 'page_nr': page_nr, 'token': answer_sudo.token, 'test_entry': answer_sudo.test_entry}
|
||||
data = {'survey': survey_sudo, 'page': page, 'page_nr': page_nr, 'answer': answer_sudo}
|
||||
if last:
|
||||
data.update({'last': True})
|
||||
return request.render('survey.survey', data)
|
||||
elif answer_sudo.state == 'done': # Display success message
|
||||
return request.render('survey.sfinished', {'survey': survey_sudo,
|
||||
'token': token,
|
||||
'user_input': answer_sudo})
|
||||
elif answer_sudo.state == 'skip':
|
||||
flag = (True if prev and prev == 'prev' else False)
|
||||
@@ -197,17 +198,17 @@ class Survey(http.Controller):
|
||||
if not page:
|
||||
page, page_nr, last = survey_sudo.next_page(answer_sudo, answer_sudo.last_displayed_page_id.id, go_back=True)
|
||||
|
||||
data = {'survey': survey_sudo, 'page': page, 'page_nr': page_nr, 'token': answer_sudo.token, 'test_entry': answer_sudo.test_entry}
|
||||
data = {'survey': survey_sudo, 'page': page, 'page_nr': page_nr, 'answer': answer_sudo}
|
||||
if last:
|
||||
data.update({'last': True})
|
||||
return request.render('survey.survey', data)
|
||||
else:
|
||||
return request.render("survey.403", {'survey': survey_sudo})
|
||||
|
||||
@http.route('/survey/prefill/<int:survey_id>/<string:token>', type='http', auth='public', website=True)
|
||||
def survey_get_answers(self, survey_id, token, page_id=None, **post):
|
||||
@http.route('/survey/prefill/<string:survey_token>/<string:answer_token>', type='http', auth='public', website=True)
|
||||
def survey_get_answers(self, survey_token, answer_token, page_id=None, **post):
|
||||
""" TDE NOTE: original comment: # AJAX prefilling of a survey -> AJAX / http ?? """
|
||||
access_data = self._get_access_data(survey_id, token, ensure_token=True)
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return {}
|
||||
|
||||
@@ -250,10 +251,10 @@ class Survey(http.Controller):
|
||||
_logger.warning("[survey] No answer has been found for question %s marked as non skipped" % answer_tag)
|
||||
return json.dumps(ret, default=str)
|
||||
|
||||
@http.route('/survey/scores/<int:survey_id>/<string:token>', type='http', auth='public', website=True)
|
||||
def survey_get_scores(self, survey_id, token, page_id=None, **post):
|
||||
@http.route('/survey/scores/<string:survey_token>/<string:answer_token>', type='http', auth='public', website=True)
|
||||
def survey_get_scores(self, survey_id, answer_token, page_id=None, **post):
|
||||
""" TDE NOTE: original comment: # AJAX scores loading for quiz correction mode -> AJAX / http ?? """
|
||||
access_data = self._get_access_data(survey_id, token, ensure_token=True)
|
||||
access_data = self._get_access_data(survey_id, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return {}
|
||||
|
||||
@@ -266,10 +267,10 @@ class Survey(http.Controller):
|
||||
ret.update({answer.question_id.id: tmp_score + answer.quizz_mark})
|
||||
return json.dumps(ret)
|
||||
|
||||
@http.route('/survey/submit/<int:survey_id>/<string:token>', type='http', methods=['POST'], auth='public', website=True)
|
||||
def survey_submit(self, survey_id, token, **post):
|
||||
@http.route('/survey/submit/<string:survey_token>/<string:answer_token>', type='http', methods=['POST'], auth='public', website=True)
|
||||
def survey_submit(self, survey_token, answer_token, **post):
|
||||
""" TDE NOTE: original comment: # AJAX submission of a page -> AJAX / http ?? """
|
||||
access_data = self._get_access_data(survey_id, token, ensure_token=True)
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return {}
|
||||
|
||||
@@ -277,14 +278,14 @@ class Survey(http.Controller):
|
||||
|
||||
page_id = int(post['page_id'])
|
||||
questions = request.env['survey.question'].sudo().search([
|
||||
('survey_id', '=', survey_id),
|
||||
('survey_id', '=', survey_sudo.id),
|
||||
('page_id', '=', page_id)
|
||||
])
|
||||
|
||||
# Answer validation
|
||||
errors = {}
|
||||
for question in questions:
|
||||
answer_tag = "%s_%s_%s" % (survey_id, page_id, question.id)
|
||||
answer_tag = "%s_%s_%s" % (survey_sudo.id, page_id, question.id)
|
||||
errors.update(question.validate_question(post, answer_tag))
|
||||
|
||||
ret = {}
|
||||
@@ -293,7 +294,7 @@ class Survey(http.Controller):
|
||||
ret['errors'] = errors
|
||||
else:
|
||||
for question in questions:
|
||||
answer_tag = "%s_%s_%s" % (survey_id, page_id, question.id)
|
||||
answer_tag = "%s_%s_%s" % (survey_sudo.id, page_id, question.id)
|
||||
request.env['survey.user_input_line'].sudo().save_lines(answer_sudo.id, question, post, answer_tag)
|
||||
|
||||
go_back = post['button_submit'] == 'previous'
|
||||
@@ -304,16 +305,16 @@ class Survey(http.Controller):
|
||||
else:
|
||||
vals.update({'state': 'skip'})
|
||||
answer_sudo.write(vals)
|
||||
ret['redirect'] = '/survey/fill/%s/%s' % (survey_sudo.id, token)
|
||||
ret['redirect'] = '/survey/fill/%s/%s' % (survey_sudo.access_token, answer_token)
|
||||
if go_back:
|
||||
ret['redirect'] += '?prev=prev'
|
||||
return json.dumps(ret)
|
||||
|
||||
@http.route('/survey/print/<int:survey_id>', type='http', auth='public', website=True)
|
||||
def survey_print(self, survey_id, token=None, **post):
|
||||
'''Display an survey in printable view; if <token> is set, it will
|
||||
grab the answers of the user_input_id that has <token>.'''
|
||||
access_data = self._get_access_data(survey_id, token, ensure_token=False)
|
||||
@http.route('/survey/print/<string:survey_token>', type='http', auth='public', website=True)
|
||||
def survey_print(self, survey_token, answer_token=None, **post):
|
||||
'''Display an survey in printable view; if <answer_token> is set, it will
|
||||
grab the answers of the user_input_id that has <answer_token>.'''
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=False)
|
||||
if access_data['validity_code'] is not True and (
|
||||
not access_data['has_survey_access'] or access_data['validity_code'] not in ['token_required', 'survey_closed', 'survey_void']):
|
||||
return self._redirect_with_error(access_data, access_data['validity_code'])
|
||||
@@ -322,13 +323,12 @@ class Survey(http.Controller):
|
||||
|
||||
return request.render('survey.survey_print', {
|
||||
'survey': survey_sudo,
|
||||
'token': token,
|
||||
'test_entry': answer_sudo.test_entry,
|
||||
'answer': answer_sudo,
|
||||
'page_nr': 0,
|
||||
'quizz_correction': True if survey_sudo.quizz_mode and token else False})
|
||||
})
|
||||
|
||||
@http.route('/survey/results/<model("survey.survey"):survey>', type='http', auth='user', website=True)
|
||||
def survey_report(self, survey, token=None, **post):
|
||||
def survey_report(self, survey, answer_token=None, **post):
|
||||
'''Display survey Results & Statistics for given survey.'''
|
||||
result_template = 'survey.result'
|
||||
current_filters = []
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Dear ${object.partner_id.name or 'participant'}<br/><br/>
|
||||
We are conducting a survey and your response would be appreciated.
|
||||
<div style="margin: 16px 0px 16px 0px;">
|
||||
<a href="${('%s?token=%s' % (object.survey_id.public_url, object.token)) | safe}"
|
||||
<a href="${('%s?answer_token=%s' % (object.survey_id.public_url, object.token)) | safe}"
|
||||
style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;">
|
||||
Start Survey
|
||||
</a>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
<record model="survey.survey" id="survey_feedback">
|
||||
<field name="title">User Feedback Form</field>
|
||||
<field name="access_token">b137640d-14d4-4748-9ef6-344ca256531e</field>
|
||||
<field name="stage_id" ref="survey.stage_in_progress" />
|
||||
<field name="access_mode">public</field>
|
||||
<field name="users_can_go_back" eval="True" />
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import uuid
|
||||
|
||||
from collections import Counter, OrderedDict
|
||||
from itertools import product
|
||||
from werkzeug import urls
|
||||
|
||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||
from odoo.addons.http_routing.models.ir_http import slug
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv import expression
|
||||
|
||||
@@ -19,9 +20,12 @@ class Survey(models.Model):
|
||||
_rec_name = 'title'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
def _default_stage(self):
|
||||
def _get_default_stage_id(self):
|
||||
return self.env['survey.stage'].search([], limit=1).id
|
||||
|
||||
def _get_default_access_token(self):
|
||||
return str(uuid.uuid4())
|
||||
|
||||
# description
|
||||
title = fields.Char('Title', required=True, translate=True)
|
||||
description = fields.Html("Description", translate=True, help="A long description of the purpose of the survey")
|
||||
@@ -29,7 +33,7 @@ class Survey(models.Model):
|
||||
thank_you_message = fields.Html("Thanks Message", translate=True, help="This message will be displayed when survey is completed")
|
||||
quizz_mode = fields.Boolean("Quizz Mode")
|
||||
active = fields.Boolean("Active", default=True)
|
||||
stage_id = fields.Many2one('survey.stage', string="Stage", default=_default_stage,
|
||||
stage_id = fields.Many2one('survey.stage', string="Stage", default=lambda self: self._get_default_stage_id(),
|
||||
ondelete="restrict", copy=False, group_expand='_read_group_stage_ids')
|
||||
is_closed = fields.Boolean("Is closed", related='stage_id.closed', readonly=True)
|
||||
category = fields.Selection([
|
||||
@@ -46,6 +50,7 @@ class Survey(models.Model):
|
||||
('internal', 'Employees Only'),
|
||||
('token', 'Invitation only')], string='Access Mode',
|
||||
default='authentication', required=True)
|
||||
access_token = fields.Char('Access Token', default=lambda self: self._get_default_access_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")
|
||||
@@ -54,6 +59,10 @@ class Survey(models.Model):
|
||||
tot_start_survey = fields.Integer("Number of started surveys", compute="_compute_survey_statistic")
|
||||
tot_comp_survey = fields.Integer("Number of completed surveys", compute="_compute_survey_statistic")
|
||||
|
||||
_sql_constraints = [
|
||||
('access_token_unique', 'unique(access_token)', 'Access token should be unique')
|
||||
]
|
||||
|
||||
@api.multi
|
||||
def _compute_users_can_signup(self):
|
||||
signup_allowed = self.env['res.users'].sudo()._get_signup_invitation_scope() == 'b2c'
|
||||
@@ -78,7 +87,7 @@ class Survey(models.Model):
|
||||
""" Computes a public URL for the survey """
|
||||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||
for survey in self:
|
||||
survey.public_url = urls.url_join(base_url, "survey/start/%s" % (survey.id))
|
||||
survey.public_url = urls.url_join(base_url, "survey/start/%s" % (survey.access_token))
|
||||
|
||||
@api.model
|
||||
def _read_group_stage_ids(self, stages, domain, order):
|
||||
@@ -326,7 +335,7 @@ class Survey(models.Model):
|
||||
""" Open the website page with the survey form """
|
||||
self.ensure_one()
|
||||
token = self.env.context.get('survey_token')
|
||||
trail = "?token=%s" % token if token else ""
|
||||
trail = "?answer_token=%s" % token if token else ""
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'name': "Start Survey",
|
||||
@@ -367,12 +376,12 @@ class Survey(models.Model):
|
||||
""" Open the website page with the survey printable view """
|
||||
self.ensure_one()
|
||||
token = self.env.context.get('survey_token')
|
||||
trail = "?token=%s" % token if token else ""
|
||||
trail = "?answer_token=%s" % token if token else ""
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'name': "Print Survey",
|
||||
'target': 'self',
|
||||
'url': '/survey/print/%s%s' % (self.id, trail)
|
||||
'url': '/survey/print/%s%s' % (self.access_token, trail)
|
||||
}
|
||||
|
||||
@api.multi
|
||||
@@ -394,7 +403,7 @@ class Survey(models.Model):
|
||||
'type': 'ir.actions.act_url',
|
||||
'name': "Test Survey",
|
||||
'target': 'self',
|
||||
'url': '/survey/test/%s' % self.id,
|
||||
'url': '/survey/test/%s' % self.access_token,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -92,7 +92,7 @@ class SurveyUserInput(models.Model):
|
||||
'type': 'ir.actions.act_url',
|
||||
'name': "View Answers",
|
||||
'target': 'self',
|
||||
'url': '/survey/print/%s?token=%s' % (self.survey_id.id, self.token)
|
||||
'url': '/survey/print/%s?answer_token=%s' % (self.survey_id.access_token, self.token)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ var tour = require('web_tour.tour');
|
||||
|
||||
tour.register('test_survey', {
|
||||
test: true,
|
||||
url: '/survey/start/1',
|
||||
url: '/survey/start/b137640d-14d4-4748-9ef6-344ca256531e',
|
||||
}, [
|
||||
// Page-1
|
||||
{
|
||||
|
||||
@@ -12,13 +12,13 @@ from odoo.tests.common import HttpCase
|
||||
class TestSurveyFlow(common.SurveyCase, HttpCase):
|
||||
|
||||
def _access_start(self, survey):
|
||||
return self.url_open('/survey/start/%s' % survey.id)
|
||||
return self.url_open('/survey/start/%s' % survey.access_token)
|
||||
|
||||
def _access_page(self, survey, token):
|
||||
return self.url_open('/survey/fill/%s/%s' % (survey.id, token))
|
||||
return self.url_open('/survey/fill/%s/%s' % (survey.access_token, token))
|
||||
|
||||
def _access_submit(self, survey, token, post_data):
|
||||
return self.url_open('/survey/submit/%s/%s' % (survey.id, token), data=post_data)
|
||||
return self.url_open('/survey/submit/%s/%s' % (survey.access_token, token), data=post_data)
|
||||
|
||||
def _find_csrf_token(self, text):
|
||||
csrf_token_re = re.compile("(input.+csrf_token.+value=\")([_a-zA-Z0-9]{51})", re.MULTILINE)
|
||||
|
||||
@@ -6,10 +6,13 @@ import odoo.tests
|
||||
class TestUi(odoo.tests.HttpCase):
|
||||
|
||||
def test_01_admin_survey_tour(self):
|
||||
self.phantom_js("/survey/start/1", "odoo.__DEBUG__.services['web_tour.tour'].run('test_survey')", "odoo.__DEBUG__.services['web_tour.tour'].tours.test_survey.ready", login="admin")
|
||||
access_token = self.env.ref('survey.survey_feedback').access_token
|
||||
self.phantom_js("/survey/start/%s" % access_token, "odoo.__DEBUG__.services['web_tour.tour'].run('test_survey')", "odoo.__DEBUG__.services['web_tour.tour'].tours.test_survey.ready", login="admin")
|
||||
|
||||
def test_02_demo_survey_tour(self):
|
||||
self.phantom_js("/survey/start/1", "odoo.__DEBUG__.services['web_tour.tour'].run('test_survey')", "odoo.__DEBUG__.services['web_tour.tour'].tours.test_survey.ready", login="demo")
|
||||
access_token = self.env.ref('survey.survey_feedback').access_token
|
||||
self.phantom_js("/survey/start/%s" % access_token, "odoo.__DEBUG__.services['web_tour.tour'].run('test_survey')", "odoo.__DEBUG__.services['web_tour.tour'].tours.test_survey.ready", login="demo")
|
||||
|
||||
def test_03_public_survey_tour(self):
|
||||
self.phantom_js("/survey/start/1", "odoo.__DEBUG__.services['web_tour.tour'].run('test_survey')", "odoo.__DEBUG__.services['web_tour.tour'].tours.test_survey.ready")
|
||||
access_token = self.env.ref('survey.survey_feedback').access_token
|
||||
self.phantom_js("/survey/start/%s" % access_token, "odoo.__DEBUG__.services['web_tour.tour'].run('test_survey')", "odoo.__DEBUG__.services['web_tour.tour'].tours.test_survey.ready")
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<h1>Thank you!</h1>
|
||||
<div t-field="survey.thank_you_message" class="oe_no_empty" />
|
||||
<div t-if='survey.quizz_mode'>You scored <t t-esc="user_input.quizz_score" /> points.</div>
|
||||
<div>If you wish, you can <a t-att-href="'/survey/print/%s?token=%s' % (survey.id, token)">review your answers</a>.</div>
|
||||
<div>If you wish, you can <a t-att-href="'/survey/print/%s?answer_token=%s' % (survey.access_token, user_input.token)">review your answers</a>.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -58,7 +58,7 @@
|
||||
<div class="jumbotron mt32">
|
||||
<h1>Login required</h1>
|
||||
<p>This survey is open only to registered people. Please
|
||||
<a t-att-href="'/web/login?redirect=%s' % ('/survey/start/%s?token=%s' % (survey.id, token))">log in</a>.
|
||||
<a t-att-href="'/web/login?redirect=%s' % ('/survey/start/%s?answer_token=%s' % (survey.access_token, answer_token))">log in</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,14 +140,14 @@
|
||||
<div class="wrap">
|
||||
<div class="oe_structure" id="oe_structure_survey_init_1"/>
|
||||
<div class="container">
|
||||
<div class="alert alert-danger text-center no_print" t-if="test_entry">
|
||||
<div class="alert alert-danger text-center no_print" t-if="answer.test_entry">
|
||||
<span>This is a test survey. Your answer will not be registered in the answers. </span>
|
||||
</div>
|
||||
<t t-call="survey.back" />
|
||||
<div class='jumbotron mt32'>
|
||||
<h1 t-field='survey.title' />
|
||||
<div t-field='survey.description' class="oe_no_empty"/>
|
||||
<a role="button" class="btn btn-primary btn-lg" t-att-href="'/survey/fill/%s/%s' % (survey.id, token)">
|
||||
<a role="button" class="btn btn-primary btn-lg" t-att-href="'/survey/fill/%s/%s' % (survey.access_token, answer.token)">
|
||||
Start Survey
|
||||
</a>
|
||||
</div>
|
||||
@@ -174,7 +174,7 @@
|
||||
<!-- A page -->
|
||||
<template id="page" name="Page">
|
||||
<div class="o_page_header">
|
||||
<div class="alert alert-danger text-center no_print" t-if="test_entry">
|
||||
<div class="alert alert-danger text-center no_print" t-if="answer.test_entry">
|
||||
<span>This is a test survey. Your answer will not be registered in the answers. </span>
|
||||
</div>
|
||||
<p class="float-right">Page <span t-raw='page_nr + 1'/> of <span t-raw="len(survey.page_ids)"/></p>
|
||||
@@ -183,13 +183,13 @@
|
||||
</div>
|
||||
|
||||
<form role="form" method="post" class="js_surveyform" t-att-name="'%s_%s' % (survey.id, page.id)"
|
||||
t-att-action="'/survey/fill/%s/%s' % (survey.id, token)"
|
||||
t-att-data-prefill="'/survey/prefill/%s/%s?page_id=%s' % (survey.id, token, page.id)"
|
||||
t-att-data-validate="'/survey/validate/%s/%s' % (survey.id, token)"
|
||||
t-att-data-submit="'/survey/submit/%s/%s' % (survey.id, token)">
|
||||
t-att-action="'/survey/fill/%s/%s' % (survey.access_token, answer.token)"
|
||||
t-att-data-prefill="'/survey/prefill/%s/%s?page_id=%s' % (survey.access_token, answer.token, page.id)"
|
||||
t-att-data-validate="'/survey/validate/%s/%s' % (survey.access_token, answer.token)"
|
||||
t-att-data-submit="'/survey/submit/%s/%s' % (survey.access_token, answer.token)">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="page_id" t-att-value="page.id" />
|
||||
<input type="hidden" name="token" t-att-value="token" />
|
||||
<input type="hidden" name="token" t-att-value="answer.token" />
|
||||
<t t-foreach='page.question_ids' t-as='question'>
|
||||
<t t-set="prefix" t-value="'%s_%s_%s' % (survey.id, page.id, question.id)" />
|
||||
<div class="js_question-wrapper" t-att-id="prefix">
|
||||
@@ -273,7 +273,7 @@
|
||||
</template>
|
||||
|
||||
<template id="simple_choice" name="Simple choice">
|
||||
<div t-if="question.display_mode == 'dropdown' and token" class="js_drop row">
|
||||
<div t-if="question.display_mode == 'dropdown' and answer.token" class="js_drop row">
|
||||
<div class="col-lg-12">
|
||||
<select class="form-control" t-att-name="prefix">
|
||||
<option disabled="1" selected="1" value="">Choose...</option>
|
||||
@@ -293,7 +293,7 @@
|
||||
<input type="text" class="form-control" t-att-name="'%s_%s' % (prefix, 'comment')"/>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="question.display_mode == 'columns' or not token" class="row js_radio">
|
||||
<div t-if="question.display_mode == 'columns' or not answer.token" class="row js_radio">
|
||||
<div t-foreach='question.labels_ids' t-as='label' t-attf-class="col-lg-#{question.column_nb}">
|
||||
<label t-att-class="' bg-success ' if quizz_correction and label.quizz_mark > 0.0 else None">
|
||||
<input type="radio" t-att-name="prefix" t-att-value='label.id' />
|
||||
@@ -365,7 +365,7 @@
|
||||
<t t-call="survey.layout">
|
||||
<div class="wrap">
|
||||
<div class="container">
|
||||
<div class="alert alert-danger text-center no_print" t-if="test_entry">
|
||||
<div class="alert alert-danger text-center no_print" t-if="answer and answer.test_entry">
|
||||
<span>This is a test survey. Your answer will not be registered in the answers. </span>
|
||||
</div>
|
||||
<t t-call="survey.back" />
|
||||
@@ -373,7 +373,7 @@
|
||||
<h1><span t-field='survey.title'/></h1>
|
||||
<t t-if="survey.description"><div t-field='survey.description' class="oe_no_empty"/></t>
|
||||
</div>
|
||||
<div role="form" class="js_surveyform" t-att-name="'%s' % (survey.id)" t-att-data-prefill="'/survey/prefill/%s/%s' % (survey.id, token)">
|
||||
<div role="form" class="js_surveyform" t-att-name="'%s' % (survey.id)" t-att-data-prefill="'/survey/prefill/%s/%s' % (survey.access_token, answer.token)">
|
||||
<t t-foreach="survey.page_ids" t-as="page">
|
||||
<div class="o_page_header">
|
||||
<h1 t-field='page.title' />
|
||||
@@ -493,20 +493,20 @@
|
||||
<tbody>
|
||||
<t t-set="text_result" t-value="prepare_result"/>
|
||||
<tr t-foreach="text_result" t-as="user_input">
|
||||
<td><a t-att-href="'/survey/print/%s?token=%s' % (user_input.survey_id.id, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
|
||||
<td><a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
|
||||
<t t-if="question.question_type == 'free_text'">
|
||||
<td>
|
||||
<a t-att-href="'/survey/print/%s?token=%s' % (user_input.survey_id.id, user_input.user_input_id.token)" t-field="user_input.value_free_text"></a><br/>
|
||||
<a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.token)" t-field="user_input.value_free_text"></a><br/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-if="question.question_type == 'textbox'">
|
||||
<td>
|
||||
<a t-att-href="'/survey/print/%s?token=%s' % (user_input.survey_id.id, user_input.user_input_id.token)" t-field="user_input.value_text"></a><br/>
|
||||
<a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.token)" t-field="user_input.value_text"></a><br/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-if="question.question_type == 'date'">
|
||||
<td>
|
||||
<a t-att-href="'/survey/print/%s?token=%s' % (user_input.survey_id.id, user_input.user_input_id.token)" t-field="user_input.value_date"></a><br/>
|
||||
<a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.token)" t-field="user_input.value_date"></a><br/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
@@ -527,7 +527,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="comments" t-as="user_input">
|
||||
<td><a t-att-href="'/survey/print/%s?token=%s' % (user_input.survey_id.id, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
|
||||
<td><a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
|
||||
<td>
|
||||
<span t-field="user_input.value_text"></span><br/>
|
||||
</td>
|
||||
@@ -711,7 +711,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="d-none" t-foreach="number_result['input_lines']" t-as="user_input">
|
||||
<td><a t-att-href="'/survey/print/%s?token=%s' % (user_input.survey_id.id, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
|
||||
<td><a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
|
||||
<td><span t-field="user_input.value_number"></span><br/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user