[MERGE][IMP] survey: clarify live session links flow

Specifications

- Handle the situation in which the quick access
link is used but the live session is not launched yet, by showing an
appropriate error message and by allowing a user with sufficient rights
to launch the live session directly from the quick access page.

- Make the live session's "go to" url in the presentation mode non-clickable
so that the presenter can not join the session as an attendee as it messes
up, and change the copy icon next to the url to a copy button, which is more
explicit.

Task-2816685

closes odoo/odoo#88434

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2022-06-08 15:45:45 +02:00
committed by GitHub
6 changed files with 61 additions and 54 deletions
@@ -19,21 +19,18 @@ class UserInputSession(http.Controller):
return request.env['survey.survey'].search([('access_token', '=', survey_token)])
def _fetch_from_session_code(self, session_code):
""" Matches a survey against a passed session_code.
We don't limit the session_state to be reachable (ready / in_progress) here because
in some cases, we want closed session as well (where session_state = False).
Instead, when necessary, the reachability is forced in routes calling this method to
avoid people using those routes to access other (private) surveys.
We limit to sessions opened within the last 7 days to avoid potential abuses. """
if session_code:
matching_survey = request.env['survey.survey'].sudo().search([
('session_start_time', '>', fields.Datetime.now() - relativedelta(days=7)),
('session_code', '=', session_code),
], limit=1)
if matching_survey:
return matching_survey
return False
""" Matches a survey against a passed session_code, and checks if it is valid.
If it is valid, returns the start url. Else, the error type."""
if not session_code:
return None, {'error': 'survey_wrong'}
survey = request.env['survey.survey'].sudo().search([('session_code', '=', session_code)], limit=1)
if not survey or survey.certification:
return None, {'error': 'survey_wrong'}
if survey.session_state in ['ready', 'in_progress']:
return survey, None
if request.env.user.has_group("survey.group_survey_user"):
return None, {'error': 'survey_session_not_launched', 'survey_id': survey.id}
return None, {'error': 'survey_session_not_launched'}
# ------------------------------------------------------------
# SURVEY SESSION MANAGEMENT
@@ -176,28 +173,24 @@ class UserInputSession(http.Controller):
@http.route('/s/<string:session_code>', type='http', auth='public', website=True)
def survey_start_short(self, session_code):
"""" Redirects to 'survey_start' route using a shortened link & token.
We match the session_code for open surveys.
Shows an error message if the survey is not valid.
This route is used in survey sessions where we need short links for people to type. """
survey, survey_error = self._fetch_from_session_code(session_code)
survey = self._fetch_from_session_code(session_code)
if survey and survey.session_state in ['ready', 'in_progress']:
return request.redirect("/survey/start/%s" % survey.access_token)
return request.redirect("/s")
if survey_error:
return request.render('survey.survey_session_code',
dict(**survey_error, session_code=session_code))
return request.redirect(survey.get_start_url())
@http.route('/survey/check_session_code/<string:session_code>', type='json', auth='public', website=True)
def survey_check_session_code(self, session_code):
""" Checks if the given code is matching a survey session_code.
If yes, redirect to /s/code route.
If not, return error. The user is invited to type again the code. """
survey = self._fetch_from_session_code(session_code)
if survey:
if survey.session_state in ['ready', 'in_progress']:
return {"survey_url": "/survey/start/%s" % survey.access_token}
else:
return {"error": "survey_session_closed"}
return {"error": "survey_wrong"}
If not, return error. The user is invited to type again the code."""
survey, survey_error = self._fetch_from_session_code(session_code)
if survey_error:
return survey_error
return {'survey_url': survey.get_start_url()}
def _prepare_manage_session_values(self, survey):
is_first_question, is_last_question = False, False
@@ -8,9 +8,10 @@ publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({
events: {
'click button[type="submit"]': '_onSubmit',
'input #session_code': '_onSessionCodeInput',
'click .o_survey_launch_session': '_onLaunchSessionClick',
},
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Widget
//--------------------------------------------------------------------------
@@ -23,9 +24,7 @@ publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({
// Init event listener
if (!self.readonly) {
$(document).on('keypress', self._onKeyPress.bind(self));
}
self.$('input').focus();
}
});
},
@@ -36,8 +35,19 @@ publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({
// Handlers
// -------------------------------------------------------------------------
_onLaunchSessionClick: async function () {
const sessionResult = await this._rpc({
'model': 'survey.survey',
'method': 'action_start_session',
'args': [[this.$('.o_survey_launch_session').data('surveyId')]],
});
window.location = sessionResult.url;
},
_onSessionCodeInput: function () {
this.el.querySelectorAll('.o_survey_error > span').forEach((elem) => elem.classList.add('d-none'));
this.$('.o_survey_launch_session').addClass('d-none');
this.$('button[type="submit"]').removeClass('d-none');
},
_onKeyPress: function (event) {
@@ -54,10 +64,10 @@ publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({
_submitCode: function () {
var self = this;
this.$('.o_survey_error > span').addClass("d-none");
this.$('.o_survey_error > span').addClass('d-none');
const sessionCodeInputVal = this.$('input#session_code').val().trim();
if (!sessionCodeInputVal) {
self.$('.o_survey_session_error_invalid_code').removeClass("d-none");
self.$('.o_survey_session_error_invalid_code').removeClass('d-none');
return;
}
this._rpc({
@@ -66,10 +76,15 @@ publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({
if (response.survey_url) {
window.location = response.survey_url;
} else {
if (response.error && response.error === 'survey_session_closed') {
self.$('.o_survey_session_error_closed').removeClass("d-none");
if (response.error && response.error === 'survey_session_not_launched') {
self.$('.o_survey_session_error_not_launched').removeClass('d-none');
if ("survey_id" in response) {
self.$('button[type="submit"]').addClass('d-none');
self.$('.o_survey_launch_session').removeClass('d-none');
self.$('.o_survey_launch_session').data('surveyId', response.survey_id);
}
} else {
self.$('.o_survey_session_error_invalid_code').removeClass("d-none");
self.$('.o_survey_session_error_invalid_code').removeClass('d-none');
}
}
});
@@ -51,6 +51,8 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre
self.stopNextQuestion = false;
// Background Management
self.refreshBackground = self.$el.data('refreshBackground');
// Copy link tooltip
self.$('.o_survey_session_copy').tooltip({delay: 0, title: 'Click to copy link', placement: 'right'});
var isRpcCall = self.$el.data('isRpcCall');
if (!isRpcCall) {
@@ -367,11 +367,6 @@ _::-webkit-full-page-media, _:future, :root .o_survey_wrap {
.o_survey_session_copy {
cursor: pointer;
opacity: .75;
transition: opacity .3s ease;
&:hover {
opacity: 1;
}
}
}
@@ -118,16 +118,20 @@
</div>
<div class="row">
<div class="col-12 col-md-4 offset-md-4 text-center">
<input id="session_code" type="text" placeholder="e.g. 4812" autocomplete="off"
class="form-control o_survey_question_text_box font-weight-bold bg-transparent text-primary text-center rounded-0 p-2 w-100"/>
<input id="session_code"
type="text" placeholder="e.g. 4812"
autocomplete="off"
t-attf-value="{{session_code if session_code else ''}}"
class="form-control o_survey_question_text_box font-weight-bold bg-transparent text-primary text-center rounded-0 p-2 w-100"/>
</div>
<div class="col-12 col-md-4 offset-md-4 text-center o_survey_error text-danger pt-2" role="alert">
<span class="o_survey_session_error_invalid_code d-none">Code is incorrect.</span>
<span class="o_survey_session_error_closed d-none">Session is finished.</span>
<div class="col-12 col-md-4 offset-md-4 text-center o_survey_error text-danger h4 pt-2 px-0" role="alert">
<span t-attf-class="o_survey_session_error_invalid_code {{'d-none' if error != 'survey_wrong' else ''}}">Oops! No survey matches this code.</span>
<span t-attf-class="o_survey_session_error_not_launched {{'d-none' if error != 'survey_session_not_launched' else ''}}">The session did not start yet.</span>
</div>
</div>
<div class="text-center mt32 p-2">
<button type="submit" class="btn btn-primary">Join Session</button>
<button groups="survey.group_survey_user" type="button" t-attf-class="o_survey_launch_session btn btn-primary {{'d-none' if error != 'survey_session_not_launched' else ''}}" t-att-data-survey-id="survey_id">Launch Session</button>
<button type="submit" t-attf-class="btn btn-primary {{'d-none' if error == 'survey_session_not_launched' and request.env.user.has_group('survey.group_survey_user') else ''}}">Join Session</button>
</div>
</div>
</div>
@@ -38,8 +38,7 @@
<div class="text-center">
<h1 class="mb-4" t-field="survey.title" />
<h2 class="mb-5 font-weight-normal">
<span class="text-break">Go to <a t-att-href="survey.session_link" t-esc="survey.session_link" target="_blank" /></span>
<i class="fa fa-link font-weight-normal ml-3 o_survey_session_copy" />
<span class="text-break">To join: <span class="text-info o_survey_session_copy" t-esc="survey.session_link"/></span>
<input class="o_survey_session_copy_url d-none" type="text" t-att-value="survey.session_link" />
</h2>
<h2 class="font-weight-normal"><span>Waiting for attendees...</span>
@@ -87,8 +86,7 @@
t-att-data-is-session-closed="is_session_closed">
<div t-if="not is_session_closed" 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>
<i class="fa fa-copy font-weight-normal ml-3 mr-1 o_survey_session_copy" />
<span>To join: <span class="text-info o_survey_session_copy" t-esc="survey.session_link"/></span>
<input class="o_survey_session_copy_url d-none" type="text" t-att-value="survey.session_link" />
</h3>
<h1 t-if="question.is_time_limited" class="o_survey_timer_container">