diff options
Diffstat (limited to 'addons/website_slides/static/src/js')
9 files changed, 1246 insertions, 7 deletions
diff --git a/addons/website_slides/static/src/js/slides_category.js b/addons/website_slides/static/src/js/slides_category.js new file mode 100644 index 00000000000..d1d7efb7747 --- /dev/null +++ b/addons/website_slides/static/src/js/slides_category.js @@ -0,0 +1,148 @@ +odoo.define('website_slides.add.section', function (require) { + 'use strict'; + + var sAnimations = require('website.content.snippets.animation'); + var core = require('web.core'); + var Widget = require('web.Widget'); + + var _t = core._t; + + var SectionDialog = Widget.extend({ + template: 'website.slide.add.section', + events: { + 'hidden.bs.modal': 'destroy', + 'click button.save': '_save', + 'click button[data-dismiss="modal"]': '_cancel', + 'change input#upload': '_slideUpload', + 'change input#url': '_slideUrl', + 'click .list-group-item': function (ev) { + this.$('.list-group-item').removeClass('active'); + $(ev.target).closest('li').addClass('active'); + } + }, + + /** + * @override + * @param {Object} el + * @param {number} channel_id + */ + init: function (el, channelID) { + this._super(el, channelID); + this.channel_id = parseInt(channelID, 10); + this.index_content = ''; + }, + /** + * @override + */ + start: function () { + this.$el.modal({ + backdrop: 'static' + }); + + return this._super.apply(this, arguments); + }, + _getValue: function () { + var canvas = this.$('#data_canvas')[0], + values = { + 'channel_id': this.channel_id || '', + 'url': this.$('#url').val(), + 'name': this.$('#section_name').val() + }; + return values; + }, + /** + * @private + */ + _validate: function () { + this.$('.form-group').removeClass('o_has_error').find('.form-control, .custom-select').removeClass('is-invalid'); + if (!this.$('#name').val()) { + this.$('#name').closest('.form-group').addClass('o_has_error').find('.form-control, .custom-select').addClass('is-invalid'); + return false; + } + var url = this.$('#url').val() ? this.is_valid_url : false; + if (!(this.file.name || url)) { + this.$('#url').closest('.form-group').addClass('o_has_error').find('.form-control, .custom-select').addClass('is-invalid'); + return false; + } + return true; + }, + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @override + * @param {Object} ev + */ + _save: function (ev) { + var self = this; + + var values = this._getValue(); + if ($(ev.target).data('published')) { + values.website_published = true; + } + this.$('.oe_slides_upload_loading').show(); + this.$('.modal-footer, .modal-body').hide(); + this._rpc({ + route: '/slides/add_category', + params: values, + }).then(function (data) { + if (data.error) { + self._displayAlert(data.error); + self.$('.oe_slides_upload_loading').hide(); + self.$('.modal-footer, .modal-body').show(); + + } else { + window.location = data.url; + } + }); + }, + /** + * @override + */ + _cancel: function () { + this.trigger('cancel'); + }, + + }); + + sAnimations.registry.websiteSlidesSection = sAnimations.Class.extend({ + selector: '.oe_slide_js_add_section', + xmlDependencies: ['/website_slides/static/src/xml/website_slides_upload.xml'], + read_events: { + 'click': '_onAddSectionClick', + }, + + /** + * @override + */ + start: function () { + // Automatically open the upload dialog if requested from query string + if ($.deparam.querystring().enable_slide_upload !== undefined) { + this._openDialog(this.$el.attr('channel_id')); + } + return this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + _openDialog: function (channelID) { + new SectionDialog(this, channelID).appendTo(document.body); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {Event} ev + */ + _onAddSectionClick: function (ev) { + console.log('test'); + this._openDialog($(ev.currentTarget).attr('channel_id')); + }, + }); + });
\ No newline at end of file diff --git a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js new file mode 100644 index 00000000000..1da173a2f46 --- /dev/null +++ b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js @@ -0,0 +1,427 @@ +var onYouTubeIframeAPIReady; + +odoo.define('website_slides.fullscreen', function (require) { + 'use strict'; + var sAnimations = require('website.content.snippets.animation'); + var Widget = require('web.Widget'); + var core = require('web.core'); + var QWeb = core.qweb; + + var tag = document.createElement('script'); + + var QuizWidget = require('website_slides.quiz'); + + tag.src = "https://www.youtube.com/iframe_api"; + var firstScriptTag = document.getElementsByTagName('script')[0]; + firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); + + + var Fullscreen = Widget.extend({ + /** + * @override + * @param {Object} el + * @param {Object} data holding channelId and optionally upload and publish control parameters + */ + init: function(el, course_id,slide_id, user_id){ + this._super.apply(this,arguments); + this.courseID = parseInt(course_id, 10); + this.slideID = parseInt(slide_id, 10); + this.userID = parseInt(user_id, 10); + this.course = undefined; + this.slide = undefined; + this.slides = []; + this.nextSlide = undefined; + this.previousSlide = undefined; + this.url = undefined; + this.urlToSmallScreen = undefined; + this.activetab = undefined; + this.player = undefined; + this.goToQuiz = false; + this.answeredQuestions = []; + this.slideTitle = undefined; + }, + start: function(){ + var self = this; + this.url = window.location.pathname; + this.urlToSmallScreen = this.url.replace('/fullscreen',''); + this._getSlides(); + this._renderPlayer(); + this._bindListEvents(); + + $(document).keydown(this._onKeyDown.bind(this)); + return this._super.apply(this, arguments); + }, + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + /** + * @private + * Renders the player accordingly to the current slide + */ + _renderPlayer: function(){ + var self = this; + var embed_url; + if(self.slide.slide_type !== 'webpage' || self.slide.htmlContent){ + if((self.slide.slide_type == "quiz" || self.slide.hasQuiz) && !self.slide.quiz){ + self._fetchQuiz(); + } else { + embed_url = $(this.slide.embed_code).attr('src'); + if (self.slide.slide_type === "video"){ + embed_url = "https://" + embed_url + "&rel=0&autoplay=1&enablejsapi=1&origin=" + window.location.origin; + } + $('.o_wslides_fullscreen_player').html(QWeb.render('website.slides.fullscreen', { + slide: self.slide, + nextSlide: self.nextSlide, + questions: self.slide.quiz ? self.slide.quiz.questions: '', + reward: self.slide.quiz ? self.slide.quiz.nb_attempts < 3 ? self.slide.quiz.possible_rewards[self.slide.quiz.nb_attempts] : self.slide.quiz.possible_rewards[3]: self.slide.maxPoints, + embed_url: embed_url, + question_count: self.slide.quiz ? self.slide.quiz.questions.length : '', + letters: self.slide.quiz ? self.letters : '', + showMiniQuiz: self.goToQuiz + })); + if(self.slide.slide_type === "video"){ + self._renderYoutubeIframe(); + } + if(self.slide.slide_type == 'webpage'){ + self._renderWebpage(); + } + if((self.slide.slide_type == "presentation" || self.slide.slide_type == "document" || self.slide.slide_type == "infographic" || self.slide.slide_type == "webpage") && !self.slide.quiz){ + self._setSlideStateAsDone(); + } + if ((self.slide.quiz && self.slide.slide_type === "quiz") || self.goToQuiz){ + self._renderQuiz(); + } + } + } else { + self._fetchHtmlContent(); + } + self._renderTitle(); + }, + _renderYoutubeIframe: function(){ + var self = this; + /** + * Due to issues of synchronization between the youtube api script and the widget's instanciation. + */ + try{ + self._setupYoutubePlayer(); + } + catch { + onYouTubeIframeAPIReady = function(){ + var self = this; + self._setupYoutubePlayer(); + }.bind(this) + } + }, + _renderWebpage: function(){ + var self = this; + $(self.slide.htmlContent).appendTo('.o_wslides_fullscreen_webpage_content'); + }, + _renderQuiz: function(){ + var self = this; + var Quiz = new QuizWidget(this, self.slide); + Quiz.appendTo('.o_wslides_fullscreen_player'); + $('.next-slide').click(function(){ + self._goToNextSlide(); + }) + $('.back-to-video').click(function(){ + self.goToQuiz = false; + self._renderPlayer(); + }); + }, + _renderTitle: function(){ + var self = this; + $('.o_wslides_fullscreen_slide_title').empty().html(QWeb.render('website.course.fullscreen.title', { + slide: self.slide, + miniQuiz: self.goToQuiz + })); + }, + /** + * @private + * Links the youtube api to the iframe present in the template + */ + _setupYoutubePlayer: function(){ + var self = this; + self.player = new YT.Player('youtube-player', { + host: 'https://www.youtube.com', + playerVars: {'autoplay': 1, 'origin': window.location.origin}, + autoplay: 1, + events: { + 'onReady': self._onPlayerReady, + 'onStateChange': this._onPlayerStateChange.bind(self) + } + }); + }, + /** + * @param {*} event + * Specific method of the youtube api. + * Whenever the player starts playing, a setinterval is created. + * This setinterval is used to check te user's progress in the video. + * Once the user reaches a particular time in the video, the slide will be considered as completed if the video doesn't have a mini-quiz. + * This method also allows to automatically go to the next slide (or the quiz associated to the current video) once the video is over + */ + _onPlayerStateChange: function(event){ + var self = this; + var tid; + clearInterval(self.tid); + if (event.data == YT.PlayerState.PLAYING && !self.slide.done) { + self.tid = setInterval(function(){ + if(event.target.getCurrentTime){ + var currentTime = event.target.getCurrentTime(); + var totalTime = event.target.getDuration(); + if(totalTime && currentTime > totalTime - 30){ + clearInterval(self.tid); + if(!self.slide.hasQuiz && !self.slide.done){ + self.slide.done = true; + self._setSlideStateAsDone(); + } + } + } + }, 1000); + } + if(event.data == YT.PlayerState.ENDED){ + self.player = undefined; + self._goToNextSlide(); + } + }, + /** + * @private + * Creates slides objects from every slide-list-cells attributes + */ + _getSlides: function(){ + var self = this; + var slides = $('.o_wslides_fullscreen_sidebar_slide_tab'); + for(var i = 0; i < slides.length;i++){ + var slide = $(slides[i]); + self.slides.push({ + id: parseInt(slide.attr('slide_id'), 10), + name: slide.attr('slide_name'), + embed_code: slide.attr('slide_embed_code'), + slide_type: slide.attr('slide_type'), + done: slide.attr('done'), + hasQuiz: slide.attr('quiz'), + slug: slide.attr('slide_slug'), + htmlContent: undefined + }); + this._getActiveSlide(); + } + }, + /** + * @private + * @param {object} slide + * Fetch the quiz for a particular slide + */ + _fetchQuiz: function(){ + var self = this; + self._rpc({ + route:"/slide/quiz/get", + params: { + 'slide_id': self.slide.id + } + }).then(function(data){ + if(data){ + self.slide.quiz = data; + self._renderPlayer(); + } + }) + }, + _fetchHtmlContent: function(){ + var self = this; + self._rpc({ + route:"/slide/html_content/get", + params: { + 'slide_id': self.slide.id + } + }).then(function(data){ + self.slide.htmlContent = data.html_content; + self._renderPlayer(); + }) + }, + /** + * @private + * Once the completion conditions are filled, + * sends a json request to the backend to set the relation between the slide and the user as being completed + */ + _setSlideStateAsDone: function(){ + var self = this; + self._rpc({ + route: '/slides/set_completed', + params: { + slide_id: self.slide.id, + } + }).then(function(data){ + $('#check-'+self.slide.id).replaceWith($('<i class="check-done o_wslides_slide_completed fa fa-check-circle"></i>')) + self.slide.done = true; + clearInterval(self.tid); + self.channelCompletion = data.channel_completion; + self._updateProgressbar(); + }); + }, + _updateProgressbar: function(){ + var self = this; + var completion = self.channelCompletion <= 100 ? self.channelCompletion : 100; + $('.o_wslides_fullscreen_sidebar_progress_gauge').css('width', completion + "%" ); + $('.o_wslides_progress_percentage').text(completion); + }, + /** + * @private + * Creates an array of letters to be used in the quiz with a foreach + */ + _generateQuizLetters: function(){ + var letters = []; + for(var i = 65; i < 91; i++){ + letters.push(String.fromCharCode(i)); + } + return letters; + }, + _goToNextSlide: function(){ + var self = this; + clearInterval(self.tid); + self.player = undefined; + self.goToQuiz = self.slide.hasQuiz && !self.goToQuiz; + if(self.nextSlide && !self.goToQuiz){ + self.slide = self.nextSlide; + self.index++; + self._setActiveTab(); + self._renderPlayer(); + self._setPreviousAndNextSlides(); + self._updateUrl(); + history.pushState(null,'',self.url); + } + else if(self.nextSlide){ + self._renderPlayer(); + } + }, + _goToPreviousSlide: function(){ + var self = this; + clearInterval(self.tid); + self.goToQuiz = false; + self.player = undefined; + if(self.previousSlide){ + self.slide = self.previousSlide; + self.index--; + self._setActiveTab(); + self._renderPlayer(); + self._setPreviousAndNextSlides(); + self._updateUrl(); + history.pushState(null,'',self.url); + } + }, + _setPreviousAndNextSlides: function(){ + var self = this; + self.previousSlide = self.index > 0 ? self.slides[self.index-1] : undefined; + self.nextSlide = self.index < (self.slides.length - 1) ? self.slides[self.index+1] : undefined; + }, + /** + * Changes the url whenever the user changes slides. + * This allows the user to refresh the page and stay at the right video + */ + _updateUrl: function(){ + var self = this; + var url = window.location.pathname.split('/'); + url[url.length-1] = self.slide.slug; + url = url.join('/'); + self.url = url; + self.urlToSmallScreen = self.url; + self.url += "?fullscreen=1"; + $('.o_wslides_small_screen').attr('href', self.urlToSmallScreen); + }, + /** + * Whenever the user changes slide, change the active tab + */ + _setActiveTab: function(){ + var self = this; + self.activeTab.removeClass('active'); + $('li.active').removeClass('active'); + $('li[slide_id='+self.slide.id+']').addClass('active'); + self.activeTab = $('.o_wslides_fullscreen_sidebar_slide_tab[index="'+self.index+'"]') + self.activeTab.addClass('active'); + }, + /** + * The first time the user gets on the player, + * get the slide that is represented by the active tab in the sidebar + */ + _getActiveSlide: function(){ + var self = this; + self.activeTab = $('.o_wslides_fullscreen_sidebar_slide_tab.active'); + self.index = parseInt(self.activeTab.attr('index'), 10); + self.slide = self.slides[self.index]; + self._setPreviousAndNextSlides(); + }, + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + _onListCellClick: function(ev){ + var self = this; + clearInterval(self.tid); + self.player = undefined; + var target = $(ev.currentTarget); + self.goToQuiz = false; + if(target[0] !== self.activeTab[0]){ + self.activeTab.removeClass('active'); + target.addClass('active'); + self.index = parseInt(target.attr('index')); + self._getActiveSlide(); + self._renderPlayer(); + $('li.active').removeClass('active'); + $('li[slide_id='+self.slide.id+']').addClass('active'); + self._setPreviousAndNextSlides(); + self._updateUrl(); + history.pushState(null,'',self.url); + } + }, + _onMiniQuizClick: function(ev){ + var self = this; + self.index = parseInt($(ev.currentTarget).attr('index')); + self.slide = self.slides[self.index]; + self.goToQuiz = true; + self._setPreviousAndNextSlides(); + self._renderPlayer(); + self._setActiveTab(); + self._updateUrl(); + history.pushState(null,'' ,self.url); + }, + /** + * @private + * Binds events related to the list + */ + _bindListEvents: function(){ + var self = this; + $('.o_wslides_fullscreen_sidebar_slide_tab').each(function () { + $(this).click(self._onListCellClick.bind(self)); + }); + + $('.o_wslides_slide_quiz ').each(function(){ + $(this).click(self._onMiniQuizClick.bind(self)); + }) + }, + _onKeyDown: function(ev){ + var self = this; + switch(ev.key){ + case "ArrowRight": + self._goToNextSlide(); + break; + case "ArrowLeft": + self._goToPreviousSlide(); + break; + } + }, + }) + + sAnimations.registry.websiteSlidesFullscreenPlayer = Widget.extend({ + selector: '.oe_js_course_slide', + xmlDependencies: ['/website_slides/static/src/xml/website_slides_fullscreen.xml'], + init: function(el){ + this._super.apply(this, arguments); + }, + start: function(){ + this._super.apply(this, arguments); + var user_id = this.$el.attr('user_id'); + var course_id = this.$el.attr('course_id'); + var slide_id = this.$el.attr('slide_id'); + var fullscreen = new Fullscreen(this, course_id, slide_id, user_id); + fullscreen.appendTo(".oe_js_course_slide"); + } + }); + + return Fullscreen; +});
\ No newline at end of file diff --git a/addons/website_slides/static/src/js/slides_course_progress_bar.js b/addons/website_slides/static/src/js/slides_course_progress_bar.js new file mode 100644 index 00000000000..2412368018f --- /dev/null +++ b/addons/website_slides/static/src/js/slides_course_progress_bar.js @@ -0,0 +1,47 @@ +odoo.define('website_slides.progress.bar', function (require) { + + var sAnimations = require('website.content.snippets.animation'); + var Widget = require('web.Widget'); + + var ProgressBar = Widget.extend({ + + /** + * @override + * @param {Object} el + * @param {number} channel_id + */ + init: function (el, completion) { + this._super(el); + this.completion = completion; + }, + /** + * @override + */ + start: function () { + this._renderProgressBar(); + return this._super.apply(this, arguments); + }, + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + _renderProgressBar: function (ev) { + var self = this; + $('.oe_slide_js_progress_bar').css('width', (self.completion <= 100 ? self.completion : 100) + '%'); + }, + }); + + sAnimations.registry.websiteSlidesProgressBar = sAnimations.Class.extend({ + selector: '.oe_slide_js_progress_bar', + /** + * @override + */ + start: function () { + var completion = parseInt($('.oe_slide_js_progress_bar').attr('channel_completion')); + var progressBar = new ProgressBar(this, completion); + progressBar.appendTo(".oe_slide_js_progress_bar"); + return this._super.apply(this, arguments); + }, + }); + + return ProgressBar; + }); diff --git a/addons/website_slides/static/src/js/slides_course_quiz.js b/addons/website_slides/static/src/js/slides_course_quiz.js new file mode 100644 index 00000000000..f98d5b3ed8a --- /dev/null +++ b/addons/website_slides/static/src/js/slides_course_quiz.js @@ -0,0 +1,206 @@ +odoo.define('website_slides.quiz', function (require) { + 'use strict'; + var sAnimations = require('website.content.snippets.animation'); + var core = require('web.core'); + var Widget = require('web.Widget'); + + var QWeb = core.qweb; + + var Quiz= Widget.extend({ + /** + * @override + * @param {Object} el + * @param {Object} data holding all the slide elements needed for the quiz + * It will either come from the fullscreen widget or the sAnimation at the end of this file + */ + init: function(el, data){ + this.slide = data; + this.answeredQuestions = []; + this.fullscreen = el; + return this._super.apply(this,arguments); + }, + start: function(){ + var self = this; + self._bindQuizEvents(); + /** + * If the quiz is rendered by the server instead of the fullscreen widget, + * questions and their answers will have to be created manually from attributes + */ + if(self.slide.quiz.questions.length === 0){ + this._setQuestions(); + } + return this._super.apply(this, arguments); + }, + _renderSuccessModal: function(){ + var self =this; + $('.o_wslides_fullscreen_quiz').append(QWeb.render('website.course.quiz.success', { + data: data + })); + $('.submit-quiz').remove(); + $('.next-slide').css('display', 'inline-block'); + $('.next-slide').click(function(){ + self.fullscreen._goToNextSlide(); + }); + $('.o_wslides_quiz_success_modal_close').click(function(){ + $('.o_wslides_quiz_success_modal').remove(); + $('.o_wslides_quiz_modal_background').remove(); + }); + $(".o_wslides_quiz_modal_background").click(function(ev){ + $(ev.currentTarget).remove(); + $('.o_wslides_quiz_success_modal').remove(); + }); + }, + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + /** + * @private + * In case the quiz is rendered by the server and the data don't come from the fullscreen widget, + * questions and their answers will have to be set here by using attributes + */ + _setQuestions: function(){ + var self = this; + $('.o_wslides_quiz_question').each(function(){ + self.slide.quiz.questions.push({ + id: parseInt($(this).attr('id')), + title: $(this).attr('title'), + answers: [] + }); + }) + for(var i = 0; i < self.slide.quiz.questions.length; i++){ + self._setAnswersForQuestion(self.slide.quiz.questions[i]); + } + }, + _setAnswersForQuestion: function (question){ + var self = this; + $('.o_wslides_quiz_answer[question_id='+question.id+']').each(function (){ + question.answers.push({ + id: parseInt($(this).attr('id')), + text: $(this).attr('text_value'), + is_correct: $(this).attr('is_correct') + }); + }); + }, + _updateProgressbar: function (){ + var self = this; + var completion = self.channelCompletion <= 100 ? self.channelCompletion : 100; + $('.o_wslides_fullscreen_sidebar_progress_gauge').css('width', completion + "%" ); + $('.o_wslides_progress_percentage').text(completion); + }, + _bindQuizEvents: function (){ + var self = this; + if (!self.slide.done){ + $('.o_wslides_quiz_answer').each(function(){ + $(this).click(self._onAnswerClick.bind(self)); + }); + } + $('.submit-quiz').click(self._onSubmitQuiz.bind(self)); + }, + _highlightAnswers: function (answers){ + var self = this; + self.answeredQuestions = []; + for (var i = 0; i < answers.goodAnswers.length; i++){ + $('#answer'+ answers.goodAnswers[i] +'').addClass('o_wslides_quiz_good_answer'); + $('#answer'+ answers.goodAnswers[i] +' .o_wslides_quiz_radio_box span').replaceWith($('<i class="fa fa-check-circle"></i>')); + var questionID =$('#answer'+ answers.goodAnswers[i] +' .o_wslides_quiz_radio_box input').attr('question_id'); + self.answeredQuestions.push(questionID); + $('.o_wslides_quiz_answer[question_id='+questionID+']:not(.o_wslides_quiz_good_answer)').addClass('o_wslides_quiz_ignored_answer'); + $('.o_wslides_quiz_answer[question_id='+questionID+']').each(function(){ + $(this).unbind('click') + }); + $('input[question_id='+questionID+']').each(function(){ + $(this).prop('disabled',true); + }); + } + for (i = 0; i < answers.badAnswers.length; i++){ + $('#answer'+ answers.badAnswers[i]).removeClass('o_wslides_quiz_good_answer') + .addClass('o_wslides_quiz_bad_answer') + .unbind('click'); + $('#answer'+ answers.badAnswers[i] +' .o_wslides_quiz_radio_box span').replaceWith($('<i class="fa fa-times "></i>')); + $('#answer'+ answers.badAnswers[i] +' .o_wslides_quiz_radio_box input').prop('checked', false); + } + }, + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + _onAnswerClick: function (ev){ + var self = this; + var target = $(ev.currentTarget); + if ((self.answeredQuestions.indexOf(target.attr('question_id'))) === -1){ + var id = target.attr('id'); + var question_id = target.attr('question_id'); + $('.o_wslides_quiz_answer[question_id='+question_id+']').removeClass('o_wslides_quiz_good_answer'); + $('#'+id+' input[type=radio]').prop('checked', true); + } + }, + _onSubmitQuiz: function (){ + var self = this; + var inputs = $('input[type=radio]:checked'); + var values = []; + for (var i = 0; i < inputs.length; i++){ + values.push(parseInt($(inputs[i]).val())); + } + if (values.length === self.slide.quiz.questions.length){ + $('.quiz-danger').remove(); + self._rpc({ + route: "/slide/quiz/submit", + params: { + slide_id: self.slide.id, + answer_ids: values, + quiz_id: self.slide.quiz_id + } + }).then(function(data){ + self._highlightAnswers(data); + self.slide.quiz.nb_attempts = data.attempts_count; + if (data.passed){ + self.channelCompletion = data.channel_completion; + self._updateProgressbar(); + $('#check-'+self.slide.id).replaceWith($('<i class="check-done o_wslides_slide_completed fa fa-check-circle"></i>')); + self.slide.done = true; + self._renderSuccessModal(); + } + else { + $('#quiz-points').text(self.slide.quiz.nb_attempts < 3 ? self.slide.quiz.possible_rewards[self.slide.quiz.nb_attempts] : self.slide.quiz.possible_rewards[self.slide.quiz.possible_rewards.length-1]); + } + }); + } else { + $('.o_wslides_fullscreen_player').append($('<p class="quiz-danger text-danger mt-1">All questions must be answered !</p>')); + } + }, +}); + + sAnimations.registry.websiteSlidesQuizNoFullscreen = Widget.extend({ + selector: '.o_w_slides_quiz_no_fullscreen', + xmlDependencies: ['/website_slides/static/src/xml/website_slides_fullscreen.xml'], + init: function (el){ + this._super.apply(this, arguments); + }, + start: function (){ + this._super.apply(this, arguments); + var slideID = parseInt(this.$el.attr('slide_id'),10); + var slideDone = this.$el.attr('slide_done'); + var nbAttempts = parseInt(this.$el.attr('nb_attempts'), 10); + var firstAttemptReward = this.$el.attr('first_reward'); + var secondAttemptReward = this.$el.attr('second_reward'); + var thirdAttemptReward = this.$el.attr('third_reward'); + var fourthAttemptReward = this.$el.attr('fourth_reward'); + var possibleRewards = [firstAttemptReward,secondAttemptReward,thirdAttemptReward,fourthAttemptReward] + var data = { + id: slideID, + done: slideDone, + quiz: { + questions: [], + nb_attempts: nbAttempts, + possible_rewards: possibleRewards, + reward: nbAttempts < possibleRewards.length ? possibleRewards[nbAttempts] : possibleRewards[possibleRewards.length-1] + } + } + if(!slideDone){ + var NewQuiz = new Quiz(this, data); + NewQuiz.appendTo(".o_w_slides_quiz_no_fullscreen"); + } + } + }); + + return Quiz; +});
\ No newline at end of file diff --git a/addons/website_slides/static/src/js/slides_course_sidebar_list.js b/addons/website_slides/static/src/js/slides_course_sidebar_list.js new file mode 100644 index 00000000000..11b61d4e5ec --- /dev/null +++ b/addons/website_slides/static/src/js/slides_course_sidebar_list.js @@ -0,0 +1,34 @@ +odoo.define('website_slides.sidebar', function (require) { + 'use strict'; + var sAnimations = require('website.content.snippets.animation'); + var core = require('web.core'); + var Widget = require('web.Widget'); + + var SideBar = Widget.extend({ + init: function (el){ + this._super.apply(this,arguments); + }, + start: function (){ + $('.o_wslides_fullscreen_toggle_sidebar').click(function (ev){ + ev.preventDefault(); + $(ev.currentTarget).toggleClass('active'); + $('.o_wslides_fullscreen_sidebar').toggleClass('o_wslides_fullscreen_sidebar_hidden'); + $('.o_wslides_fullscreen_player').toggleClass('o_wslides_fullscreen_player_no_sidebar') + }) + return this._super.apply(this, arguments); + }, + }); + + sAnimations.registry.websiteSlidesSidebarList = Widget.extend({ + selector: '.o_wslides_fullscreen_toggle_sidebar', + // xmlDependencies: ['/website_slides/static/src/xml/website_slides.xml'], + init: function (el){ + this._super.apply(this, arguments); + }, + start: function (){ + this._super.apply(this, arguments); + var sideBar = new SideBar(this); + sideBar.appendTo(".oe_js_side_bar_list"); + } + }); +});
\ No newline at end of file diff --git a/addons/website_slides/static/src/js/slides_course_slides_list.js b/addons/website_slides/static/src/js/slides_course_slides_list.js new file mode 100644 index 00000000000..bf634f542e3 --- /dev/null +++ b/addons/website_slides/static/src/js/slides_course_slides_list.js @@ -0,0 +1,251 @@ +odoo.define('website_slides.slideslist', function (require) { + 'use strict'; + var sAnimations = require('website.content.snippets.animation'); + var Widget = require('web.Widget'); + + var SlideUpload = require('website_slides.upload_modal'); + + var List = Widget.extend({ + init: function (el){ + this._super.apply(this,arguments); + this.draggedElement = undefined; + this.dropTarget = undefined; + this.slideCount = undefined; + this.slides = []; + this.categories = []; + }, + start: function (){ + this._super.apply(this,arguments); + this.slideCount = $('li.content-slide').length; + //Change links HREF to fullscreen mode for SEO + var links = $(".link-to-slide"); + for (var i = 0; i < links.length; i++){ + $(links[i]).attr('href', $(links[i]).attr('href') + "?fullscreen=1"); + } + this._bindEvents(); + }, + _bindEvents: function (){ + var self = this; + $('.slide-draggable').each(function (){ + self._addSlideDragAndDropHandlers($(this)); + }); + $('.section-draggable').each(function (){ + self._addSectionDragAndDropHandlers($(this)); + }); + $('.course-section').each(function (){ + self._addDropSlideOnSectionHandler($(this)); + }); + }, + _unbind: function (className){ + $("."+className).each(function (){ + $(this).unbind(); + }); + }, + _unbindAll: function (){ + this._unbind('slide-draggable'); + this._unbind('section-draggable'); + this._unbind('course-section'); + }, + _getSlides: function (){ + var self = this; + var slides = $('li.content-slide'); + for(var i = 0; i < slides.length;i++){ + var slide = $(slides[i]); + self.slides.push({ + id: parseInt(slide.attr('slide_id')), + category_id: parseInt(slide.attr('category_id')), + sequence: i + }); + } + }, + _getCategories: function (){ + var self = this; + self.categories = []; + var categories = $('.course-section'); + for (var i = 0; i < categories.length;i++){ + var category = $(categories[i]); + self.categories.push(parseInt(category.attr('category_id'))); + } + }, + _addDropSlideOnSectionHandler: function (target){ + var self = this; + target.on('drop', function (ev){ + if (ev.preventDefault){ + ev.preventDefault(); + } + self.dropTarget = $(ev.currentTarget); + self.draggedElement[0].parentNode.removeChild(self.draggedElement[0]); + $('ul[category_id='+target.attr('category_id')+']').append(self.draggedElement) + self._addSlideDragAndDropHandlers(self.draggedElement); + self._reorderSlides(); + }); + target.on('dragover', function (ev){ + if(ev.preventDefault){ + ev.preventDefault(); + } + }); + }, + _addSlideDragAndDropHandlers: function (target){ + var self = this; + target.on('dragstart', function (ev){ + $('.section-draggable').removeClass('hold') + self._unbind('section-draggable'); + ev.originalEvent.dataTransfer.effectAllowed = 'move'; + ev.originalEvent.dataTransfer.setData('text/html', this.outerHTML); + self.draggedElement = target; + self.draggedElement.addClass('hold'); + }); + target.on('dragover', function (ev){ + if ($(ev.currentTarget) !== self.draggedElement){ + if (ev.preventDefault){ + ev.preventDefault(); + } + target.addClass('slide-hovered'); + } + }); + target.on('dragleave', function (ev){ + if (ev.preventDefault){ + ev.preventDefault(); + } + target.removeClass('slide-hovered'); + }); + target.on('drop', function (ev){ + if (self.draggedElement.hasClass('slide-draggable') && target.hasClass('slide-draggable')){ + if (ev.preventDefault){ + ev.preventDefault(); + } + target.removeClass('slide-hovered'); + target.removeClass('hold'); + if (target !== self.draggedElement){ + self.dropTarget = $(ev.currentTarget); + self.draggedElement[0].parentNode.removeChild(self.draggedElement[0]); + var dropHTML = ev.originalEvent.dataTransfer.getData('text/html'); + target[0].insertAdjacentHTML('beforebegin',dropHTML); + self.draggedElement = $(target[0].previousSibling); + self._reorderSlides(); + } + self._unbindAll(); + self._bindEvents(); + } + }); + target.on('dragend', function (ev){ + if (ev.preventDefault){ + ev.preventDefault(); + } + target.removeClass('slide-hovered'); + target.removeClass('hold'); + }); + }, + _addSectionDragAndDropHandlers: function(target){ + var self = this; + target.on('dragstart', function (ev){ + self._unbind('slide-draggable'); + self._unbind('course-section'); + ev.originalEvent.dataTransfer.effectAllowed = 'move'; + ev.originalEvent.dataTransfer.setData('text/html', this.outerHTML); + self.draggedElement = target; + self.draggedElement.addClass('hold'); + }); + target.on('dragover', function (ev){ + if (target.hasClass('section-draggable') && self.draggedElement.hasClass('section-draggable')){ + if (ev.preventDefault){ + ev.preventDefault(); + } + target.addClass('slide-hovered'); + } + }); + target.on('dragleave', function (ev){ + if (ev.preventDefault){ + ev.preventDefault(); + } + target.removeClass('slide-hovered'); + }); + target.on('drop', function (ev){ + if(ev.preventDefault){ + ev.preventDefault(); + } + if(self.draggedElement.hasClass('section-draggable') && target.hasClass('section-draggable')){ + target.removeClass('slide-hovered'); + target.removeClass('hold'); + self.dropTarget = $(ev.currentTarget); + if(target !== self.draggedElement && $(ev.currentTarget).hasClass('section-draggable')){ + self.draggedElement[0].parentNode.removeChild(self.draggedElement[0]); + var dropHTML = ev.originalEvent.dataTransfer.getData('text/html'); + target[0].insertAdjacentHTML('beforebegin',dropHTML); + self.draggedElement = $(target[0].previousSibling); + self._reorderCategories(); + self._reorderSlides(); + self._rebindUploadButton(self.draggedElement.attr('category_id')); + } + self._unbindAll(); + self._bindEvents(); + } + }); + target.on('dragend', function (ev){ + if (ev.preventDefault){ + ev.preventDefault(); + } + target.removeClass('slide-hovered'); + target.removeClass('hold'); + }); + }, + _reorderCategories: function (){ + var self = this; + self._getCategories(); + self._rpc({ + route: '/web/dataset/resequence', + params: { + model: "slide.category", + ids: self.categories + } + }).then(function (){ + self._resetCategoriesIndex(); + }); + }, + _resetCategoriesIndex: function (){ + var categoriesIndexes = $('.section-index') + for (var i = 0; i < categoriesIndexes.length; i++){ + $(categoriesIndexes[i]).text(i+1) + } + }, + _reorderSlides: function(){ + var self = this; + // In case the slide was transfered to another section + if (self.draggedElement.hasClass('slide-draggable')){ + self.draggedElement.attr('category_id', parseInt(self.dropTarget.attr('category_id'))) + } + self.slides = []; + self._getSlides(); + self._rpc({ + route: "/slides/resequence_slides", + params: { + slides_data: self.slides + } + }).then(function(){ + }); + }, + _rebindUploadButton: function(categoryID){ + var self = this; + this.$('.oe_slide_js_upload[data-category-id='+categoryID+']').click(function(ev){ + ev.preventDefault(); + var data = $(ev.currentTarget).data(); + var dialog = new SlideUpload.SlideUploadDialog(self, data); + dialog.appendTo(document.body); + dialog.open(); + }) + } + }) + + sAnimations.registry.websiteSlidesCourseSlidesList = Widget.extend({ + selector: '.oe_js_course_slides_list', + xmlDependencies: ['/website_slides/static/src/xml/website_slides_upload.xml'], + init: function (el){ + this._super.apply(this, arguments); + }, + start: function (){ + this._super.apply(this, arguments); + var list = new List(this); + list.appendTo(".oe_js_course_slides_list"); + } + }); +});
\ No newline at end of file diff --git a/addons/website_slides/static/src/js/slides_delete_slide.js b/addons/website_slides/static/src/js/slides_delete_slide.js new file mode 100644 index 00000000000..e3eef38502d --- /dev/null +++ b/addons/website_slides/static/src/js/slides_delete_slide.js @@ -0,0 +1,88 @@ +odoo.define('website_slides.delete.slide', function (require) { + + var sAnimations = require('website.content.snippets.animation'); + var core = require('web.core'); + var Widget = require('web.Widget'); + + var _t = core._t; + var QWeb = core.qweb; + + var DeleteSlideDialog = Widget.extend({ + template: 'website.slide.delete.slide', + events: { + 'hidden.bs.modal': 'destroy', + 'click button[data-dismiss="modal"]': '_cancel', + 'click button.delete': '_delete' + }, + + /** + * @override + * @param {Object} el + * @param {number} channel_id + */ + init: function (el, slideID) { + this._super(el, slideID); + this.slide_id = parseInt(slideID, 10); + }, + /** + * @override + */ + start: function () { + this.$el.modal({ + backdrop: 'static' + }); + return this._super.apply(this, arguments); + }, + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + _delete: function (ev) { + var self = this; + // TO FIX: CallBack is not executed + $('[slide_id='+this.slide_id+']').remove(); + this._rpc({ + model: 'slide.slide', + method: 'unlink', + args: [[self.slide_id]], + }).then(function () { + $('[slide='+this.slide_id+']').remove(); + }); + }, + /** + * @override + */ + _cancel: function () { + this.trigger('cancel'); + } + }); + + sAnimations.registry.websiteSlidesDeleteSlide = sAnimations.Class.extend({ + selector: '.oe_slide_js_delete_slide', + xmlDependencies: ['/website_slides/static/src/xml/website_slides_upload.xml'], + read_events: { + 'click': '_onDeleteSlideClick', + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + _openDialog: function (slideID) { + new DeleteSlideDialog(this, slideID).appendTo(document.body); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {Event} ev + */ + _onDeleteSlideClick: function (ev) { + var target = $(ev.currentTarget); + this._openDialog(target.attr('slide_id')); + }, + }); + return DeleteSlideDialog; + }); diff --git a/addons/website_slides/static/src/js/slides_upload.js b/addons/website_slides/static/src/js/slides_upload.js index 855b7e52932..7b0a2964bf8 100644 --- a/addons/website_slides/static/src/js/slides_upload.js +++ b/addons/website_slides/static/src/js/slides_upload.js @@ -30,6 +30,7 @@ var SlideUploadDialog = Dialog.extend({ this._setup(); this.channelID = parseInt(options.channelId, 10); + this.defaultCategoryID = parseInt(options.categoryId,10) this.canUpload = options.canUpload === 'True'; this.canPublish = options.canPublish === 'True'; @@ -198,6 +199,7 @@ var SlideUploadDialog = Dialog.extend({ */ _getSelect2DropdownValues: function (){ var result = {}; + var self = this; // tags var tagValues = []; _.each(this.$('#tag_ids').select2('data'), function (val) { @@ -211,11 +213,17 @@ var SlideUploadDialog = Dialog.extend({ result['tag_ids'] = tagValues; } // category - var categoryValue = this.$('#category_id').select2('data'); - if (categoryValue && categoryValue.create) { - result['category_id'] = [0, {'name': categoryValue.text}]; - } else if (categoryValue) { - result['category_id'] = [categoryValue.id]; + if(!self.defaultCategoryID){ + var categoryValue = this.$('#category_id').select2('data'); + if (categoryValue && categoryValue.create) { + result['category_id'] = [0, {'name': categoryValue.text}]; + } else if (categoryValue) { + result['category_id'] = [categoryValue.id]; + this.categoryID = categoryValue.id; + } + } else { + result['category_id'] = [self.defaultCategoryID]; + this.categoryID = self.defaultCategoryID; } return result; }, @@ -303,6 +311,25 @@ var SlideUploadDialog = Dialog.extend({ return values; }, + _reorderSlidesSequence: function(){ + var self = this; + var slidesElement = $('li.content-slide'); + var slides = []; + for(var i = 0; i < slidesElement.length;i++){ + slides.push({ + id: parseInt($(slidesElement[i]).attr('slide_id')), + category_id: parseInt($(slidesElement[i]).attr('category_id')), + sequence: i + }) + } + self._rpc({ + route: '/slides/resequence_slides', + params: { + slides_data: slides + } + }).then(function(){ + }) + }, /** * Init the data relative to the support slide type to upload * @@ -325,6 +352,11 @@ var SlideUploadDialog = Dialog.extend({ label: _t('Video'), template: 'website.slide.upload.modal.video', }, + quiz: { + icon: 'fa-question-circle', + label: _t('Quiz'), + template: 'website.slide.upload.quiz' + } }; }, /** @@ -472,12 +504,11 @@ var SlideUploadDialog = Dialog.extend({ _onChangeSlideUrl: function (ev) { var self = this; var url = $(ev.target).val(); - this._alertRemove(); this.isValidUrl = false; this.set('can_submit_form', false); this._fetchUrlPreview(url).then(function (data) { - this.set('can_submit_form', true); + self.set('can_submit_form', true); if (data.error) { self._alertDisplay(data.error); } else { @@ -508,6 +539,12 @@ var SlideUploadDialog = Dialog.extend({ self.set('state', oldType); self._alertDisplay(data.error); } else { + //Quick and really dirty fix for reordering issues + if(data.channel_type == 'training' && self.categoryID){ + var categoryElement = $('ul[category_id='+self.categoryID+']'); + $('<li hidden class="content-slide" slide_id="'+data.slide_id+'" category_id="'+self.categoryID+'">temp</li>').appendTo(categoryElement) + self._reorderSlidesSequence(); + } window.location = data.url; } }); diff --git a/addons/website_slides/static/src/js/website_slides.editor.js b/addons/website_slides/static/src/js/website_slides.editor.js index 40513e785e2..72a18bb93ee 100644 --- a/addons/website_slides/static/src/js/website_slides.editor.js +++ b/addons/website_slides/static/src/js/website_slides.editor.js @@ -5,6 +5,7 @@ var core = require('web.core'); var Dialog = require('web.Dialog'); var QWeb = core.qweb; var WebsiteNewMenu = require('website.newMenu'); +var wUtils = require('website.utils'); var _t = core._t; |
