[IMP] survey: add background option per section
This commit adds the background per section feature. If the section to display has a background image configured, the background will be refreshed using that section background image. If the section has no background, the survey background is used. The background is refreshed at the same time that the next question is loaded. To ease technical maintenance and to keep it simple, the background is always faded out/in at each page change only if there are some sections on the survey that has a specific background image. If no background image are set on the survey sections, the background will never be faded out. Note: The next section to display depends on free text (section with description) configuration and on conditional questions. The next section to display can be the next question's section or directly the next question itself (if the question is a section). This section's background image implementation works both for the regular survey form as well as the "live session" mode of the survey module. Task-2225393 closes odoo/odoo#79156 Signed-off-by: Warnon Aurélien (awa) <awa@odoo.com>
This commit is contained in:
committed by
Aurélien Warnon
parent
d5ea0bf26b
commit
fd75481d52
@@ -68,6 +68,7 @@ sent mails with personal token for the invitation of the survey.
|
||||
'survey/static/src/js/survey_timer.js',
|
||||
'survey/static/src/js/survey_breadcrumb.js',
|
||||
'survey/static/src/js/survey_form.js',
|
||||
'survey/static/src/js/survey_preload_image_mixin.js',
|
||||
'survey/static/src/js/survey_print.js',
|
||||
'survey/static/src/js/survey_result.js',
|
||||
('include', 'web._assets_helpers'),
|
||||
|
||||
@@ -319,10 +319,11 @@ class Survey(http.Controller):
|
||||
|
||||
def _prepare_question_html(self, survey_sudo, answer_sudo, **post):
|
||||
""" Survey page navigation is done in AJAX. This function prepare the 'next page' to display in html
|
||||
and send back this html to the survey_form widget that will inject it into the page."""
|
||||
and send back this html to the survey_form widget that will inject it into the page.
|
||||
Background url must be given to the caller in order to process its refresh as we don't have the next question
|
||||
object at frontend side."""
|
||||
survey_data = self._prepare_survey_data(survey_sudo, answer_sudo, **post)
|
||||
|
||||
survey_content = False
|
||||
if answer_sudo.state == 'done':
|
||||
survey_content = request.env.ref('survey.survey_fill_form_done')._render(survey_data)
|
||||
else:
|
||||
@@ -345,10 +346,17 @@ class Survey(http.Controller):
|
||||
'page_number': page_ids.index(survey_data['question'].id)
|
||||
})
|
||||
|
||||
background_image_url = survey_sudo.background_image_url
|
||||
if 'question' in survey_data:
|
||||
background_image_url = survey_data['question'].background_image_url
|
||||
elif 'page' in survey_data:
|
||||
background_image_url = survey_data['page'].background_image_url
|
||||
|
||||
return {
|
||||
'survey_content': survey_content,
|
||||
'survey_progress': survey_progress,
|
||||
'survey_navigation': request.env.ref('survey.survey_navigation')._render(survey_data),
|
||||
'background_image_url': background_image_url
|
||||
}
|
||||
|
||||
@http.route('/survey/<string:survey_token>/<string:answer_token>', type='http', auth='public', website=True)
|
||||
@@ -364,18 +372,32 @@ class Survey(http.Controller):
|
||||
return request.render('survey.survey_page_fill',
|
||||
self._prepare_survey_data(access_data['survey_sudo'], answer_sudo, **post))
|
||||
|
||||
@http.route('/survey/get_background_image/<string:survey_token>/<string:answer_token>', type='http', auth="public", website=True, sitemap=False)
|
||||
def survey_get_background(self, survey_token, answer_token):
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
|
||||
if access_data['validity_code'] is not True:
|
||||
return werkzeug.exceptions.Forbidden()
|
||||
# --------------------------------------------------------------------------
|
||||
# ROUTES to handle question images + survey background transitions + Tool
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
|
||||
@http.route('/survey/<string:survey_token>/get_background_image',
|
||||
type='http', auth="public", website=True, sitemap=False)
|
||||
def survey_get_background(self, survey_token):
|
||||
survey_sudo, dummy = self._fetch_from_access_token(survey_token, False)
|
||||
return self._get_background_image(survey_sudo._name, survey_sudo.id)
|
||||
|
||||
@http.route('/survey/<string:survey_token>/<int:section_id>/get_background_image',
|
||||
type='http', auth="public", website=True, sitemap=False)
|
||||
def survey_section_get_background(self, survey_token, section_id):
|
||||
survey_sudo, dummy = self._fetch_from_access_token(survey_token, False)
|
||||
|
||||
section = survey_sudo.page_ids.filtered(lambda q: q.id == section_id)
|
||||
if not section:
|
||||
# trying to access a question that is not in this survey
|
||||
raise werkzeug.exceptions.Forbidden()
|
||||
|
||||
return self._get_background_image(section._name, section.id)
|
||||
|
||||
def _get_background_image(self, model_name, res_id):
|
||||
status, headers, image_base64 = request.env['ir.http'].sudo().binary_content(
|
||||
model='survey.survey', id=survey_sudo.id, field='background_image',
|
||||
model=model_name, id=res_id, field='background_image',
|
||||
default_mimetype='image/png')
|
||||
|
||||
return request.env['ir.http']._content_image_get_response(status, headers, image_base64)
|
||||
|
||||
@http.route('/survey/get_question_image/<string:survey_token>/<string:answer_token>/<int:question_id>/<int:suggested_answer_id>', type='http', auth="public", website=True, sitemap=False)
|
||||
|
||||
@@ -87,13 +87,17 @@ class UserInputSession(http.Controller):
|
||||
|
||||
Writing the next question on the survey is sudo'ed to avoid potential access right issues.
|
||||
e.g: a survey user can create a live session from any survey but he can only write
|
||||
on its own survey. """
|
||||
on its own survey.
|
||||
|
||||
In addition to return a pre-rendered html template with the next question, we also return the background
|
||||
to display. Background image depends on the next question to display and cannot be extracted from the
|
||||
html rendered question template. The background needs to be changed at frontend side on a specific selector."""
|
||||
|
||||
survey = self._fetch_from_token(survey_token)
|
||||
|
||||
if not survey or not survey.session_state:
|
||||
# no open session
|
||||
return ''
|
||||
return {}
|
||||
|
||||
if survey.session_state == 'ready':
|
||||
survey._session_open()
|
||||
@@ -107,13 +111,19 @@ class UserInputSession(http.Controller):
|
||||
'session_question_id': next_question.id,
|
||||
'session_question_start_time': fields.Datetime.now() + relativedelta(seconds=1)
|
||||
})
|
||||
request.env['bus.bus']._sendone(survey.access_token, 'next_question', {'question_start': now.timestamp()})
|
||||
request.env['bus.bus']._sendone(survey.access_token, 'next_question', {
|
||||
'question_start': now.timestamp()
|
||||
})
|
||||
|
||||
template_values = self._prepare_manage_session_values(survey)
|
||||
template_values['is_rpc_call'] = True
|
||||
return request.env.ref('survey.user_input_session_manage_content')._render(template_values)
|
||||
|
||||
return {
|
||||
'background_image_url': survey.session_question_id.background_image_url,
|
||||
'question_html': request.env.ref('survey.user_input_session_manage_content')._render(template_values)
|
||||
}
|
||||
else:
|
||||
return False
|
||||
return {}
|
||||
|
||||
@http.route('/survey/session/results/<string:survey_token>', type='json', auth='user', website=True)
|
||||
def survey_session_results(self, survey_token, **kwargs):
|
||||
|
||||
@@ -58,6 +58,8 @@ class SurveyQuestion(models.Model):
|
||||
'Description', translate=True, sanitize=False, # TDE TODO: sanitize but find a way to keep youtube iframe media stuff
|
||||
help="Use this field to add additional explanations about your question or to illustrate it with pictures or a video")
|
||||
question_placeholder = fields.Char("Placeholder", translate=True, compute="_compute_question_placeholder", store=True, readonly=False)
|
||||
background_image = fields.Image("Background Image", compute="_compute_background_image", store=True, readonly=False)
|
||||
background_image_url = fields.Char("Background Url", compute="_compute_background_image_url")
|
||||
survey_id = fields.Many2one('survey.survey', string='Survey', ondelete='cascade')
|
||||
scoring_type = fields.Selection(related='survey_id.scoring_type', string='Scoring Type', readonly=True)
|
||||
sequence = fields.Integer('Sequence', default=10)
|
||||
@@ -179,6 +181,34 @@ class SurveyQuestion(models.Model):
|
||||
or not question.question_placeholder: # avoid CacheMiss errors
|
||||
question.question_placeholder = False
|
||||
|
||||
@api.depends('is_page')
|
||||
def _compute_background_image(self):
|
||||
""" Background image is only available on sections. """
|
||||
for question in self.filtered(lambda q: not q.is_page):
|
||||
question.background_image = False
|
||||
|
||||
@api.depends('survey_id.access_token', 'background_image', 'page_id', 'survey_id.background_image_url')
|
||||
def _compute_background_image_url(self):
|
||||
""" How the background url is computed:
|
||||
- For a question: it depends on the related section (see below)
|
||||
- For a section:
|
||||
- if a section has a background, then we create the background URL using this section's ID
|
||||
- if not, then we fallback on the survey background url """
|
||||
base_bg_url = "/survey/%s/%s/get_background_image"
|
||||
for question in self:
|
||||
if question.is_page:
|
||||
background_section_id = question.id if question.background_image else False
|
||||
else:
|
||||
background_section_id = question.page_id.id if question.page_id.background_image else False
|
||||
|
||||
if background_section_id:
|
||||
question.background_image_url = base_bg_url % (
|
||||
question.survey_id.access_token,
|
||||
background_section_id
|
||||
)
|
||||
else:
|
||||
question.background_image_url = question.survey_id.background_image_url
|
||||
|
||||
@api.depends('is_page')
|
||||
def _compute_question_type(self):
|
||||
for question in self:
|
||||
|
||||
@@ -54,7 +54,8 @@ class Survey(models.Model):
|
||||
description_done = fields.Html(
|
||||
"End Message", translate=True,
|
||||
help="This message will be displayed when survey is completed")
|
||||
background_image = fields.Binary("Background Image")
|
||||
background_image = fields.Image("Background Image")
|
||||
background_image_url = fields.Char('Background Url', compute="_compute_background_image_url")
|
||||
active = fields.Boolean("Active", default=True)
|
||||
user_id = fields.Many2one(
|
||||
'res.users', string='Responsible',
|
||||
@@ -171,6 +172,12 @@ class Survey(models.Model):
|
||||
('badge_uniq', 'unique (certification_badge_id)', "The badge for each survey should be unique!"),
|
||||
]
|
||||
|
||||
@api.depends('background_image', 'access_token')
|
||||
def _compute_background_image_url(self):
|
||||
self.background_image_url = False
|
||||
for survey in self.filtered(lambda survey: survey.background_image and survey.access_token):
|
||||
survey.background_image_url = "/survey/%s/get_background_image" % survey.access_token
|
||||
|
||||
def _compute_users_can_signup(self):
|
||||
signup_allowed = self.env['res.users'].sudo()._get_signup_invitation_scope() == 'b2c'
|
||||
for survey in self:
|
||||
|
||||
@@ -22,7 +22,7 @@ var FormDescriptionPage = FieldChar.extend({
|
||||
var $button = $(
|
||||
'<div class="input-group-append">\
|
||||
<button type="button" title="Open section" class="btn oe_edit_only o_icon_button">\
|
||||
<i class="fa fa-fw o_button_icon fa-info-circle"/>\
|
||||
<i class="fa fa-fw o_button_icon fa-external-link"/>\
|
||||
</button>\
|
||||
</div>'
|
||||
);
|
||||
|
||||
@@ -9,10 +9,12 @@ var Dialog = require('web.Dialog');
|
||||
var dom = require('web.dom');
|
||||
var utils = require('web.utils');
|
||||
|
||||
var SurveyPreloadImageMixin = require('survey.preload_image_mixin');
|
||||
|
||||
var _t = core._t;
|
||||
var isMac = navigator.platform.toUpperCase().includes('MAC');
|
||||
|
||||
publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloadImageMixin, {
|
||||
selector: '.o_survey_form',
|
||||
events: {
|
||||
'change .o_survey_form_choice_item': '_onChangeChoiceItem',
|
||||
@@ -441,20 +443,35 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
self.$(selectorsToFadeout.join(',')).fadeOut(this.fadeInOutDelay, function () {
|
||||
resolveFadeOut();
|
||||
});
|
||||
// Background management - Fade in / out on each transition
|
||||
if (this.options.refreshBackground) {
|
||||
$('div.o_survey_background').addClass('o_survey_background_transition');
|
||||
}
|
||||
|
||||
Promise.all([fadeOutPromise, nextScreenPromise]).then(function (results) {
|
||||
return self._onNextScreenDone(results[1], options);
|
||||
var nextScreenWithBackgroundPromise = nextScreenPromise.then(function (result) {
|
||||
self.nextScreenResult = result;
|
||||
// once we have the next question, wait for the preload of the background
|
||||
if (self.options.refreshBackground && result.background_image_url) {
|
||||
return self._preloadBackground(result.background_image_url);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for the fade out and the preload of the next background. The next question have already been fetched.
|
||||
Promise.all([fadeOutPromise, nextScreenWithBackgroundPromise]).then(function () {
|
||||
return self._onNextScreenDone(options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle server side validation and display eventual error messages.
|
||||
*
|
||||
* @param {string} result the HTML result of the screen to display
|
||||
* @param {Object} options see '_submitForm' for details
|
||||
*/
|
||||
_onNextScreenDone: function (result, options) {
|
||||
_onNextScreenDone: function (options) {
|
||||
var self = this;
|
||||
var result = this.nextScreenResult;
|
||||
|
||||
if (!(options && options.isFinish)
|
||||
&& !this.options.sessionInProgress) {
|
||||
@@ -511,7 +528,11 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
// prevent enter submit if we're on a page description (there is nothing to submit)
|
||||
this.preventEnterSubmit = true;
|
||||
}
|
||||
|
||||
// Background management - reset background overlay opacity to 0.7 to discover next background.
|
||||
if (this.options.refreshBackground) {
|
||||
$('div.o_survey_background').css("background-image", "url(" + result.background_image_url + ")");
|
||||
$('div.o_survey_background').removeClass('o_survey_background_transition');
|
||||
}
|
||||
this.$('.o_survey_form_content').fadeIn(this.fadeInOutDelay);
|
||||
$("html, body").animate({ scrollTop: 0 }, this.fadeInOutDelay);
|
||||
self._focusOnFirstInput();
|
||||
@@ -992,6 +1013,9 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend({
|
||||
}
|
||||
},
|
||||
|
||||
// OTHER TOOLS
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will automatically focus on the first input to allow the user to complete directly the survey,
|
||||
* without having to manually get the focus (only if the input has the right type - can write something inside -)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
odoo.define('survey.preload_image_mixin', function (require) {
|
||||
"use strict";
|
||||
|
||||
|
||||
return {
|
||||
/**
|
||||
* Load the target section background and render it when loaded.
|
||||
*
|
||||
* This method is used to pre-load the image during the questions transitions (fade out) in order
|
||||
* to be sure the image is fully loaded when setting it as background of the next question and
|
||||
* finally display it (fade in)
|
||||
*
|
||||
* This idea is to wait until new background is loaded before changing the background
|
||||
* (to avoid flickering or loading latency)
|
||||
*
|
||||
* @param {string} imageUrl
|
||||
* @private
|
||||
*/
|
||||
_preloadBackground: async function (imageUrl) {
|
||||
var resolvePreload;
|
||||
|
||||
// We have to manually create a promise here because the "onload" API does not provide one.
|
||||
var preloadPromise = new Promise(function (resolve, reject) {resolvePreload = resolve;});
|
||||
var background = new Image();
|
||||
background.onload = function () {
|
||||
resolvePreload(imageUrl);
|
||||
};
|
||||
background.src = imageUrl;
|
||||
|
||||
return preloadPromise;
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
@@ -2,13 +2,14 @@ odoo.define('survey.session_manage', function (require) {
|
||||
'use strict';
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
var SurveyPreloadImageMixin = require('survey.preload_image_mixin');
|
||||
var SurveySessionChart = require('survey.session_chart');
|
||||
var SurveySessionTextAnswers = require('survey.session_text_answers');
|
||||
var SurveySessionLeaderBoard = require('survey.session_leaderboard');
|
||||
var core = require('web.core');
|
||||
var _t = core._t;
|
||||
|
||||
publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPreloadImageMixin, {
|
||||
selector: '.o_survey_session_manage',
|
||||
events: {
|
||||
'click .o_survey_session_copy': '_onCopySessionLink',
|
||||
@@ -41,6 +42,10 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
// display props
|
||||
self.showBarChart = self.$el.data('showBarChart');
|
||||
self.showTextAnswers = self.$el.data('showTextAnswers');
|
||||
// Question transition
|
||||
self.stopNextQuestion = false;
|
||||
// Background Management
|
||||
self.refreshBackground = self.$el.data('refreshBackground');
|
||||
|
||||
var isRpcCall = self.$el.data('isRpcCall');
|
||||
if (!isRpcCall) {
|
||||
@@ -304,6 +309,12 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
_nextQuestion: function (goBack) {
|
||||
var self = this;
|
||||
|
||||
// stop calling multiple times "get next question" process until next question is fully loaded.
|
||||
if (this.stopNextQuestion) {
|
||||
return;
|
||||
}
|
||||
this.stopNextQuestion = true;
|
||||
|
||||
this.isStartScreen = false;
|
||||
if (this.surveyTimerWidget) {
|
||||
this.surveyTimerWidget.destroy();
|
||||
@@ -315,12 +326,9 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
resolveFadeOut();
|
||||
});
|
||||
|
||||
var nextQuestionPromise = this._rpc({
|
||||
route: _.str.sprintf('/survey/session/next_question/%s', self.surveyAccessToken),
|
||||
params: {
|
||||
'go_back': goBack,
|
||||
}
|
||||
});
|
||||
if (this.refreshBackground) {
|
||||
$('div.o_survey_background').addClass('o_survey_background_transition');
|
||||
}
|
||||
|
||||
// avoid refreshing results while transitioning
|
||||
if (this.resultsRefreshInterval) {
|
||||
@@ -328,41 +336,72 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend({
|
||||
delete this.resultsRefreshInterval;
|
||||
}
|
||||
|
||||
Promise.all([fadeOutPromise, nextQuestionPromise]).then(async function (results) {
|
||||
if (results[1]) {
|
||||
var $renderedTemplate = $(results[1]);
|
||||
self.$el.replaceWith($renderedTemplate);
|
||||
|
||||
// Ensure new question is fully loaded before force loading previous question screen.
|
||||
await self.attachTo($renderedTemplate);
|
||||
if (goBack) {
|
||||
// As we arrive on "question" screen, simulate going to the results screen or leaderboard.
|
||||
self._setShowInputs(true);
|
||||
self._setShowAnswers(true);
|
||||
if (self.sessionShowLeaderboard && self.isScoredQuestion) {
|
||||
self.currentScreen = 'leaderboard';
|
||||
self.leaderBoard.showLeaderboard(false, self.isScoredQuestion);
|
||||
} else {
|
||||
self.currentScreen = 'results';
|
||||
self._refreshResults();
|
||||
}
|
||||
} else {
|
||||
self._startTimer();
|
||||
}
|
||||
self.$el.fadeIn(self.fadeInOutTime);
|
||||
} else if (self.sessionShowLeaderboard) {
|
||||
// Display last screen if leaderboard activated
|
||||
self.isLastQuestion = true;
|
||||
self._setupLeaderboard().then(function () {
|
||||
self.$('.o_survey_session_leaderboard_title').text(_t('Final Leaderboard'));
|
||||
self.$('.o_survey_session_navigation_next').addClass('d-none');
|
||||
self.$('.o_survey_leaderboard_buttons').removeClass('d-none');
|
||||
self.leaderBoard.showLeaderboard(false, false);
|
||||
});
|
||||
var nextQuestionPromise = this._rpc({
|
||||
route: _.str.sprintf('/survey/session/next_question/%s', self.surveyAccessToken),
|
||||
params: {
|
||||
'go_back': goBack,
|
||||
}
|
||||
}).then(function (result) {
|
||||
self.nextQuestion = result;
|
||||
if (self.refreshBackground && result.background_image_url) {
|
||||
return self._preloadBackground(result.background_image_url);
|
||||
} else {
|
||||
self.$('.o_survey_session_close').click();
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
Promise.all([fadeOutPromise, nextQuestionPromise]).then(function () {
|
||||
return self._onNextQuestionDone(goBack);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the screen with the next question's rendered template.
|
||||
*
|
||||
* @param {boolean} goBack Whether we are going back to the previous question or not
|
||||
*/
|
||||
_onNextQuestionDone: async function (goBack) {
|
||||
var self = this;
|
||||
|
||||
if (this.nextQuestion.question_html) {
|
||||
var $renderedTemplate = $(this.nextQuestion.question_html);
|
||||
this.$el.replaceWith($renderedTemplate);
|
||||
|
||||
// Ensure new question is fully loaded before force loading previous question screen.
|
||||
await this.attachTo($renderedTemplate);
|
||||
if (goBack) {
|
||||
// As we arrive on "question" screen, simulate going to the results screen or leaderboard.
|
||||
this._setShowInputs(true);
|
||||
this._setShowAnswers(true);
|
||||
if (this.sessionShowLeaderboard && this.isScoredQuestion) {
|
||||
this.currentScreen = 'leaderboard';
|
||||
this.leaderBoard.showLeaderboard(false, this.isScoredQuestion);
|
||||
} else {
|
||||
this.currentScreen = 'results';
|
||||
this._refreshResults();
|
||||
}
|
||||
} else {
|
||||
this._startTimer();
|
||||
}
|
||||
this.$el.fadeIn(this.fadeInOutTime);
|
||||
} else if (this.sessionShowLeaderboard) {
|
||||
// Display last screen if leaderboard activated
|
||||
this.isLastQuestion = true;
|
||||
this._setupLeaderboard().then(function () {
|
||||
self.$('.o_survey_session_leaderboard_title').text(_t('Final Leaderboard'));
|
||||
self.$('.o_survey_session_navigation_next').addClass('d-none');
|
||||
self.$('.o_survey_leaderboard_buttons').removeClass('d-none');
|
||||
self.leaderBoard.showLeaderboard(false, false);
|
||||
});
|
||||
} else {
|
||||
this.$('.o_survey_session_close').click();
|
||||
}
|
||||
|
||||
// Background Management
|
||||
if (this.refreshBackground) {
|
||||
$('div.o_survey_background').css("background-image", "url(" + this.nextQuestion.background_image_url + ")");
|
||||
$('div.o_survey_background').removeClass('o_survey_background_transition');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,22 @@ nav#oe_main_menu_navbar {
|
||||
/**********************************************************
|
||||
Common Style
|
||||
**********************************************************/
|
||||
// the survey background image takes all the page background, with a translucent white overlay (box-shadow)
|
||||
// When changing the background from one section to another, the overlay will become opaque to simulate a fade out of
|
||||
// the background image. This ensure a smooth transition from one background to another, likewise the question
|
||||
// transition.
|
||||
.o_survey_background {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
transition: box-shadow 0.3s ease-in-out;
|
||||
box-shadow: inset 0 0 0 10000px rgba(255,255,255,.7);
|
||||
background: no-repeat fixed center;
|
||||
background-size: cover;
|
||||
&.o_survey_background_transition {
|
||||
box-shadow: inset 0 0 0 10000px rgba(255,255,255,1);
|
||||
}
|
||||
}
|
||||
|
||||
.o_survey_wrap {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="scoring_type" invisible="1"/>
|
||||
<sheet>
|
||||
<div class="float-right d-flex flex-column text-center" attrs="{'invisible': [('is_page', '=', False)]}">
|
||||
<label for="background_image"/>
|
||||
<field name="background_image" widget="image" class="oe_avatar"/>
|
||||
</div>
|
||||
<div class="oe_title" style="width: 100%;">
|
||||
<label for="title" string="Section" attrs="{'invisible': [('is_page', '=', False)]}"/>
|
||||
<label for="title" string="Question" attrs="{'invisible': [('is_page', '=', True)]}"/>
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
<tree decoration-bf="is_page" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="title" widget="survey_description_page"/>
|
||||
<field name="background_image" invisible="1"/>
|
||||
<field name="question_type" />
|
||||
<field name="is_page" invisible="1"/>
|
||||
<field name="questions_selection" invisible="1"/>
|
||||
|
||||
@@ -8,7 +8,15 @@
|
||||
<t t-set="no_livechat" t-value="True"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='wrapwrap']" position="attributes">
|
||||
<attribute name="t-att-style" add="(('height: 100%; overflow: auto; background: url(' + '/survey/get_background_image/%s/%s' % (survey.access_token, answer.access_token) + ') no-repeat fixed center; box-shadow: inset 0 0 0 10000px rgba(255,255,255,.7); background-size: cover;') if survey and survey.background_image and answer else 'height: 100%; overflow: auto;')"/>
|
||||
<attribute name="t-att-style"
|
||||
add="(('background-image: url(' + question.background_image_url + ');')
|
||||
if question and question.background_image_url
|
||||
else ('background-image: url(' + page.background_image_url + ');')
|
||||
if page and page.background_image_url
|
||||
else ('background-image: url(' + survey.background_image_url + ');')
|
||||
if survey.background_image_url
|
||||
else '')"/>
|
||||
<attribute name="t-att-class" add="'o_survey_background'"/>
|
||||
</xpath>
|
||||
<xpath expr="//head/t[@t-call-assets][last()]" position="after">
|
||||
<t t-call-assets="survey.survey_assets" lazy_load="True"/>
|
||||
@@ -84,6 +92,11 @@
|
||||
|
||||
<template id="survey_fill_form" name="Survey: main page content">
|
||||
<t t-set="survey_form_readonly" t-value="answer.state == 'done'"/>
|
||||
<t t-set="background_image_url"
|
||||
t-value="question.background_image_url
|
||||
if question else page.background_image_url
|
||||
if page else survey.background_image_url
|
||||
if survey.background_image_url else False"/>
|
||||
<form role="form" method="post" t-att-name="survey.id"
|
||||
class="d-flex flex-grow-1 align-items-center"
|
||||
t-att-data-answer-token="answer.access_token"
|
||||
@@ -96,7 +109,8 @@
|
||||
t-att-data-is-page-description="bool(question and question.is_page and not is_html_empty(question.description))"
|
||||
t-att-data-questions-layout="survey.questions_layout"
|
||||
t-att-data-triggered-questions-by-answer="json.dumps(triggered_questions_by_answer)"
|
||||
t-att-data-selected-answers="json.dumps(selected_answers)">
|
||||
t-att-data-selected-answers="json.dumps(selected_answers)"
|
||||
t-att-data-refresh-background="any(page.background_image for page in survey.page_ids)">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="token" t-att-value="answer.access_token" />
|
||||
<div class="o_survey_error alert alert-danger d-none" role="alert">
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<!--Set question to false to avoid background miss match as last question is still in context and is used in survey.layout t-call.-->
|
||||
<t t-set="question" t-value="False"/>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,13 @@
|
||||
<t t-set="no_livechat" t-value="True"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='wrapwrap']" position="attributes">
|
||||
<attribute name="t-att-style" add="('height: 100%; overflow: auto; background: url(' + '/web/image/survey.survey/%s/background_image' % survey.id + ') no-repeat fixed center; box-shadow: inset 0 0 0 10000px rgba(255,255,255,.7); background-size: cover;') if survey and survey.background_image else 'height: 100%; overflow: auto;'"/>
|
||||
<attribute name="t-att-style"
|
||||
add="(('background-image: url(' + survey.session_question_id.background_image_url + ');')
|
||||
if survey.session_question_id and survey.session_question_id.background_image_url
|
||||
else ('background-image: url(' + survey.background_image_url + ');')
|
||||
if survey.background_image_url
|
||||
else '')"/>
|
||||
<attribute name="t-att-class" add="'o_survey_background'"/>
|
||||
</xpath>
|
||||
<xpath expr="//head/t[@t-call-assets][last()]" position="after">
|
||||
<t t-call-assets="survey.survey_assets" lazy_load="True"/>
|
||||
@@ -76,7 +82,8 @@
|
||||
t-att-data-is-last-question="is_last_question"
|
||||
t-att-data-current-screen="'question' if is_scored_question else 'userInputs'"
|
||||
t-att-data-show-bar-chart="show_bar_chart"
|
||||
t-att-data-show-text-answers="show_text_answers">
|
||||
t-att-data-show-text-answers="show_text_answers"
|
||||
t-att-data-refresh-background="any(page.background_image for page in survey.page_ids)">
|
||||
<div class="o_survey_question_header flex-wrap px-3 w-100 d-flex justify-content-between align-items-center position-absolute">
|
||||
<h3>
|
||||
<span>Go to <a t-att-href="survey.session_link" t-esc="survey.session_link" target="_blank" /></span>
|
||||
@@ -137,6 +144,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_survey_session_leaderboard w-100 flex-column flex-grow-1" style="display: none;">
|
||||
<!--Set question to false to avoid background miss match when no question are displayed.-->
|
||||
<t t-set="question" t-value="False"/>
|
||||
<div class="d-flex">
|
||||
<h1 class="o_survey_session_leaderboard_title flex-grow-1">
|
||||
<span t-if="is_last_question">Final Leaderboard</span>
|
||||
|
||||
Reference in New Issue
Block a user