diff options
| author | Jeremy Kersten <jke@odoo.com> | 2018-12-06 19:23:44 +0000 |
|---|---|---|
| committer | Jeremy Kersten <jke@odoo.com> | 2018-12-06 20:49:42 +0000 |
| commit | 1c5ffe17374b63916341908cb11446d13f65f8c9 (patch) | |
| tree | f6116471a6ec3c6f387f5566f38df5dcc1113240 /addons/website_forum/static/src | |
| parent | 08807fcba959f3d8b70513824f614a2985f001fc (diff) | |
[REF] website_forum: mass moderation, modern UI bs4 and clean type
Now forum only allow to post message of type 'question'.
New 'modern' UI with Bootstrap 4
New moderation modal for bulk spam
Co-authored-by: qha <qha@odoo.com>
Co-authored-by: qsm <qsm@odoo.com>
Co-authored-by: jke <jke@odoo.com>
Thanks to @qha-odoo for UI
Thanks to @qsm-odoo for reviewSsss
closes odoo/odoo#29235
Diffstat (limited to 'addons/website_forum/static/src')
6 files changed, 231 insertions, 179 deletions
diff --git a/addons/website_forum/static/src/js/website_forum.js b/addons/website_forum/static/src/js/website_forum.js index 49865103f7b..5ab377fc561 100644 --- a/addons/website_forum/static/src/js/website_forum.js +++ b/addons/website_forum/static/src/js/website_forum.js @@ -3,11 +3,14 @@ odoo.define('website_forum.website_forum', function (require) { var core = require('web.core'); var sAnimations = require('website.content.snippets.animation'); +var session = require('web.session'); +var qweb = core.qweb; var _t = core._t; sAnimations.registry.websiteForum = sAnimations.Class.extend({ - selector: '#wrapwrap:has(.website_forum)', + selector: '.website_forum', + xmlDependencies: ['/website_forum/static/src/xml/website_forum_share_templates.xml'], read_events: { 'click .karma_required': '_onKarmaRequiredClick', 'mouseenter .o_js_forum_tag_follow': '_onTagFollowBoxMouseEnter', @@ -19,7 +22,7 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ 'mouseleave .o_forum_user_info': '_onUserInfoMouseLeave', 'mouseleave .o_forum_user_bio_expand': '_onUserBioExpandMouseLeave', 'click .flag:not(.karma_required)': '_onFlagAlertClick', - 'click .vote_up, .vote_down:not(.karma_required)': '_onVotePostClick', + 'click .vote_up:not(.karma_required), .vote_down:not(.karma_required)': '_onVotePostClick', 'click .o_js_validation_queue a[href*="/validate"]': '_onValidationQueueClick', 'click .accept_answer:not(.karma_required)': '_onAcceptAnswerClick', 'click .favourite_question': '_onFavoriteQuestionClick', @@ -28,7 +31,6 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ 'click .send_validation_email': '_onSendValidationEmailClick', 'click .validated_email_close': '_onCloseValidatedEmailClick', 'click .js_close_intro': '_onCloseIntroClick', - 'change .link_url, .o_forum_post_link': '_onCheckPostURL', }, /** @@ -82,8 +84,8 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ dataType: 'json', data: function (term) { return { - q: term, - l: 50, + query: term, + limit: 50, }; }, results: function (data) { @@ -112,7 +114,7 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ _.each($('textarea.load_editor'), function (textarea) { var $textarea = $(textarea); - var editorKarma = $textarea.data('karma') || 30; // default value for backward compatibility + var editorKarma = $textarea.data('karma') || 30; // default value for backward compatibility if (!$textarea.val().match(/\S/)) { $textarea.val('<p><br/></p>'); } @@ -147,6 +149,8 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ $el.addClass('text-success'); } }); + + return this._super.apply(this, arguments); }, //-------------------------------------------------------------------------- @@ -165,11 +169,11 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ } ev.preventDefault(); var msg = karma + ' ' + _t("karma is required to perform this action. You can earn karma by having your answers upvoted by the community."); - if ($('a[href*="/login"]').length) { + if (session.is_website_user) { msg = _t("Sorry you must be logged in to perform this action"); } - var $warning = $('<div class="alert alert-danger alert-dismissable oe_forum_alert mt8" id="karma_alert">' + - '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">×</button>' + + var $warning = $('<div class="alert alert-danger alert-dismissable oe_forum_alert" id="karma_alert">' + + '<button type="button" class="close notification_close" data-dismiss="alert">×</button>' + msg + '</div>'); var $voteAlert = $('#karma_alert'); if ($voteAlert.length) { @@ -257,7 +261,7 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ ev.preventDefault(); var $link = $(ev.currentTarget); this._rpc({ - route: $link.data('href'), + route: $link.data('href') || $link.attr('href') || $link.attr('action'), }).then(function (data) { if (data.error) { var $warning; @@ -273,6 +277,7 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ '<div class="alert alert-danger alert-dismissable oe_forum_alert" id="flag_alert">' + '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">×</button>' + _t("This post is already flagged") + + '<button type="button" class="close notification_close" t-att-id="notification.id" data-dismiss="alert" aria-label="Close">×</button>' + '</div>' ); } else if (data.error === 'post_non_flaggable') { @@ -280,6 +285,7 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ '<div class="alert alert-danger alert-dismissable oe_forum_alert" id="flag_alert">' + '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">×</button>' + _t("This post can not be flagged") + + '<button type="button" class="close notification_close" t-att-id="notification.id" data-dismiss="alert" aria-label="Close">×</button>' + '</div>' ); } @@ -290,12 +296,12 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ } else if (data.success) { var elem = $link; if (data.success === 'post_flagged_moderator') { - elem.html(' Flagged'); + 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.html(' Flagged'); + elem.data('href') && elem.html(' Flagged'); var forumAnswer = elem.closest('.forum_answer'); forumAnswer.fadeIn(1000); forumAnswer.slideUp(1000); @@ -331,7 +337,7 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ $link.parent().append($warning); } } else { - $link.parent().find('.vote_count').html(data['vote_count']); + $link.parent().find('.vote_count').html(data.vote_count); if (data.user_vote === 0) { $link.parent().find('.text-success').removeClass('text-success'); $link.parent().find('.text-warning').removeClass('text-warning'); @@ -432,14 +438,16 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ * @param {Event} ev */ _onCloseNotificationClick: function (ev) { - ev.preventDefault(); - var $link = $(ev.currentTarget); - this._rpc({ - route: '/forum/notification_read', - params: { - notification_id: $link.attr('id'), - }, - }); + if (!session.is_website_user) { + ev.preventDefault(); + var $link = $(ev.currentTarget); + this._rpc({ + route: '/forum/notification_read', + params: { + notification_id: $link.attr('id'), + }, + }); + } }, /** * @private @@ -477,83 +485,82 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ $('.forum_intro').slideUp(); return true; }, +}); + +sAnimations.registry.websiteForumSpam = sAnimations.Class.extend({ + selector: '.o_wforum_moderation_queue', + xmlDependencies: ['/website_forum/static/src/xml/website_forum_share_templates.xml'], + read_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 */ - _onCheckPostURL: function (ev) { - ev.preventDefault(); - var $link = $(ev.currentTarget); - var displayError = function () { - var $warning = $( - '<div class="alert alert-danger alert-dismissable" style="position:absolute; margin-top: -180px; margin-left: 90px;">' + - '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">×</button>' + - _t("Please enter valid URL. Example: http://www.odoo.com") + - '</div>' - ); - $link.parent().append($warning); - $link.parents('form').find('button')[0].disabled = true; - }; - var url = $link.val(); - if (url.search('^http(s?)://.*')) { - url = 'http://' + url; - } - - // https://gist.github.com/dperini/729294 - var regex = new RegExp( - "^" + - // protocol identifier - "(?:(?:https?|ftp)://)" + - // user:pass authentication - "(?:\\S+(?::\\S*)?@)?" + - "(?:" + - // IP address exclusion - // private & local networks - "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + - "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + - "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" + - // IP address dotted notation octets - // excludes loopback network 0.0.0.0 - // excludes reserved space >= 224.0.0.0 - // excludes network & broacast addresses - // (first & last IP address of each class) - "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + - "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + - "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + - "|" + - // host name - "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" + - // domain name - "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" + - // TLD identifier - "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" + - // TLD may end with dot - "\\.?" + - ")" + - // port number - "(?::\\d{2,5})?" + - // resource path - "(?:[/?#]\\S*)?" + - "$", "i" - ); + _onSelectallSpamClick: function (ev) { + var $spamInput = this.$('.modal .tab-pane.active input'); + $spamInput.prop('checked', true); + }, - if (regex.test(url)) { - this._rpc({ - route: '/forum/get_url_title', - params: { - url: url, - }, - }).then(function (data) { - if (data) { - $('input[name="post_name"]')[0].value = data; - $link.parents('form').find('button')[0].disabled = false; - } else { - displayError(); - } + /** + * @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); }); - } else { - displayError(); - } + 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 index 9da7059d13f..c1d0d8a1b4a 100644 --- a/addons/website_forum/static/src/js/website_forum.share.js +++ b/addons/website_forum/static/src/js/website_forum.share.js @@ -70,7 +70,7 @@ var ForumShare = sAnimations.registry.socialShare.extend({ }); sAnimations.registry.websiteForumShare = sAnimations.Class.extend({ - selector: '#wrapwrap:has(.website_forum)', + selector: '.website_forum', read_events: { 'click :not(.karma_required).oe_social_share_call': '_onShareCallClick', }, diff --git a/addons/website_forum/static/src/js/website_tour_forum.js b/addons/website_forum/static/src/js/website_tour_forum.js index ba178f8cbcb..560de25d451 100644 --- a/addons/website_forum/static/src/js/website_tour_forum.js +++ b/addons/website_forum/static/src/js/website_tour_forum.js @@ -24,7 +24,7 @@ odoo.define("website_forum.tour_forum", function (require) { content: _t("Click <em>Continue</em> to create the forum."), position: "right", }, { - trigger: ".btn-block a:first", + trigger: ".btn-lg.btn-block", position: "left", content: _t("Ask the question in this forum by clicking on the button."), }, { diff --git a/addons/website_forum/static/src/js/website_tour_forum_question.js b/addons/website_forum/static/src/js/website_tour_forum_question.js index 34c0737cc8b..57b94a3eb43 100644 --- a/addons/website_forum/static/src/js/website_tour_forum_question.js +++ b/addons/website_forum/static/src/js/website_tour_forum_question.js @@ -10,7 +10,7 @@ odoo.define('website_forum.tour_forum_question', function (require) { wait_for: base.ready(), }, [{ content: "Ask the question in this forum by clicking on the button.", - trigger: '.btn-block a:first', + trigger: '.btn-lg.btn-block', }, { content: "Give your question content.", trigger: 'input[name=post_name]', diff --git a/addons/website_forum/static/src/scss/website_forum.scss b/addons/website_forum/static/src/scss/website_forum.scss index b4fb02792c2..f834b8c3f73 100644 --- a/addons/website_forum/static/src/scss/website_forum.scss +++ b/addons/website_forum/static/src/scss/website_forum.scss @@ -1,76 +1,101 @@ +$gold: #ffcc00; +$silver: #cccccc; +$bronze: #eea91e; + +.question-block .question-name { + max-width: 80%; +} +.o_post_content { + pre { + padding: 1.5rem; + border-radius: 5px; + color: $white; + background-color: $gray-700; + } + blockquote { + position: relative; + padding-left: 3rem; + border-left: 2px solid $gray-500; + font-style: italic; + font-weight: lighter; + } +} .vote { min-width: 38px; margin-right: 12px; - .vote_count { - font-size: 160%; font-weight: bold; line-height: 12px; + font-size: 160%; } - .vote_down, .vote_up { font-size: 130%; } } -.author-box { - min-width: 200px; - padding: 4px; - background-color: #f5f5f5; -} - -.question-block { - margin-left: 50px; -} - .o_forum_tag_follow_box { - display: none; position: absolute; + display: none; z-index: 1; text-align: center; opacity: 0.9; } -.question { - clear: left; +.forum_answer { + position: relative; } -.question { - .question-name { - font-size: 150%; +.post_to_validate { + overflow: hidden; + .validate_answer { + display: none; } +} - .badge { - background-color: #ccc; - margin-left: 4px; +.o_wforum_post_count_answer_count { + border-left: 3px solid; +} +.validate_answer { + @include o-position-absolute(0,0,0,0); + height: 100%; + .bg-success { + @include o-position-absolute(0,0,0,0); + height: 100%; + width: 4px; } - - .badge-active { - background-color: #428bca; + span { + @include o-position-absolute(10px,0,0,-9px); + font-size: 20px; + i { + padding: 2px 3px; + border-radius: 100%; + } } +} - img { - max-width: 600px; - height: auto !important; - } + .user_name_people { + font-weight: bold; + max-width: 60%; } -.forum_answer { - img { - max-width: 600px; - height: auto !important; - } +.o_wforum_people_photo_container { + width: 33%; +} +.o_profile_main { + overflow: hidden; } img.o_forum_avatar { max-height: 40px; margin-right: 10px; + width: auto; } img.o_forum_avatar_big { max-width: 75px; margin-right: 10px; + width: auto; } .oe_grey { @@ -78,39 +103,46 @@ img.o_forum_avatar_big { } .badge-gold { - color: #ffcc00; + color: $gold; +} +.bg-gold { + background-color: $gold; } .badge-silver { - color: #cccccc; + color: $silver; +} +.bg-silver { + background-color: $silver; } .badge-bronze { - color: #eea91e; + color: $bronze; +} +.bg-bronze { + background-color: $bronze; } .oe_answer_true { - color: #3c763d; - + color: theme-color('success'); &:hover { - color: #4D964E; + color: lighten(theme-color('success'), 20%); } } .oe_answer_false { - color: #bbbbbb; - + color: $gray-400; &:hover { - color: #9E9C9C; + color: $gray-600; } } .favourite_question { - color: #bbbbbb !important; + color: $gray-400 !important; } .forum_favourite_question { - color: #ffcc00 !important; + color: $gold !important; } a.no-decoration { @@ -118,25 +150,24 @@ a.no-decoration { text-decoration: none !important; } -.faq-question:hover { - text-decoration: none !important; - color: #428bca; -} - -.oe_comment_grey { - background-color: #f5f5f5; - padding: 4px; -} - .close.comment_delete { - font-size: 16px; + font-size: 0.875rem; float: none; + opacity: 1; + font-weight: 300; + line-height: 1.5; } - .country_flag { - max-height: 16px; display: inline-block; margin-left: 2px; + max-height: 13px; + width: auto !important; +} + +img.country_flag_people { + @include o-position-absolute(0,0); + max-height: 25px; + width: auto; } .tag_text .text-core .text-wrap { @@ -168,13 +199,13 @@ a.no-decoration { } button.btn-link.text-muted { - color: #999; + color: $gray-600; } .o_forum_user_bio_expand { position: absolute; - z-index: 200; display: none; + z-index: 200; } .o_forum_tooltip_heading { @@ -184,7 +215,7 @@ button.btn-link.text-muted { } .o_forum_user_bio { - color: black; + color: $black; width: 318px; min-height: 100px; cursor: auto; @@ -193,11 +224,9 @@ button.btn-link.text-muted { line-height: 13px; font-size: 11px; word-wrap: break-word; - .o_forum_tooltip_line { min-height: 13px; } - .o_forum_tooltip_header { min-height: 75px; } 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 index 8cd8a27117e..205b05d9011 100644 --- a/addons/website_forum/static/src/xml/website_forum_share_templates.xml +++ b/addons/website_forum/static/src/xml/website_forum_share_templates.xml @@ -1,30 +1,46 @@ <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 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 class="share-icons text-center"> - <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 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 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 |
