diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..2b3ed3b --- /dev/null +++ b/.eslintrc @@ -0,0 +1,103 @@ +{ + "env": { + "jasmine": true, + "node": true, + "mocha": true, + "browser": true, + "builtin": true + }, + ecmaVersion: 7, + "globals": + {"Ember":true, + "jQuery":true, + "$":true, + "RSVP":true, + "Discourse":true, + "Em":true, + "PreloadStore":true, + "Handlebars":true, + "I18n":true, + "bootbox":true, + "module":true, + "moduleFor":true, + "moduleForComponent":true, + "Pretender":true, + "sandbox":true, + "controllerFor":true, + "test":true, + "ok":true, + "not":true, + "expect":true, + "equal":true, + "visit":true, + "andThen":true, + "click":true, + "currentPath":true, + "currentRouteName":true, + "currentURL":true, + "fillIn":true, + "keyEvent":true, + "triggerEvent":true, + "count":true, + "exists":true, + "visible":true, + "invisible":true, + "asyncRender":true, + "selectDropdown":true, + "asyncTestDiscourse":true, + "fixture":true, + "find":true, + "sinon":true, + "moment":true, + "start":true, + "_":true, + "alert":true, + "containsInstance":true, + "deepEqual":true, + "notEqual":true, + "define":true, + "require":true, + "requirejs":true, + "hasModule":true, + "Blob":true, + "File":true}, + "rules": { + "block-scoped-var": 2, + "dot-notation": 0, + "eqeqeq": [ + 2, + "allow-null" + ], + "guard-for-in": 2, + "no-bitwise": 2, + "no-caller": 2, + "no-cond-assign": 0, + "no-debugger": 2, + "no-empty": 0, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-parens": 0, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-loop-func": 2, + "no-multi-str": 2, + "no-new": 2, + "no-plusplus": 0, + "no-proto": 2, + "no-script-url": 2, + "no-sequences": 2, + "no-shadow": 2, + "no-undef": 2, + "no-unused-vars": 2, + "no-with": 2, + "no-this-before-super": 2, + "semi": 2, + "strict": 0, + "valid-typeof": 2, + "wrap-iife": [ + 2, + "inside" + ] + }, + "parser": "babel-eslint" +} diff --git a/assets/javascripts/discourse/initializers/extend-for-solved-button.js.es6 b/assets/javascripts/discourse/initializers/extend-for-solved-button.js.es6 index 03420f7..2700cf9 100644 --- a/assets/javascripts/discourse/initializers/extend-for-solved-button.js.es6 +++ b/assets/javascripts/discourse/initializers/extend-for-solved-button.js.es6 @@ -4,18 +4,165 @@ import { Button } from 'discourse/components/post-menu'; import Topic from 'discourse/models/topic'; import User from 'discourse/models/user'; import TopicStatus from 'discourse/views/topic-status'; -import computed from 'ember-addons/ember-computed-decorators'; import { popupAjaxError } from 'discourse/lib/ajax-error'; +import { withPluginApi } from 'discourse/lib/plugin-api'; + +function clearAccepted(topic) { + const posts = topic.get('postStream.posts'); + posts.forEach(post => { + if (post.get('post_number') > 1 ) { + post.set('accepted_answer',false); + post.set('can_accept_answer',true); + post.set('can_unaccept_answer',false); + } + }); +} + +function unacceptPost(post) { + if (!post.get('can_unaccept_answer')) { return; } + const topic = post.topic; + + post.setProperties({ + can_accept_answer: true, + can_unaccept_answer: false, + accepted_answer: false + }); + topic.set('accepted_answer', undefined); + + Discourse.ajax("/solution/unaccept", { + type: 'POST', + data: { id: post.get('id') } + }).catch(popupAjaxError); +} + +function acceptPost(post) { + const topic = post.topic; + + clearAccepted(topic); + + post.setProperties({ + can_unaccept_answer: true, + can_accept_answer: false, + accepted_answer: true + }); + + topic.setProperties({ + username: post.get('username'), + post_number: post.get('post_number') + }); + + Discourse.ajax("/solution/accept", { + type: 'POST', + data: { id: post.get('.id') } + }).catch(popupAjaxError); +} + +// Code for older discourse installs for backwards compatibility +function oldPluginCode() { + PostView.reopen({ + classNameBindings: ['post.accepted_answer:accepted-answer'] + }); + + PostMenuComponent.registerButton(function(visibleButtons){ + var position = 0; + + var canAccept = this.get('post.can_accept_answer'); + var canUnaccept = this.get('post.can_unaccept_answer'); + var accepted = this.get('post.accepted_answer'); + var isOp = Discourse.User.currentProp("id") === this.get('post.topic.user_id'); + + if (!accepted && canAccept && !isOp) { + // first hidden position + if (this.get('collapsed')) { return; } + position = visibleButtons.length - 2; + } + if (canAccept) { + visibleButtons.splice(position,0,new Button('acceptAnswer', 'solved.accept_answer', 'check-square-o', {className: 'unaccepted'})); + } + if (canUnaccept || accepted) { + var locale = canUnaccept ? 'solved.unaccept_answer' : 'solved.accepted_answer'; + visibleButtons.splice(position,0,new Button( + 'unacceptAnswer', + locale, + 'check-square', + {className: 'accepted fade-out', prefixHTML: '' + I18n.t('solved.solution') + ''}) + ); + } + + }); + + PostMenuComponent.reopen({ + acceptedChanged: function() { + this.rerender(); + }.observes('post.accepted_answer'), + + clickUnacceptAnswer() { + unacceptPost(this.get('post')); + }, + + clickAcceptAnswer() { + acceptPost(this.get('post')); + } + }); +} + +function initializeWithApi(api) { + const currentUser = api.getCurrentUser(); + + api.includePostAttributes('can_accept_answer', 'can_unaccept_answer', 'accepted_answer'); + + api.addPostMenuButton('solved', attrs => { + const canAccept = attrs.can_accept_answer; + const canUnaccept = attrs.can_unaccept_answer; + const accepted = attrs.accepted_answer; + const isOp = currentUser && currentUser.id === attrs.user_id; + const position = (!accepted && canAccept && !isOp) ? 'second-last-hidden' : 'first'; + + if (canAccept) { + return { + action: 'acceptAnswer', + icon: 'check-square-o', + className: 'unaccepted', + title: 'solved.accept_answer', + position + }; + } else if (canUnaccept || accepted) { + const title = canUnaccept ? 'solved.unaccept_answer' : 'solved.accepted_answer'; + return { + action: 'unacceptAnswer', + icon: 'check-square', + title, + className: 'accepted fade-out', + position, + beforeButton(h) { + return h('span.accepted-text', I18n.t('solved.solution')); + } + }; + } + }); + + api.attachWidgetAction('post', 'acceptAnswer', function() { + const post = this.model; + const current = post.get('topic.postStream.posts').filterProperty('accepted_answer'); + acceptPost(post); + + current.forEach(p => this.appEvents.trigger('post-stream:refresh', { id: p.id })); + }); + + api.attachWidgetAction('post', 'unacceptAnswer', function() { + unacceptPost(this.model); + }); +} export default { name: 'extend-for-solved-button', - initialize: function() { + initialize() { Topic.reopen({ // keeping this here cause there is complex localization - acceptedAnswerHtml: function(){ + acceptedAnswerHtml: function() { const username = this.get('accepted_answer.username'); - var postNumber = this.get('accepted_answer.post_number'); + const postNumber = this.get('accepted_answer.post_number'); if (!username || !postNumber) { return ""; @@ -33,7 +180,7 @@ export default { TopicStatus.reopen({ statuses: function(){ - var results = this._super(); + const results = this._super(); if (this.topic.has_accepted_answer) { results.push({ openTag: 'span', @@ -46,90 +193,6 @@ export default { }.property() }); - PostView.reopen({ - classNameBindings: ['post.accepted_answer:accepted-answer'] - }); - - PostMenuComponent.registerButton(function(visibleButtons){ - var position = 0; - - var canAccept = this.get('post.can_accept_answer'); - var canUnaccept = this.get('post.can_unaccept_answer'); - var accepted = this.get('post.accepted_answer'); - var isOp = User.currentProp("id") === this.get('post.topic.user_id'); - - if (!accepted && canAccept && !isOp) { - // first hidden position - if (this.get('collapsed')) { return; } - position = visibleButtons.length - 2; - } - if (canAccept) { - visibleButtons.splice(position,0,new Button('acceptAnswer', 'solved.accept_answer', 'check-square-o', {className: 'unaccepted'})); - } - if (canUnaccept || accepted) { - var locale = canUnaccept ? 'solved.unaccept_answer' : 'solved.accepted_answer'; - visibleButtons.splice(position,0,new Button( - 'unacceptAnswer', - locale, - 'check-square', - {className: 'accepted fade-out', prefixHTML: '' + I18n.t('solved.solution') + ''}) - ); - } - - }); - - PostMenuComponent.reopen({ - acceptedChanged: function(){ - this.rerender(); - }.observes('post.accepted_answer'), - - clickUnacceptAnswer: function(){ - if (!this.get('post.can_unaccept_answer')) { return; } - - this.set('post.can_accept_answer', true); - this.set('post.can_unaccept_answer', false); - this.set('post.accepted_answer', false); - this.set('post.topic.accepted_answer', undefined); - - Discourse.ajax("/solution/unaccept", { - type: 'POST', - data: { - id: this.get('post.id') - } - }).catch(popupAjaxError); - }, - - clearAcceptedAnswer: function(){ - const posts = this.get('post.topic.postStream.posts'); - posts.forEach(function(post){ - if (post.get('post_number') > 1 ) { - post.set('accepted_answer',false); - post.set('can_accept_answer',true); - post.set('can_unaccept_answer',false); - } - }); - }, - - clickAcceptAnswer: function(){ - - this.clearAcceptedAnswer(); - - this.set('post.can_unaccept_answer', true); - this.set('post.can_accept_answer', false); - this.set('post.accepted_answer', true); - - this.set('post.topic.accepted_answer', { - username: this.get('post.username'), - post_number: this.get('post.post_number') - }); - - Discourse.ajax("/solution/accept", { - type: 'POST', - data: { - id: this.get('post.id') - } - }).catch(popupAjaxError); - } - }); + withPluginApi('0.1', initializeWithApi, { noApi: oldPluginCode }); } }; diff --git a/plugin.rb b/plugin.rb index 758555c..6adb14e 100644 --- a/plugin.rb +++ b/plugin.rb @@ -207,20 +207,21 @@ after_initialize do topic = (topic_view && topic_view.topic) || object.topic if topic - scope.can_accept_answer?(topic) && - object.post_number > 1 && !accepted_answer + return scope.can_accept_answer?(topic) && object.post_number > 1 && !accepted_answer end + + false end def can_unaccept_answer topic = (topic_view && topic_view.topic) || object.topic if topic - scope.can_accept_answer?(topic) && post_custom_fields["is_accepted_answer"] + return scope.can_accept_answer?(topic) && (post_custom_fields["is_accepted_answer"] == 'true') end end def accepted_answer - post_custom_fields["is_accepted_answer"] + post_custom_fields["is_accepted_answer"] == 'true' end end