diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/website_forum/static/src | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/website_forum/static/src')
7 files changed, 1303 insertions, 0 deletions
diff --git a/addons/website_forum/static/src/js/tours/website_forum.js b/addons/website_forum/static/src/js/tours/website_forum.js new file mode 100644 index 00000000..55f57457 --- /dev/null +++ b/addons/website_forum/static/src/js/tours/website_forum.js @@ -0,0 +1,67 @@ +odoo.define("website_forum.tour_forum", function (require) { + "use strict"; + + var core = require("web.core"); + var tour = require("web_tour.tour"); + + var _t = core._t; + + tour.register("question", { + url: "/forum/1", + }, [{ + trigger: ".o_forum_ask_btn", + position: "left", + content: _t("Create a new post in this forum by clicking on the button."), + }, { + trigger: "input[name=post_name]", + position: "top", + content: _t("Give your post title."), + }, { + trigger: ".note-editable p", + extra_trigger: "input[name=post_name]:not(:propValue(\"\"))", + content: _t("Put your question here."), + position: "bottom", + run: "text", + }, { + trigger: ".select2-choices", + extra_trigger: ".note-editable p:not(:containsExact(\"<br>\"))", + content: _t("Insert tags related to your question."), + position: "top", + run: function (actions) { + actions.auto("input[id=s2id_autogen2]"); + }, + }, { + trigger: "button:contains(\"Post\")", + extra_trigger: "input[id=s2id_autogen2]:not(:propValue(\"Tags\"))", + content: _t("Click to post your question."), + position: "bottom", + }, { + extra_trigger: 'div.modal.modal_shown', + trigger: ".modal-header button.close", + auto: true, + }, + { + trigger: "a:contains(\"Answer\").collapsed", + content: _t("Click to answer."), + position: "bottom", + }, + { + trigger: ".note-editable p", + content: _t("Put your answer here."), + position: "bottom", + run: "text", + }, { + trigger: "button:contains(\"Post Answer\")", + extra_trigger: ".note-editable p:not(:containsExact(\"<br>\"))", + content: _t("Click to post your answer."), + position: "bottom", + }, { + extra_trigger: 'div.modal.modal_shown', + trigger: ".modal-header button.close", + auto: true, + }, { + trigger: ".o_wforum_validate_toggler[data-karma=\"20\"]:first", + content: _t("Click here to accept this answer."), + position: "right", + }]); +}); diff --git a/addons/website_forum/static/src/js/website_forum.editor.js b/addons/website_forum/static/src/js/website_forum.editor.js new file mode 100644 index 00000000..74072c59 --- /dev/null +++ b/addons/website_forum/static/src/js/website_forum.editor.js @@ -0,0 +1,144 @@ +odoo.define('website_forum.editor', function (require) { +"use strict"; + +var core = require('web.core'); +var WebsiteNewMenu = require('website.newMenu'); +var Dialog = require('web.Dialog'); + +var _t = core._t; + +var ForumCreateDialog = Dialog.extend({ + xmlDependencies: Dialog.prototype.xmlDependencies.concat( + ['/website_forum/static/src/xml/website_forum_templates.xml'] + ), + template: 'website_forum.add_new_forum', + events: _.extend({}, Dialog.prototype.events, { + 'change input[name="privacy"]': '_onPrivacyChanged', + }), + + /** + * @override + * @param {Object} parent + * @param {Object} options + */ + init: function (parent, options) { + options = _.defaults(options || {}, { + title: _t("New Forum"), + size: 'medium', + buttons: [ + { + text: _t("Create"), + classes: 'btn-primary', + click: this.onCreateClick.bind(this), + }, + { + text: _t("Discard"), + close: true + }, + ] + }); + this._super(parent, options); + }, + start: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + var $input = self.$('#group_id'); + $input.select2({ + width: '100%', + allowClear: true, + formatNoMatches: false, + multiple: false, + selection_data: false, + fill_data: function (query, data) { + var that = this; + var tags = {results: []}; + _.each(data, function (obj) { + if (that.matcher(query.term, obj.display_name)) { + tags.results.push({id: obj.id, text: obj.display_name}); + } + }); + query.callback(tags); + }, + query: function (query) { + var that = this; + // fetch data only once and store it + if (!this.selection_data) { + self._rpc({ + model: 'res.groups', + method: 'search_read', + args: [[], ['display_name']], + }).then(function (data) { + that.fill_data(query, data); + that.selection_data = data; + }); + } else { + this.fill_data(query, this.selection_data); + } + } + }); + }); + }, + onCreateClick: function () { + var $dialog = this.$el; + var $forumName = $dialog.find('input[name=forum_name]'); + if (!$forumName.val()) { + $forumName.addClass('border-danger'); + return; + } + var $forumPrivacyGroup = $dialog.find('input[name=group_id]'); + var forumPrivacy = $dialog.find('input:radio[name=privacy]:checked').val(); + if (forumPrivacy === 'private' && !$forumPrivacyGroup.val()) { + this.$("#group-required").removeClass('d-none'); + return; + } + var addMenu = ($dialog.find('input[type="checkbox"]').is(':checked')); + var forumMode = $dialog.find('input:radio[name=mode]:checked').val(); + return this._rpc({ + route: '/forum/new', + params: { + forum_name: $forumName.val(), + forum_mode: forumMode, + forum_privacy: forumPrivacy, + forum_privacy_group: $forumPrivacyGroup.val(), + add_menu: addMenu || "", + }, + }).then(function (url) { + window.location.href = url; + return new Promise(function () {}); + }); + }, + /** + * @private + */ + _onPrivacyChanged: function (ev) { + this.$('.show_visibility_group').toggleClass('d-none', ev.target.value !== 'private'); + }, +}); + +WebsiteNewMenu.include({ + actions: _.extend({}, WebsiteNewMenu.prototype.actions || {}, { + new_forum: '_createNewForum', + }), + + //-------------------------------------------------------------------------- + // Actions + //-------------------------------------------------------------------------- + + /** + * Asks the user information about a new forum to create, then creates it + * and redirects the user to this new forum. + * + * @private + * @returns {Promise} Unresolved if there is a redirection + */ + _createNewForum: function () { + var self = this; + var def = new Promise(function (resolve) { + var dialog = new ForumCreateDialog(self, {}); + dialog.open(); + dialog.on('closed', self, resolve); + }); + return def; + }, +}); +}); diff --git a/addons/website_forum/static/src/js/website_forum.js b/addons/website_forum/static/src/js/website_forum.js new file mode 100644 index 00000000..e65d8944 --- /dev/null +++ b/addons/website_forum/static/src/js/website_forum.js @@ -0,0 +1,593 @@ +odoo.define('website_forum.website_forum', function (require) { +'use strict'; + +const dom = require('web.dom'); +var core = require('web.core'); +var weDefaultOptions = require('web_editor.wysiwyg.default_options'); +var wysiwygLoader = require('web_editor.loader'); +var publicWidget = require('web.public.widget'); +var session = require('web.session'); +var qweb = core.qweb; + +var _t = core._t; + +publicWidget.registry.websiteForum = publicWidget.Widget.extend({ + selector: '.website_forum', + xmlDependencies: ['/website_forum/static/src/xml/website_forum_share_templates.xml'], + events: { + 'click .karma_required': '_onKarmaRequiredClick', + 'mouseenter .o_js_forum_tag_follow': '_onTagFollowBoxMouseEnter', + 'mouseleave .o_js_forum_tag_follow': '_onTagFollowBoxMouseLeave', + 'mouseenter .o_forum_user_info': '_onUserInfoMouseEnter', + 'mouseleave .o_forum_user_info': '_onUserInfoMouseLeave', + 'mouseleave .o_forum_user_bio_expand': '_onUserBioExpandMouseLeave', + 'click .flag:not(.karma_required)': '_onFlagAlertClick', + 'click .vote_up:not(.karma_required), .vote_down:not(.karma_required)': '_onVotePostClick', + 'click .o_js_validation_queue a[href*="/validate"]': '_onValidationQueueClick', + 'click .o_wforum_validate_toggler:not(.karma_required)': '_onAcceptAnswerClick', + 'click .o_wforum_favourite_toggle': '_onFavoriteQuestionClick', + 'click .comment_delete': '_onDeleteCommentClick', + 'click .js_close_intro': '_onCloseIntroClick', + 'submit .js_wforum_submit_form:has(:not(.karma_required).o_wforum_submit_post)': '_onSubmitForm', + }, + + /** + * @override + */ + start: function () { + var self = this; + + this.lastsearch = []; + + // float-left class messes up the post layout OPW 769721 + $('span[data-oe-model="forum.post"][data-oe-field="content"]').find('img.float-left').removeClass('float-left'); + + // welcome message action button + var forumLogin = _.string.sprintf('%s/web?redirect=%s', + window.location.origin, + escape(window.location.href) + ); + $('.forum_register_url').attr('href', forumLogin); + + // Initialize forum's tooltips + this.$('[data-toggle="tooltip"]').tooltip({delay: 0}); + this.$('[data-toggle="popover"]').popover({offset: 8}); + + $('input.js_select2').select2({ + tags: true, + tokenSeparators: [',', ' ', '_'], + maximumInputLength: 35, + minimumInputLength: 2, + maximumSelectionSize: 5, + lastsearch: [], + createSearchChoice: function (term) { + if (_.filter(self.lastsearch, function (s) { + return s.text.localeCompare(term) === 0; + }).length === 0) { + //check Karma + if (parseInt($('#karma').val()) >= parseInt($('#karma_edit_retag').val())) { + return { + id: '_' + $.trim(term), + text: $.trim(term) + ' *', + isNew: true, + }; + } + } + }, + formatResult: function (term) { + if (term.isNew) { + return '<span class="badge badge-primary">New</span> ' + _.escape(term.text); + } else { + return _.escape(term.text); + } + }, + ajax: { + url: '/forum/get_tags', + dataType: 'json', + data: function (term) { + return { + query: term, + limit: 50, + }; + }, + results: function (data) { + var ret = []; + _.each(data, function (x) { + ret.push({ + id: x.id, + text: x.name, + isNew: false, + }); + }); + self.lastsearch = ret; + return {results: ret}; + } + }, + // Take default tags from the input value + initSelection: function (element, callback) { + var data = []; + _.each(element.data('init-value'), function (x) { + data.push({id: x.id, text: x.name, isNew: false}); + }); + element.val(''); + callback(data); + }, + }); + + _.each($('textarea.o_wysiwyg_loader'), function (textarea) { + var $textarea = $(textarea); + var editorKarma = $textarea.data('karma') || 0; // default value for backward compatibility + var $form = $textarea.closest('form'); + var hasFullEdit = parseInt($("#karma").val()) >= editorKarma; + // Warning: Do not activate any option that adds inline style. + // Because the style is deleted after save. + var toolbar = [ + ['style', ['style']], + ['font', ['bold', 'italic', 'underline', 'clear']], + ['para', ['ul', 'ol', 'paragraph']], + ['table', ['table']], + ]; + if (hasFullEdit) { + toolbar.push(['insert', ['link', 'picture']]); + } + toolbar.push(['history', ['undo', 'redo']]); + + var options = { + height: 200, + minHeight: 80, + toolbar: toolbar, + styleWithSpan: false, + styleTags: _.without(weDefaultOptions.styleTags, 'h1', 'h2', 'h3'), + recordInfo: { + context: self._getContext(), + res_model: 'forum.post', + res_id: +window.location.pathname.split('-').pop(), + }, + disableFullMediaDialog: true, + disableResizeImage: true, + }; + if (!hasFullEdit) { + options.plugins = { + LinkPlugin: false, + MediaPlugin: false, + }; + } + wysiwygLoader.load(self, $textarea[0], options).then(wysiwyg => { + // float-left class messes up the post layout OPW 769721 + $form.find('.note-editable').find('img.float-left').removeClass('float-left'); + // o_we_selected_image has not always been removed when + // saving a post so we need the line below to remove it if it is present. + $form.find('.note-editable').find('img.o_we_selected_image').removeClass('o_we_selected_image'); + $form.on('click', 'button, .a-submit', () => { + $form.find('.note-editable').find('img.o_we_selected_image').removeClass('o_we_selected_image'); + wysiwyg.save(); + }); + }); + }); + + _.each(this.$('.o_wforum_bio_popover'), authorBox => { + $(authorBox).popover({ + trigger: 'hover', + offset: 10, + animation: false, + html: true, + }).popover('hide').data('bs.popover').tip.classList.add('o_wforum_bio_popover_container'); + }); + + this.$('#post_reply').on('shown.bs.collapse', function (e) { + const replyEl = document.querySelector('#post_reply'); + const scrollingElement = dom.closestScrollable(replyEl.parentNode); + dom.scrollTo(replyEl, { + forcedOffset: $(scrollingElement).innerHeight() - $(replyEl).innerHeight(), + }); + }); + + return this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * + * @override + * @param {Event} ev + */ + _onSubmitForm: function (ev) { + let validForm = true; + + let $form = $(ev.currentTarget); + let $title = $form.find('input[name=post_name]'); + let $textarea = $form.find('textarea[name=content]'); + // It's not really in the textarea that the user write at first + let textareaContent = $form.find('.o_wysiwyg_wrapper .note-editable.panel-body').text().trim(); + + if ($title.length && $title[0].required) { + if ($title.val()) { + $title.removeClass('is-invalid'); + } else { + $title.addClass('is-invalid'); + validForm = false; + } + } + + // Because the textarea is hidden, we add the red or green border to its container + if ($textarea[0] && $textarea[0].required) { + let $textareaContainer = $form.find('.o_wysiwyg_wrapper .note-editor.panel.panel-default'); + if (!textareaContent.length) { + $textareaContainer.addClass('border border-danger rounded-top'); + validForm = false; + } else { + $textareaContainer.removeClass('border border-danger rounded-top'); + } + } + + if (validForm) { + // Stores social share data to display modal on next page. + if ($form.has('.oe_social_share_call').length) { + sessionStorage.setItem('social_share', JSON.stringify({ + targetType: $(ev.currentTarget).find('.o_wforum_submit_post').data('social-target-type'), + })); + } + } else { + ev.preventDefault(); + setTimeout(function() { + var $buttons = $(ev.currentTarget).find('button[type="submit"], a.a-submit'); + _.each($buttons, function (btn) { + let $btn = $(btn); + $btn.find('i').remove(); + $btn.prop('disabled', false); + }); + }, 0); + } + }, + /** + * @private + * @param {Event} ev + */ + _onKarmaRequiredClick: function (ev) { + var $karma = $(ev.currentTarget); + var karma = $karma.data('karma'); + var forum_id = $('#wrapwrap').data('forum_id'); + if (!karma) { + return; + } + ev.preventDefault(); + var msg = karma + ' ' + _t("karma is required to perform this action. "); + var title = _t("Karma Error"); + if (forum_id) { + msg += '<a class="alert-link" href="/forum/' + forum_id + '/faq">' + _t("Read the guidelines to know how to gain karma.") + '</a>'; + } + if (session.is_website_user) { + msg = _t("Sorry you must be logged in to perform this action"); + title = _t("Access Denied"); + } + this.call('crash_manager', 'show_warning', { + message: msg, + title: title, + }, { + sticky: false, + }); + }, + /** + * @private + * @param {Event} ev + */ + _onTagFollowBoxMouseEnter: function (ev) { + $(ev.currentTarget).find('.o_forum_tag_follow_box').stop().fadeIn().css('display', 'block'); + }, + /** + * @private + * @param {Event} ev + */ + _onTagFollowBoxMouseLeave: function (ev) { + $(ev.currentTarget).find('.o_forum_tag_follow_box').stop().fadeOut().css('display', 'none'); + }, + /** + * @private + * @param {Event} ev + */ + _onUserInfoMouseEnter: function (ev) { + $(ev.currentTarget).parent().find('.o_forum_user_bio_expand').delay(500).toggle('fast'); + }, + /** + * @private + * @param {Event} ev + */ + _onUserInfoMouseLeave: function (ev) { + $(ev.currentTarget).parent().find('.o_forum_user_bio_expand').clearQueue(); + }, + /** + * @private + * @param {Event} ev + */ + _onUserBioExpandMouseLeave: function (ev) { + $(ev.currentTarget).fadeOut('fast'); + }, + /** + * @private + * @param {Event} ev + */ + _onFlagAlertClick: function (ev) { + var self = this; + ev.preventDefault(); + var $link = $(ev.currentTarget); + this._rpc({ + route: $link.data('href') || ($link.attr('href') !== '#' && $link.attr('href')) || $link.closest('form').attr('action'), + }).then(function (data) { + if (data.error) { + var message; + if (data.error === 'anonymous_user') { + message = _t("Sorry you must be logged to flag a post"); + } else if (data.error === 'post_already_flagged') { + message = _t("This post is already flagged"); + } else if (data.error === 'post_non_flaggable') { + message = _t("This post can not be flagged"); + } + self.call('crash_manager', 'show_warning', { + message: message, + title: _t("Access Denied"), + }, { + sticky: false, + }); + } else if (data.success) { + var elem = $link; + if (data.success === 'post_flagged_moderator') { + elem.data('href') && elem.html(' Flagged'); + var c = parseInt($('#count_flagged_posts').html(), 10); + c++; + $('#count_flagged_posts').html(c); + } else if (data.success === 'post_flagged_non_moderator') { + elem.data('href') && elem.html(' Flagged'); + var forumAnswer = elem.closest('.forum_answer'); + forumAnswer.fadeIn(1000); + forumAnswer.slideUp(1000); + } + } + }); + }, + /** + * @private + * @param {Event} ev + */ + _onVotePostClick: function (ev) { + var self = this; + ev.preventDefault(); + var $btn = $(ev.currentTarget); + this._rpc({ + route: $btn.data('href'), + }).then(function (data) { + if (data.error) { + var message; + if (data.error === 'own_post') { + message = _t('Sorry, you cannot vote for your own posts'); + } else if (data.error === 'anonymous_user') { + message = _t('Sorry you must be logged to vote'); + } + self.call('crash_manager', 'show_warning', { + message: message, + title: _t("Access Denied"), + }, { + sticky: false, + }); + } else { + var $container = $btn.closest('.vote'); + var $items = $container.children(); + var $voteUp = $items.filter('.vote_up'); + var $voteDown = $items.filter('.vote_down'); + var $voteCount = $items.filter('.vote_count'); + var userVote = parseInt(data['user_vote']); + + $voteUp.prop('disabled', userVote === 1); + $voteDown.prop('disabled', userVote === -1); + + $items.removeClass('text-success text-danger text-muted o_forum_vote_animate'); + void $container[0].offsetWidth; // Force a refresh + + if (userVote === 1) { + $voteUp.addClass('text-success'); + $voteCount.addClass('text-success'); + $voteDown.removeClass('karma_required'); + } + if (userVote === -1) { + $voteDown.addClass('text-danger'); + $voteCount.addClass('text-danger'); + $voteUp.removeClass('karma_required'); + } + if (userVote === 0) { + if (!$voteDown.data('can-downvote')) { + $voteDown.addClass('karma_required'); + } + if (!$voteUp.data('can-upvote')) { + $voteUp.addClass('karma_required'); + } + } + $voteCount.html(data['vote_count']).addClass('o_forum_vote_animate'); + } + }); + }, + /** + * @private + * @param {Event} ev + */ + _onValidationQueueClick: function (ev) { + ev.preventDefault(); + var $link = $(ev.currentTarget); + $link.parents('.post_to_validate').hide(); + $.get($link.attr('href')).then(() => { + var left = $('.o_js_validation_queue:visible').length; + var type = $('h2.o_page_header a.active').data('type'); + $('#count_post').text(left); + $('#moderation_tools a[href*="/' + type + '_"]').find('strong').text(left); + if (!left) { + this.$('.o_caught_up_alert').removeClass('d-none'); + } + }, function () { + $link.parents('.o_js_validation_queue > div').addClass('bg-danger text-white').css('background-color', '#FAA'); + $link.parents('.post_to_validate').show(); + }); + }, + /** + * @private + * @param {Event} ev + */ + _onAcceptAnswerClick: function (ev) { + ev.preventDefault(); + var $link = $(ev.currentTarget); + var target = $link.data('target'); + + this._rpc({ + route: $link.data('href'), + }).then(data => { + if (data.error) { + if (data.error === 'anonymous_user') { + var message = _t("Sorry, anonymous users cannot choose correct answer."); + } + this.call('crash_manager', 'show_warning', { + message: message, + title: _t("Access Denied"), + }, { + sticky: false, + }); + } else { + _.each(this.$('.forum_answer'), answer => { + var $answer = $(answer); + var isCorrect = $answer.is(target) ? data : false; + var $toggler = $answer.find('.o_wforum_validate_toggler'); + var newHelper = isCorrect ? $toggler.data('helper-decline') : $toggler.data('helper-accept'); + + $answer.toggleClass('o_wforum_answer_correct', isCorrect); + $toggler.tooltip('dispose') + .attr('data-original-title', newHelper) + .tooltip({delay: 0}); + }); + } + }); + }, + /** + * @private + * @param {Event} ev + */ + _onFavoriteQuestionClick: function (ev) { + ev.preventDefault(); + var $link = $(ev.currentTarget); + this._rpc({ + route: $link.data('href'), + }).then(function (data) { + $link.toggleClass('o_wforum_gold fa-star', data) + .toggleClass('fa-star-o text-muted', !data); + }); + }, + /** + * @private + * @param {Event} ev + */ + _onDeleteCommentClick: function (ev) { + ev.preventDefault(); + var $link = $(ev.currentTarget); + var $container = $link.closest('.o_wforum_post_comments_container'); + + this._rpc({ + route: $link.closest('form').attr('action'), + }).then(function () { + $link.closest('.o_wforum_post_comment').remove(); + + var count = $container.find('.o_wforum_post_comment').length; + if (count) { + $container.find('.o_wforum_comments_count').text(count); + } else { + $container.find('.o_wforum_comments_count_header').remove(); + } + }); + }, + /** + * @private + * @param {Event} ev + */ + _onCloseIntroClick: function (ev) { + ev.preventDefault(); + document.cookie = 'forum_welcome_message = false'; + $('.forum_intro').slideUp(); + return true; + }, +}); + +publicWidget.registry.websiteForumSpam = publicWidget.Widget.extend({ + selector: '.o_wforum_moderation_queue', + xmlDependencies: ['/website_forum/static/src/xml/website_forum_share_templates.xml'], + events: { + 'click .o_wforum_select_all_spam': '_onSelectallSpamClick', + 'click .o_wforum_mark_spam': 'async _onMarkSpamClick', + 'input #spamSearch': '_onSpamSearchInput', + }, + + /** + * @override + */ + start: function () { + this.spamIDs = this.$('.modal').data('spam-ids'); + return this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {Event} ev + */ + _onSelectallSpamClick: function (ev) { + var $spamInput = this.$('.modal .tab-pane.active input'); + $spamInput.prop('checked', true); + }, + + /** + * @private + * @param {Event} ev + */ + _onSpamSearchInput: function (ev) { + var self = this; + var toSearch = $(ev.currentTarget).val(); + return this._rpc({ + model: 'forum.post', + method: 'search_read', + args: [ + [['id', 'in', self.spamIDs], + '|', + ['name', 'ilike', toSearch], + ['content', 'ilike', toSearch]], + ['name', 'content'] + ], + kwargs: {} + }).then(function (o) { + _.each(o, function (r) { + r.content = $('<p>' + $(r.content).html() + '</p>').text().substring(0, 250); + }); + self.$('div.post_spam').html(qweb.render('website_forum.spam_search_name', { + posts: o, + })); + }); + }, + + /** + * @private + * @param {Event} ev + */ + _onMarkSpamClick: function (ev) { + var key = this.$('.modal .tab-pane.active').data('key'); + var $inputs = this.$('.modal .tab-pane.active input.custom-control-input:checked'); + var values = _.map($inputs, function (o) { + return parseInt(o.value); + }); + return this._rpc({model: 'forum.post', + method: 'mark_as_offensive_batch', + args: [this.spamIDs, key, values], + }).then(function () { + window.location.reload(); + }); + }, +}); + +}); diff --git a/addons/website_forum/static/src/js/website_forum.share.js b/addons/website_forum/static/src/js/website_forum.share.js new file mode 100644 index 00000000..68a2b8f3 --- /dev/null +++ b/addons/website_forum/static/src/js/website_forum.share.js @@ -0,0 +1,99 @@ +odoo.define('website_forum.share', function (require) { +'use strict'; + +var core = require('web.core'); +var publicWidget = require('web.public.widget'); + +var qweb = core.qweb; + +// FIXME There is no reason to inherit from socialShare here +var ForumShare = publicWidget.registry.socialShare.extend({ + selector: '', + xmlDependencies: publicWidget.registry.socialShare.prototype.xmlDependencies + .concat(['/website_forum/static/src/xml/website_forum_share_templates.xml']), + events: {}, + + /** + * @override + * @param {Object} parent + * @param {Object} options + * @param {string} targetType + */ + init: function (parent, options, targetType) { + this._super.apply(this, arguments); + this.targetType = targetType; + }, + /** + * @override + */ + start: function () { + var def = this._super.apply(this, arguments); + this._onMouseEnter(); + return def; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _bindSocialEvent: function () { + this._super.apply(this, arguments); + $('.oe_share_bump').click($.proxy(this._postBump, this)); + }, + /** + * @private + */ + _render: function () { + var $question = this.$('article.question'); + if (!this.targetType) { + this._super.apply(this, arguments); + } else if (this.targetType === 'social-alert') { + $question.before(qweb.render('website.social_alert', {medias: this.socialList})); + } else { + $('body').append(qweb.render('website.social_modal', { + medias: this.socialList, + target_type: this.targetType, + state: $question.data('state'), + })); + $('#oe_social_share_modal').modal('show'); + } + }, + /** + * @private + */ + _postBump: function () { + this._rpc({ // FIXME + route: '/forum/post/bump', + params: { + post_id: this.element.data('id'), + }, + }); + }, +}); + +publicWidget.registry.websiteForumShare = publicWidget.Widget.extend({ + selector: '.website_forum', + + /** + * @override + */ + start: function () { + // Retrieve stored social data + if (sessionStorage.getItem('social_share')) { + var socialData = JSON.parse(sessionStorage.getItem('social_share')); + (new ForumShare(this, false, socialData.targetType)).attachTo($(document.body)); + sessionStorage.removeItem('social_share'); + } + // Display an alert if post has no reply and is older than 10 days + var $questionContainer = $('.oe_js_bump'); + if ($questionContainer.length) { + new ForumShare(this, false, 'social-alert').attachTo($questionContainer); + } + + return this._super.apply(this, arguments); + }, +}); +}); diff --git a/addons/website_forum/static/src/scss/website_forum.scss b/addons/website_forum/static/src/scss/website_forum.scss new file mode 100644 index 00000000..12ad1ac3 --- /dev/null +++ b/addons/website_forum/static/src/scss/website_forum.scss @@ -0,0 +1,272 @@ +$gold: #eca801; +$silver: #cccccc; +$bronze: #eea91e; + +.website_forum { + .o_forum_ask_btn { + @include media-breakpoint-up(md) { + box-shadow: $box-shadow; + width: 200px; + } + } + + .o_wforum_nav .nav-link { + @include o-hover-text-color($body-color, $link-color); + line-height: 1; + + .fa { + opacity: 0.5; + } + + &:hover, &.active { + .fa { + opacity: 1; + } + } + + &.active { + background-color: rgba(theme-color('info'), 0.1); + color: darken(theme-color('info'), 15%)!important; + } + + img.o_forum_avatar { + @include size(30px); + } + } + + // Single Post + .o_wforum_post, .note-editable.panel-body { + word-wrap: break-word; + + pre { + color: color-yiq(gray('100')); + border-radius: $border-radius; + padding: 1rem; + background-color: gray('100'); + white-space: pre-wrap; + } + + blockquote { + position: relative; + padding-left: 1em; + border-left: .25em solid gray('500'); + color: gray('600'); + } + + #post_reply { + img.o_forum_avatar { + @include size(24px); + } + } + } + + .o_wforum_readable { + max-width: 700px; + + p { + margin-bottom: 0.5rem; + } + + img { + max-width: 100%; + } + + &::after { + content: ""; + display: table; + clear: both; + } + } + + textarea.o_wysiwyg_loader + .note-editor { + border: 0; + + .note-toolbar { + @include border-top-radius($border-radius); + height: 32px; + } + + .note-editable, .note-statusbar { + border: 1px solid $border-color; + border-width: 0 1px; + } + + .note-statusbar { + display: block; + border-bottom-width: 1px; + } + } + + .o_wforum_author_box { + &.o_show_info { + line-height: 1.2; + img { + @include size(2em); + } + } + + &.o_compact { + line-height: 1; + + img { + @include size(1.4em); + } + } + + } + + .forum_answer { + .o_wforum_answer_correct_badge { + display: none; + } + + .o_wforum_author_box_check { + @include size(1em); + display: none; + top: 0; + right: -3px; + box-shadow: 0 0 0 2px white; + line-height: .8; + } + + .o_wforum_validate_toggler { + @include o-hover-text-color(gray('400'), lighten(theme-color('success'), 20%)); + } + + &.o_wforum_answer_correct { + .o_wforum_answer_correct_badge, .o_wforum_author_box .o_wforum_author_box_check { + display: inline; + } + + .o_wforum_validate_toggler { + @include o-hover-text-color(theme-color('success'), theme-color('warning')); + + &:hover .fa.fa-check:before { + content: '\f00d'; + } + } + + .o_wforum_answer_header .o_wforum_author_pic { + border: 2px solid $success; + padding: 3px; + } + } + } + + .o_wforum_gold { + color: $gold; + } + + a.no-decoration { + cursor: pointer; + text-decoration: none !important; + } +} + +.website_forum, .o_wforum_profile_tab { + .vote { + .vote_count { + line-height: 1; + animation-play-state: paused; + + &.o_forum_vote_animate { + animation: bounceIn 0.3s ease running; + } + } + + .vote_down, .vote_up { + line-height: 0.5; + font-size: 1.1em; + @include o-hover-text-color(rgba($text-muted, 0.5), theme-color('success')); + } + + .vote_down { + @include o-hover-text-color(rgba($text-muted, 0.5), theme-color('danger')); + } + + &.o_wforum_vote_vertical { + @include media-breakpoint-up(sm) { + font-size: 1.2em; + } + } + } +} + +.o_js_forum_tag_follow { + .badge { + font-size: 100%; + } + + .o_forum_tag_follow_box { + @include o-position-absolute(100%, auto, auto, 0); + display: none; + z-index: 1; + + .card { + padding: ($grid-gutter-width*0.5) - 1px; + } + } +} + +.o_profile_main { + overflow: hidden; +} + +img.o_forum_avatar { + @include size(40px); + object-fit: cover; +} + +img.o_forum_avatar_big { + @include size(75px); + object-fit: cover; +} + +.o_wprofile_email_validation_container { + img.o_forum_avatar { + @include size(16px); + } +} + +.o_wforum_bio_popover_wrap { + .o_wforum_bio_popover_name { + address > div, span[data-oe-model="res.country"] { + display: flex; + align-items: center; + } + span[data-oe-model="res.country"] { + margin-left: 10px; + } + } + + .o_wforum_bio_popover_info .css_editable_mode_hidden > div:last-child > .o_forum_tooltip_line { + margin-top: -0.5rem; // compensate parent 'mt-2' class + } + + .o_wforum_bio_popover_bio p { + margin-top: 8px; + margin-bottom: 0; + } +} + +.o_wforum_elearning_navtabs_container { + @include media-breakpoint-up(md) { + background-color: theme-color('secondary'); + } +} + +.website_forum { + margin-bottom: $spacer; +} + +.popover.o_wforum_bio_popover_container { + max-width: 552px; +} + +.o_wforum_forum_card_bg { + background-image: linear-gradient(99deg, theme-color('secondary') 10%, darken(theme-color('secondary'), 10%) 90%); + + #o_wforum_forums_index_list & { + min-height: 100px; + } +} diff --git a/addons/website_forum/static/src/xml/website_forum_share_templates.xml b/addons/website_forum/static/src/xml/website_forum_share_templates.xml new file mode 100644 index 00000000..e1ef8d75 --- /dev/null +++ b/addons/website_forum/static/src/xml/website_forum_share_templates.xml @@ -0,0 +1,75 @@ +<templates id="template" xml:space="preserve"> + <t t-name="website.social_modal"> + <div role="dialog" class="modal fade" id="oe_social_share_modal"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <header class="modal-header alert alert-info mb0" role="status"> + <h4 class="modal-title">Thanks for posting!</h4> + <button type="button" class="close" data-dismiss="modal"> + <span role="img" aria-label="Close">x</span> + </button> + </header> + <main class="modal-body"> + <t t-if="target_type == 'question'" t-call="website_forum.social_message_question"/> + <t t-if="target_type == 'answer'" t-call="website_forum.social_message_answer"/> + <t t-if="target_type == 'default'" t-call="website_forum.social_message_default"/> + <div t-if="state != 'pending'" class="share-icons text-center text-primary"> + <t t-foreach="medias" t-as="media"> + <a style="cursor: pointer" t-attf-class="fa-stack fa-lg share #{media}" t-attf-aria-label="Share on #{media}" t-attf-title="Share on #{media}"> + <span class="fa fa-square fa-stack-2x"></span> + <span t-attf-class="oe_social_#{media} fa fa-#{media} fa-stack-1x fa-inverse"></span> + </a> + </t> + </div> + </main> + </div> + </div> + </div> + </t> + <t t-name="website_forum.spam_search_name"> + <t t-foreach="posts" t-as="post"> + <div class="card mb-1 o_spam_character"> + <div class="card-body py-2"> + <div class="custom-control custom-checkbox"> + <input type="checkbox" class="custom-control-input" t-attf-id="post_#{post.id}" t-att-value='post.id' checked='checked'/> + <label class="custom-control-label" t-attf-for="post_#{post.id}"> + <b><t t-esc="post.name" /></b> + <p class='text-muted'><t t-esc="post.content" /></p> + </label> + </div> + </div> + </div> + </t> + </t> + <t t-name="website_forum.social_message_question"> + <p>On average, <b>45% of questions shared</b> on social networks get an answer within + 5 hours. Questions shared on two social networks have <b>65% more chance to get an + answer</b> !</p> + <p t-if="state == 'pending'">You can share your question once it has been validated</p> + </t> + <t t-name="website_forum.social_message_answer"> + <p>By sharing you answer, you will get additional <b>karma points</b> if your + answer is selected as the right one. See what you can do with karma + <a href="/forum/help-1/faq" target="_blank">here</a>.</p> + </t> + <t t-name="website_forum.social_message_default"> + <p>Share this content to increase your chances to be featured on the front page and attract more visitors.</p> + </t> + + <t t-name="website.social_alert"> + <div class="alert alert-info alert-dismissable" role="status"> + <button type="button" class="close" data-dismiss="alert"> + <span role="img" aria-label="Close">×</span> + </button> + <p>Move this question to the top of the list by sharing it on social networks.</p><br/> + <div> + <t t-foreach="medias" t-as="media"> + <a style="cursor: pointer" t-attf-class="fa-stack fa-lg share oe_share_bump #{media}" t-attf-aria-label="Share on #{media}" t-attf-title="Share on #{media}"> + <span class="fa fa-square fa-stack-2x"></span> + <span t-attf-class="oe_social_#{media} fa fa-#{media} fa-stack-1x fa-inverse"></span> + </a> + </t> + </div> + </div> + </t> +</templates> diff --git a/addons/website_forum/static/src/xml/website_forum_templates.xml b/addons/website_forum/static/src/xml/website_forum_templates.xml new file mode 100644 index 00000000..aedc6223 --- /dev/null +++ b/addons/website_forum/static/src/xml/website_forum_templates.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + <t t-name="website_forum.add_new_forum"> + <form id="editor_new_forum"> + <div class="form-group row"> + <label for="page-name" class="col-md-4 col-form-label">Forum Name</label> + <div class="col-md-8"> + <input type="text" name="forum_name" class="form-control" required="required"/> + <div class="custom-control custom-checkbox mt-2"> + <input type="checkbox" class="custom-control-input" id="add_to_menu" required="required"/> + <label class="custom-control-label" for="add_to_menu">Add to menu</label> + </div> + </div> + </div> + <div class="form-group row mt-2"> + <label class="col-md-4 col-form-label" data-toggle="tooltip" data-placement="bottom" + title="Questions and Answers mode: only one answer allowed\n Discussions mode: multiple answers allowed">Forum Mode</label> + <div class="col-md-8"> + <div class="custom-control custom-radio"> + <input type="radio" id="questions" name="mode" class="custom-control-input" value="questions" checked="checked"/> + <label class="custom-control-label" for="questions">Questions and Answers</label> + </div> + <div class="custom-control custom-radio"> + <input type="radio" id="discussions" name="mode" class="custom-control-input" value="discussions"/> + <label class="custom-control-label" for="discussions">Discussions</label> + </div> + </div> + </div> + <div class="form-group row mt-2"> + <label class="col-md-4 col-form-label" data-toggle="tooltip" data-placement="bottom" + title="Public: Forum is public\nSigned In: Forum is visible for signed in users\nSome users: Forum and their content are hidden for non members of selected group">Privacy</label> + <div class="col-md-8"> + <div class="custom-control custom-radio"> + <input type="radio" id="public" name="privacy" class="custom-control-input" value="public" checked="checked"/> + <label class="custom-control-label" for="public">Public</label> + </div> + <div class="custom-control custom-radio"> + <input type="radio" id="connected" name="privacy" class="custom-control-input" value="connected"/> + <label class="custom-control-label" for="connected">Signed In</label> + </div> + <div class="custom-control custom-radio"> + <input type="radio" id="private" name="privacy" class="custom-control-input" value="private"/> + <label class="custom-control-label" for="private">Some Users</label> + </div> + <div class="form-group show_visibility_group d-none"> + <input type="text" class="form-control" name="group_id" id="group_id" placeholder="Select Authorized Group"/> + <p id="group-required" class="text-danger mt-1 mb-0 d-none">Please fill in this field</p> + </div> + </div> + </div> + </form> + </t> +</templates> |
