diff --git a/addons/website_slides/controllers/main.py b/addons/website_slides/controllers/main.py index edad9c10419..916c27869ca 100644 --- a/addons/website_slides/controllers/main.py +++ b/addons/website_slides/controllers/main.py @@ -259,6 +259,37 @@ class WebsiteSlides(WebsiteProfile): session_slide_answer_quiz.pop(str(slide['id']), None) request.session['slide_answer_quiz'] = json.dumps(session_slide_answer_quiz) + # TAG UTILITIES + # -------------------------------------------------- + + def _create_or_get_channel_tag(self, tag_id, group_id): + if not tag_id: + return request.env['slide.channel.tag'] + # handle creation of new channel tag + if tag_id[0] == 0: + group_id = self._create_or_get_channel_tag_group(group_id) + if not group_id: + return {'error': _('Missing "Tag Group" for creating a new "Tag".')} + + new_tag = request.env['slide.channel.tag'].create({ + 'name': tag_id[1]['name'], + 'group_id': group_id, + }) + return new_tag + return request.env['slide.channel.tag'].browse(tag_id[0]) + + def _create_or_get_channel_tag_group(self, group_id): + if not group_id: + return False + # handle creation of new channel tag group + if group_id[0] == 0: + tag_group = request.env['slide.channel.tag.group'].create({ + 'name': group_id[1]['name'], + }) + group_id = tag_group.id + # use existing channel tag group + return group_id[0] + # -------------------------------------------------- # SLIDE.CHANNEL MAIN / SEARCH # -------------------------------------------------- @@ -617,29 +648,9 @@ class WebsiteSlides(WebsiteProfile): if not can_upload or not can_publish: return {'error': _('You cannot add tags to this course.')} - if tag_id: - # handle creation of new channel tag - if tag_id[0] == 0: - if group_id: - # handle creation of new channel tag group - if group_id[0] == 0: - tag_group = request.env['slide.channel.tag.group'].create({ - 'name': group_id[1]['name'], - }) - group_id = tag_group.id - # use existing channel tag group - else: - group_id = group_id[0] - else: - return {'error': _('Missing "Tag Group" for creating a new "Tag".')} + tag = self._create_or_get_channel_tag(tag_id, group_id) + tag.write({'channel_ids': [(4, channel.id, 0)]}) - request.env['slide.channel.tag'].create({'name': tag_id[1]['name'], - 'channel_ids': [channel.id], - 'group_id': group_id, - }) - else: - # use existing channel tag - request.env['slide.channel.tag'].browse(tag_id[0]).write({'channel_ids': [(4, channel.id, 0)]}) return {'url': "/slides/%s" % (slug(channel))} @http.route(['/slides/channel/subscribe'], type='json', auth='user', website=True) @@ -811,6 +822,15 @@ class WebsiteSlides(WebsiteProfile): result = slide._send_share_email(email, fullscreen) return result + # -------------------------------------------------- + # TAGS SECTION + # -------------------------------------------------- + + @http.route('/slide_channel_tag/add', type='json', auth='user', methods=['POST'], website=True) + def slide_channel_tag_create_or_get(self, tag_id, group_id): + tag = self._create_or_get_channel_tag(tag_id, group_id) + return {'tag_id': tag.id} + # -------------------------------------------------- # QUIZ SECTION # -------------------------------------------------- diff --git a/addons/website_slides/static/src/img/channel-documentation-layout.png b/addons/website_slides/static/src/img/channel-documentation-layout.png new file mode 100644 index 00000000000..6967033a926 Binary files /dev/null and b/addons/website_slides/static/src/img/channel-documentation-layout.png differ diff --git a/addons/website_slides/static/src/img/channel-training-layout.png b/addons/website_slides/static/src/img/channel-training-layout.png new file mode 100644 index 00000000000..cb79d38eebb Binary files /dev/null and b/addons/website_slides/static/src/img/channel-training-layout.png differ diff --git a/addons/website_slides/static/src/img/onboarding-quiz.png b/addons/website_slides/static/src/img/onboarding-quiz.png new file mode 100644 index 00000000000..1ab746f3cad Binary files /dev/null and b/addons/website_slides/static/src/img/onboarding-quiz.png differ diff --git a/addons/website_slides/static/src/js/slides_course_quiz.js b/addons/website_slides/static/src/js/slides_course_quiz.js index 57bedab2cfa..9b3c0ad76d6 100644 --- a/addons/website_slides/static/src/js/slides_course_quiz.js +++ b/addons/website_slides/static/src/js/slides_course_quiz.js @@ -567,11 +567,7 @@ odoo.define('website_slides.quiz', function (require) { } this.quiz.questionsCount++; event.data.questionFormWidget.destroy(); - if (event.data.save_and_new) { - this._onCreateQuizClick(); - } else { - this.$('.o_wslides_js_quiz_add_question').removeClass('d-none'); - } + this.$('.o_wslides_js_quiz_add_question').removeClass('d-none'); }, /** diff --git a/addons/website_slides/static/src/js/slides_course_quiz_finish.js b/addons/website_slides/static/src/js/slides_course_quiz_finish.js index a20d558e368..8d6d11e5658 100644 --- a/addons/website_slides/static/src/js/slides_course_quiz_finish.js +++ b/addons/website_slides/static/src/js/slides_course_quiz_finish.js @@ -24,7 +24,6 @@ var SlideQuizFinishModal = Dialog.extend({ options = _.defaults(options || {}, { size: 'medium', dialogClass: 'd-flex p-0', - $parentNode: parent.$el, technical: false, renderHeader: false, renderFooter: false diff --git a/addons/website_slides/static/src/js/slides_course_quiz_question_form.js b/addons/website_slides/static/src/js/slides_course_quiz_question_form.js index b2a6df8e009..e134cb352e6 100644 --- a/addons/website_slides/static/src/js/slides_course_quiz_question_form.js +++ b/addons/website_slides/static/src/js/slides_course_quiz_question_form.js @@ -116,14 +116,12 @@ var QuestionFormWidget = publicWidget.Widget.extend({ }, /** - * Handler when user click on 'Save & New', 'Save & Close' - * or 'Update' buttons. + * Handler when user click on 'Save' or 'Update' buttons. * @param ev * @private */ _validateQuestion: function (ev) { this._createOrUpdateQuestion({ - save_and_new: $(ev.currentTarget).hasClass('o_wslides_js_quiz_create_next'), update: $(ev.currentTarget).hasClass('o_wslides_js_quiz_update'), }); }, @@ -169,7 +167,6 @@ var QuestionFormWidget = publicWidget.Widget.extend({ } else { self.trigger_up('display_created_question', { newQuestionRenderedTemplate: renderedQuestion, - save_and_new: options.save_and_new, questionFormWidget: self }); } diff --git a/addons/website_slides/static/src/js/slides_course_tag_add.js b/addons/website_slides/static/src/js/slides_course_tag_add.js index dcab7176a93..b62e9cf0809 100644 --- a/addons/website_slides/static/src/js/slides_course_tag_add.js +++ b/addons/website_slides/static/src/js/slides_course_tag_add.js @@ -29,12 +29,14 @@ var TagCourseDialog = Dialog.extend({ click: this._onClickFormSubmit.bind(this) }, { text: _t("Discard"), - close: true + click: this._onClickClose.bind(this) }] }); this.channelID = parseInt(options.channelId, 10); this.tagIds = options.channelTagIds || []; + // Open with a tag name as default + this.defaultTag = options.defaultTag; this._super(parent, options); }, start: function () { @@ -42,6 +44,9 @@ var TagCourseDialog = Dialog.extend({ return this._super.apply(this, arguments).then(function () { self._bindSelect2Dropdown(); self._hideTagGroup(); + if (self.defaultTag) { + self._setDefaultSelection(); + } }); }, @@ -151,6 +156,11 @@ var TagCourseDialog = Dialog.extend({ return values; }, + _setDefaultSelection: function () { + this.$('#tag_id').select2('data', {id: _.uniqueId('tag_'), text: this.defaultTag, create: true}, true); + this.$('#tag_id').select2('readonly', true); + }, + /** * Get value for tag_id and [when appropriate] tag_group_id to send to server * @@ -260,7 +270,16 @@ var TagCourseDialog = Dialog.extend({ //-------------------------------------------------------------------------- // Handler //-------------------------------------------------------------------------- + _onClickFormSubmit: function () { + if (this.defaultTag && !this.channelID) { + this._createNewTag(); + } else { + this._addTagToChannel(); + } + }, + + _addTagToChannel: function () { var self = this; var $form = this.$('#slides_channel_tag_add_form'); if (this._formValidate($form)) { @@ -280,6 +299,34 @@ var TagCourseDialog = Dialog.extend({ } }, + _createNewTag: function () { + var self = this; + var $form = this.$('#slides_channel_tag_add_form'); + this.$('#tag_id').select2('readonly', false); + var valid = this._formValidate($form); + this.$('#tag_id').select2('readonly', true); + if (valid) { + var values = this._getSelect2DropdownValues(); + return this._rpc({ + route: '/slide_channel_tag/add', + params: { + 'tag_id': values.tag_id, + 'group_id': values.group_id + }, + }).then(function (data) { + self.trigger_up('tag_refresh', { tag_id: data.tag_id }); + self.close(); + }); + } + }, + + _onClickClose: function () { + if (this.defaultTag && !this.channelID) { + this.trigger_up('tag_remove_new'); + } + this.close(); + }, + _onChangeTag: function (ev) { var self = this; var tag = $(ev.currentTarget).select2('data'); diff --git a/addons/website_slides/static/src/js/slides_course_unsubscribe.js b/addons/website_slides/static/src/js/slides_course_unsubscribe.js index 74992e29baf..2003f8ea688 100644 --- a/addons/website_slides/static/src/js/slides_course_unsubscribe.js +++ b/addons/website_slides/static/src/js/slides_course_unsubscribe.js @@ -13,7 +13,7 @@ var SlideUnsubscribeDialog = Dialog.extend({ template: 'slides.course.unsubscribe.modal', _texts: { titleSubscribe: _t("Subscribe"), - titleUnsubscribe: _t("Unsubscribe"), + titleUnsubscribe: _t("Notifications"), titleLeaveCourse: _t("Leave the course") }, @@ -60,7 +60,7 @@ var SlideUnsubscribeDialog = Dialog.extend({ if (state === '_subscription') { btnList.push({text: _t("Save"), classes: "btn-primary", click: this._onClickSubscriptionSubmit.bind(this)}); btnList.push({text: _t("Discard"), close: true}); - btnList.push({text: _t("Leave the course"), classes: "btn-danger ml-auto", click: this._onClickLeaveCourse.bind(this)}); + btnList.push({text: _t("or Leave the course"), classes: "btn-danger ml-auto", click: this._onClickLeaveCourse.bind(this)}); } else if (state === '_leave') { btnList.push({text: _t("Leave the course"), classes: "btn-danger", click: this._onClickLeaveCourseSubmit.bind(this)}); btnList.push({text: _t("Discard"), click: this._onClickLeaveCourseCancel.bind(this)}); diff --git a/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js b/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js index 1a2d217a727..604170b646a 100644 --- a/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js +++ b/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js @@ -19,9 +19,9 @@ odoo.define('website_slides.slide.preview', function (require) { }).then(function (isPreview) { if (isPreview) { $slideTarget.removeClass('badge-light badge-hide border'); - $slideTarget.addClass('badge-success py-1'); + $slideTarget.addClass('badge-success'); } else { - $slideTarget.removeClass('badge-success py-1'); + $slideTarget.removeClass('badge-success'); $slideTarget.addClass('badge-light badge-hide border'); } }); diff --git a/addons/website_slides/static/src/js/slides_upload.js b/addons/website_slides/static/src/js/slides_upload.js index b5704d68792..3ab24914d5a 100644 --- a/addons/website_slides/static/src/js/slides_upload.js +++ b/addons/website_slides/static/src/js/slides_upload.js @@ -67,11 +67,12 @@ var SlideUploadDialog = Dialog.extend({ this._alertRemove(); $('
', { "class": 'alert alert-warning', + id: 'upload-alert', role: 'alert' }).text(message).insertBefore(this.$('form')); }, _alertRemove: function () { - this.$('.alert-warning').remove(); + this.$('#upload-alert').remove(); }, /** * Section and tags management from select2 @@ -188,17 +189,17 @@ var SlideUploadDialog = Dialog.extend({ if (! this.modulesToInstallStatus.installing) { btnList.push({text: this.modulesToInstallStatus.failed ? _t("Retry") : _t("Install"), classes: 'btn-primary', click: this._onClickInstallModuleConfirm.bind(this)}); } - btnList.push({text: _t("Go Back"), classes: 'o_w_slide_go_back', click: this._onClickGoBack.bind(this)}); + btnList.push({text: _t("Discard"), classes: 'o_w_slide_go_back', click: this._onClickGoBack.bind(this)}); } else if (state !== '_upload') { // no button when uploading if (this.canUpload) { if (this.canPublish) { - btnList.push({text: _t("Publish"), classes: 'btn-primary o_w_slide_upload o_w_slide_upload_published', click: this._onClickFormSubmit.bind(this)}); - btnList.push({text: _t("Save as Draft"), classes: 'o_w_slide_upload', click: this._onClickFormSubmit.bind(this)}); + btnList.push({text: _t("Save & Publish"), classes: 'btn-primary o_w_slide_upload o_w_slide_upload_published', click: this._onClickFormSubmit.bind(this)}); + btnList.push({text: _t("Save"), classes: 'o_w_slide_upload', click: this._onClickFormSubmit.bind(this)}); } else { - btnList.push({text: _t("Save as Draft"), classes: 'btn-primary o_w_slide_upload', click: this._onClickFormSubmit.bind(this)}); + btnList.push({text: _t("Save"), classes: 'btn-primary o_w_slide_upload', click: this._onClickFormSubmit.bind(this)}); } } - btnList.push({text: _t("Go Back"), classes: 'o_w_slide_go_back', click: this._onClickGoBack.bind(this)}); + btnList.push({text: _t("Discard"), classes: 'o_w_slide_go_back', click: this._onClickGoBack.bind(this)}); } return btnList; }, @@ -351,22 +352,20 @@ var SlideUploadDialog = Dialog.extend({ }; }, /** - * Show the preview/right column and resize the modal + * Show the preview * @private */ _showPreviewColumn: function () { - this.$('#o_wslides_js_slide_upload_left_column').removeClass('col').addClass('col-md-6'); - this.$('#o_wslides_js_slide_upload_preview_column').removeClass('d-none'); - this.$modal.find('.modal-dialog').addClass('modal-lg'); + this.$('.o_slide_tutorial').addClass('d-none'); + this.$('.o_slide_preview').removeClass('d-none'); }, /** - * Hide the preview/right column and resize the modal + * Hide the preview * @private */ _hidePreviewColumn: function () { - this.$('#o_wslides_js_slide_upload_left_column').addClass('col').removeClass('col-md-6'); - this.$('#o_wslides_js_slide_upload_preview_column').addClass('d-none'); - this.$modal.find('.modal-dialog').removeClass('modal-lg'); + this.$('.o_slide_tutorial').removeClass('d-none'); + this.$('.o_slide_preview').addClass('d-none'); }, /** * @private @@ -388,6 +387,7 @@ var SlideUploadDialog = Dialog.extend({ _onChangeType: function () { var currentType = this.get('state'); var tmpl; + this.$modal.find('.modal-dialog').removeClass('modal-lg'); if (currentType === '_select') { tmpl = 'website.slide.upload.modal.select'; } else if (currentType === '_upload') { @@ -396,6 +396,7 @@ var SlideUploadDialog = Dialog.extend({ tmpl = 'website.slide.upload.modal.import'; } else { tmpl = this.slide_type_data[currentType]['template']; + this.$modal.find('.modal-dialog').addClass('modal-lg'); } this.$('.o_w_slide_upload_modal_container').empty(); this.$('.o_w_slide_upload_modal_container').append(QWeb.render(tmpl, {widget: this})); @@ -407,8 +408,6 @@ var SlideUploadDialog = Dialog.extend({ } else { this.set_title(_t("Upload a document")); } - - this.$modal.find('.modal-dialog').removeClass('modal-lg'); }, _onChangeCanSubmitForm: function (ev) { if (this.get('can_submit_form')) { 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 9865ccd5267..8ee780a2534 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 TagCourseDialog = require('website_slides.channel_tag.add').TagCourseDialog; var wUtils = require('website.utils'); var _t = core._t; @@ -13,8 +14,16 @@ var _t = core._t; var ChannelCreateDialog = Dialog.extend({ template: 'website.slide.channel.create', xmlDependencies: Dialog.prototype.xmlDependencies.concat( - ['/website_slides/static/src/xml/website_slides_channel.xml'] + ['/website_slides/static/src/xml/website_slides_channel.xml', + '/website_slides/static/src/xml/website_slides_channel_tag.xml'] ), + events: _.extend({}, Dialog.prototype.events, { + 'change input#tag_ids' : '_onChangeTag', + }), + custom_events: _.extend({}, Dialog.prototype.custom_events, { + 'tag_refresh': '_onTagRefresh', + 'tag_remove_new': '_onTagRemoveNew', + }), /** * @override * @param {Object} parent @@ -45,6 +54,29 @@ var ChannelCreateDialog = Dialog.extend({ formatNoMatches: false, multiple: true, selection_data: false, + formatSelection: function (data) { + if (data.tag) { + data.text = data.tag; + } + return data.text; + }, + createSearchChoice: function(term, data) { + var addedTags = $(this.opts.element).select2('data'); + if (_.filter(_.union(addedTags, data), function (tag) { + return tag.text.toLowerCase().localeCompare(term.toLowerCase()) === 0; + }).length === 0) { + if (this.opts.can_create) { + return { + id: _.uniqueId('tag_'), + create: true, + tag: term, + text: _.str.sprintf(_t("Create new Tag '%s'"), term), + }; + } else { + return undefined; + } + } + }, fill_data: function (query, data) { var that = this, tags = {results: []}; @@ -87,6 +119,44 @@ var ChannelCreateDialog = Dialog.extend({ $form.submit(); } }, + _onChangeTag: function (ev) { + var self = this; + var tags = $(ev.currentTarget).select2('data'); + tags.forEach(function (element) { + if (element.create) { + new TagCourseDialog(self, { defaultTag: element.text }).open(); + } + }); + }, + /** + * Replace the new tag ID by its real ID + * @param ev + * @private + */ + _onTagRefresh: function (ev) { + var $tag_ids = $('#tag_ids'); + var tags = $tag_ids.select2('data'); + tags.forEach(function (element) { + if (element.create) { + element.id = ev.data.tag_id; + element.create = false; + } + }); + $tag_ids.select2('data', tags); + // Set selection_data to false to force tag reload + $tag_ids.data('select2').opts.selection_data = false; + }, + /** + * Remove the created tag if the user clicks on 'Discard' on the create tag Dialog + * @private + */ + _onTagRemoveNew: function () { + var tags = $('#tag_ids').select2('data'); + tags = tags.filter(function (value) { + return !value.create; + }); + $('#tag_ids').select2('data', tags); + }, }); WebsiteNewMenu.include({ diff --git a/addons/website_slides/static/src/scss/slides_slide_fullscreen.scss b/addons/website_slides/static/src/scss/slides_slide_fullscreen.scss index a6c406e5242..059a0669641 100644 --- a/addons/website_slides/static/src/scss/slides_slide_fullscreen.scss +++ b/addons/website_slides/static/src/scss/slides_slide_fullscreen.scss @@ -119,6 +119,6 @@ .modal-open { > .modal-backdrop { - z-index: $zindex-dropdown - 1; + z-index: $zindex-modal-backdrop; } } diff --git a/addons/website_slides/static/src/scss/website_slides.scss b/addons/website_slides/static/src/scss/website_slides.scss index 1f4175d1e99..c3b87821628 100644 --- a/addons/website_slides/static/src/scss/website_slides.scss +++ b/addons/website_slides/static/src/scss/website_slides.scss @@ -554,6 +554,11 @@ $o-wslides-fs-side-width: 300px; } } +#slide_channel_add_form input[name="channel_type"]:checked + label { + border: 4px solid $primary; + box-shadow: 3px 3px 5px gray; +} + // Embed PDFViewer // ************************************************** #PDFViewer.o_wslides_fs_pdf_viewer { diff --git a/addons/website_slides/static/src/xml/slide_quiz_create.xml b/addons/website_slides/static/src/xml/slide_quiz_create.xml index 6ec408926b9..6bd3480e774 100644 --- a/addons/website_slides/static/src/xml/slide_quiz_create.xml +++ b/addons/website_slides/static/src/xml/slide_quiz_create.xml @@ -68,10 +68,7 @@
- Save & Close - - - Save & New + Save Cancel diff --git a/addons/website_slides/static/src/xml/website_slides_channel.xml b/addons/website_slides/static/src/xml/website_slides_channel.xml index c6c95ebbf2b..80f0214856d 100644 --- a/addons/website_slides/static/src/xml/website_slides_channel.xml +++ b/addons/website_slides/static/src/xml/website_slides_channel.xml @@ -8,38 +8,44 @@
- +

Please fill in this field

-
- -
- - - Training (with progress) - + +
+
+
+ + +
-
- - - Documentation - +
+
+ + +
-
+
- +