[IMP] website_slides,_survey: allow certification creation at frontend

Purpose
=======

Before this commit, in the front end, you had to create a certification before
adding it to a course. This commit allows creation of certification in
front-end with pre-filled settings.

Functional Spec
===============

1. On a course, add content, type certification, you can choose an existing one
   (normal flow) or create a new one in the dropdown by typing a new survey
   name (new flow).
3. When submitting (Publish or save as draft), a certification survey, with
   pre-filled settings is created and linked to the slide.
   Pre-filled settings:
        * One page per question
        * Attempts limit : 1
        * Time limit : no limit
        * Scoring without answers
        * Passing score : 70%
        * Is Certification : True
        * Email Template : Certification: Send by email
        * Give Badge : False
4. After submit, the user stays on the Course Content.
5. A toaster is displayed in the top right saying:
    Toaster's Title: Certification created
    Content: Follow this link to add questions to your certification.
    <a>Edit certification</a>

A website editor can select a certification (if available) for the creation of
a slide.

Task id: 1981399
PR #43577

Co-Authored-By: Florent Lejoly <fle@odoo.com>
Co-Authored-By: David Beguin <dbe@odoo.com>
This commit is contained in:
Florent Lejoly
2020-04-16 10:26:32 +00:00
committed by Thibault Delavallée
co-authored by David Beguin
parent dd53c515c8
commit 24033e4aaf
6 changed files with 167 additions and 27 deletions
@@ -1,13 +1,67 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import http
from odoo import http, _
from odoo.http import request
from odoo.exceptions import AccessError
from odoo.addons.website_slides.controllers.main import WebsiteSlides
class WebsiteSlidesSurvey(http.Controller):
class WebsiteSlidesSurvey(WebsiteSlides):
@http.route(['/slides_survey/certification/search_read'], type='json', auth='user', methods=['POST'], website=True)
def slides_certification_search_read(self, fields):
can_create = request.env['survey.survey'].check_access_rights('create', raise_exception=False)
return {
'read_results': request.env['survey.survey'].search_read([('certification', '=', True)], fields),
'can_create': can_create,
}
# ------------------------------------------------------------
# Overrides
# ------------------------------------------------------------
@http.route(['/slides/add_slide'], type='json', auth='user', methods=['POST'], website=True)
def create_slide(self, *args, **post):
create_new_survey = post['slide_type'] == "certification" and post.get('survey') and not post['survey']['id']
linked_survey_id = int(post.get('survey', {}).get('id') or 0)
if create_new_survey:
# If user cannot create a new survey, no need to create the slide either.
if not request.env['survey.survey'].check_access_rights('create', raise_exception=False):
return {'error': _('You are not allowed to create a survey.')}
# Create survey first as certification slide needs a survey_id (constraint)
post['survey_id'] = request.env['survey.survey'].create({
'title': post['survey']['title'],
'background_image': post['image_1920'],
'questions_layout': 'page_per_question',
'is_attempts_limited': True,
'attempts_limit': 1,
'is_time_limited': False,
'scoring_type': 'scoring_without_answers',
'certification': True,
'scoring_success_min': 70.0,
'certification_mail_template_id': request.env.ref('survey.mail_template_certification').id,
}).id
elif linked_survey_id:
try:
request.env['survey.survey'].browse([linked_survey_id]).read(['title'])
except AccessError:
return {'error': _('You are not allowed to link a certification.')}
post['survey_id'] = post['survey']['id']
# Then create the slide
result = super(WebsiteSlidesSurvey, self).create_slide(*args, **post)
if create_new_survey:
# Set the redirect_url used in toaster
action_id = request.env.ref('survey.action_survey_form').id
result.update({
'redirect_url': '/web#id=%s&action=%s&model=survey.survey&view_type=form' % (post['survey_id'], action_id),
'redirect_to_certification': True
})
return result
@@ -0,0 +1,39 @@
odoo.define('website_slides_survey.certification_upload_toast', function (require) {
'use strict';
var publicWidget = require('web.public.widget');
var sessionStorage = window.sessionStorage;
var core = require('web.core');
var _t = core._t;
publicWidget.registry.CertificationUploadToast = publicWidget.Widget.extend({
selector: '.o_wslides_survey_certification_upload_toast',
/**
* @override
*/
start: function () {
var self = this;
this._super.apply(this, arguments).then(function () {
var url = sessionStorage.getItem("survey_certification_url");
if (url) {
self.displayNotification({
type: 'info',
title: _t('Certification created'),
message: _.str.sprintf(
_t('Follow this link to add questions to your certification. <a href="%s">Edit certification</a>'),
url
),
sticky: true,
});
sessionStorage.removeItem("survey_certification_url");
}
});
},
});
return publicWidget.registry.CertificationUploadToast;
});
@@ -3,12 +3,34 @@ odoo.define('website_slides_survey.upload_modal', function (require) {
var core = require('web.core');
var _t = core._t;
var sessionStorage = window.sessionStorage;
var SlidesUpload = require('website_slides.upload_modal');
/**
* Management of the new 'certification' slide_type
*/
SlidesUpload.SlideUploadDialog.include({
events: _.extend({}, SlidesUpload.SlideUploadDialog.prototype.events || {}, {
'change input#certification_id': '_onChangeCertification'
}),
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* Will automatically set the title of the slide to the title of the chosen certification
*/
_onChangeCertification: function (ev) {
if (ev.added && ev.added.text) {
this.$("input#name").val(ev.added.text);
}
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Overridden to add the "certification" slide type
*
@@ -56,7 +78,7 @@ SlidesUpload.SlideUploadDialog.include({
var result = this._super.apply(this, arguments);
var $certificationInput = this.$('#certification_id');
if ($certificationInput.length !== 0){
if ($certificationInput.length !== 0) {
var $select2Container = $certificationInput
.closest('.form-group')
.find('.select2-container');
@@ -76,15 +98,38 @@ SlidesUpload.SlideUploadDialog.include({
* @override
* @private
*/
_getSelect2DropdownValues: function (){
_getSelect2DropdownValues: function () {
var result = this._super.apply(this, arguments);
var certificateValue = this.$('#certification_id').select2('data');
var survey = {};
if (certificateValue) {
result['survey_id'] = certificateValue.id;
if (certificateValue.create) {
survey.id = false;
survey.title = certificateValue.text;
} else {
survey.id = certificateValue.id;
}
}
result['survey'] = survey;
return result;
}
},
/**
* Overridde to handle certification created on-the-fly: toaster will hold
* survey edit url, need to put it in session to use it in CertificationUploadToast
*
* @override
* @private
*/
_onFormSubmitDone: function (data) {
if (!data.error && data.redirect_to_certification) {
sessionStorage.setItem("survey_certification_url", data.redirect_url);
window.location.reload();
} else {
this._super.apply(this, arguments);
}
},
});
SlidesUpload.websiteSlidesUpload.include({
@@ -3,33 +3,28 @@
<t t-name="website.slide.upload.modal.certification">
<div>
<form class="clearfix">
<div class="form-group row">
<label for="certification_id" class="col-form-label col-md-3">Certification</label>
<div class="controls col-md-9">
<input class="form-control" id="certification_id" required="required"/>
<div class="row">
<div class="col" id="o_wslides_js_slide_upload_left_column">
<div class="form-group">
<label for="certification_id" class="col-form-label col-md-3">Certification</label>
<input class="form-control" id="certification_id" required="required"/>
</div>
<div class="form-group">
<label for="upload" class="col-form-label">Choose an Image</label>
<input id="upload" class="form-control" data-prevent-onchange='true' name="file" accept="image/*" type="file" required="required"/>
</div>
<t t-call="website.slide.upload.modal.common"/>
<canvas id="data_canvas" class="d-none"></canvas>
</div>
</div>
<t t-call="website.slide.upload.modal.common"/>
<div class="form-group row">
<div class="col-md-4">
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6 d-none">
<div class="img-thumbnail">
<div class="o_slide_preview">
<img src="/website_slides/static/src/img/document.png" id="slide-image" title="Content Preview" alt="Content Preview" class="img-fluid"/>
</div>
</div>
</div>
<div class="col-md-8">
<ul class="list-group">
<li class="list-group-item">
<h5 class="list-group-item-heading">
<label for="upload" class="col-form-label">Cover Image File</label>
</h5>
<input id="upload" name="file" class="form-control" accept="image/*" type="file" required="required" data-prevent-onchange="1"/>
</li>
</ul>
</div>
</div>
<canvas id="data_canvas" class="d-none"></canvas>
</div>
</div>
</form>
</div>
</t>
@@ -8,6 +8,7 @@
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/website_slides_survey/static/src/js/slides_upload.js"/>
<script type="text/javascript" src="/website_slides_survey/static/src/js/slides_course_fullscreen_player.js"/>
<script type="text/javascript" src="/website_slides_survey/static/src/js/slides_certification_upload_toast.js"/>
</xpath>
</template>
</data>
@@ -32,5 +32,11 @@
<attribute name="t-if">channel.can_upload and not slide.question_ids and slide.slide_type != 'certification'</attribute>
</xpath>
</template>
<template id="course_slides_list_inherit_survey" inherit_id="website_slides.course_slides_list">
<xpath expr="//div[hasclass('o_wslides_content_actions')]" position="inside">
<div class="o_wslides_survey_certification_upload_toast"/>
</xpath>
</template>
</data>
</odoo>