Merge pull request #2262 from vikhyat/poll-plugin

Add open/close buttons to poll UI
This commit is contained in:
Sam 2014-04-14 10:54:10 +10:00
commit fc1f225091
7 changed files with 152 additions and 46 deletions

View File

@ -9,7 +9,7 @@ Allows you to add a poll to the first post of a topic.
## Closing the poll ## Closing the poll
Change the start of the topic title from "Poll: " to "Closed Poll: " Change the start of the topic title from "Poll: " to "Closed Poll: ". This feature is disabled if the `allow_user_locale` site setting is enabled.
_Note: closing a topic will also close the poll._ _Note: closing a topic will also close the poll._
@ -21,12 +21,12 @@ list to be used like this:
``` ```
Intro Text Intro Text
- Item one - Item one
- Item two - Item two
Here are your choices: Here are your choices:
[poll] [poll]
- Option 1 - Option 1
- Option 2 - Option 2

View File

@ -14,14 +14,28 @@
{{/each}} {{/each}}
</table> </table>
<button {{action toggleShowResults}}> <button {{action toggleShowResults}} class="btn btn-small">
{{#if showResults}} {{#if showResults}}
<i class="fa fa-eye-slash"></i>
{{i18n poll.results.hide}} {{i18n poll.results.hide}}
{{else}} {{else}}
<i class="fa fa-eye"></i>
{{i18n poll.results.show}} {{i18n poll.results.show}}
{{/if}} {{/if}}
</button> </button>
{{#if controller.showToggleClosePoll}}
<button {{action toggleClosePoll}} class="btn btn-small">
{{#if poll.closed}}
<i class="fa fa-unlock-alt"></i>
{{i18n poll.open_poll}}
{{else}}
<i class="fa fa-lock"></i>
{{i18n poll.close_poll}}
{{/if}}
</button>
{{/if}}
{{#if loading}} {{#if loading}}
<i class="fa fa-spin fa-spinner"></i> <i class="fa fa-spin fa-spinner"></i>
{{/if}} {{/if}}

View File

@ -44,8 +44,10 @@ var Poll = Discourse.Model.extend({
var PollController = Discourse.Controller.extend({ var PollController = Discourse.Controller.extend({
poll: null, poll: null,
showResults: Em.computed.oneWay('poll.closed'), showResults: Em.computed.oneWay('poll.closed'),
disableRadio: Em.computed.any('poll.closed', 'loading'), disableRadio: Em.computed.any('poll.closed', 'loading'),
showToggleClosePoll: function() {
return this.get('poll.post.topic.details.can_edit') && !Discourse.SiteSettings.allow_user_locale;
}.property('poll.post.topic.details.can_edit'),
actions: { actions: {
selectOption: function(option) { selectOption: function(option) {
@ -67,6 +69,18 @@ var PollController = Discourse.Controller.extend({
toggleShowResults: function() { toggleShowResults: function() {
this.set('showResults', !this.get('showResults')); this.set('showResults', !this.get('showResults'));
},
toggleClosePoll: function() {
this.set('loading', true);
return Discourse.ajax("/poll/toggle_close", {
type: "PUT",
data: {post_id: this.get('poll.post.id')}
}).then(function(topicJson) {
this.set('poll.post.topic.title', topicJson.basic_topic.title);
this.set('poll.post.topic.fancy_title', topicJson.basic_topic.title);
this.set('loading', false);
}.bind(this));
} }
} }
}); });

View File

@ -15,3 +15,6 @@ en:
results: results:
show: Show Results show: Show Results
hide: Hide Results hide: Hide Results
close_poll: "Close Poll"
open_poll: "Open Poll"

View File

@ -58,11 +58,45 @@ after_initialize do
render json: poll.serialize(current_user) render json: poll.serialize(current_user)
end end
def toggle_close
post = Post.find(params[:post_id])
topic = post.topic
poll = PollPlugin::Poll.new(post)
# Make sure the user is allowed to close the poll.
Guardian.new(current_user).ensure_can_edit!(topic)
# Make sure this is actually a poll.
unless poll.has_poll_details?
render status: 400, json: false
return
end
# Make sure the topic is not closed.
if topic.closed?
render status: 400, json: false
return
end
# Modify topic title.
if topic.title =~ /^(#{I18n.t('poll.prefix').strip})\s?:/i
topic.title = topic.title.gsub(/^(#{I18n.t('poll.prefix').strip})\s?:/i, I18n.t('poll.closed_prefix') + ':')
elsif topic.title =~ /^(#{I18n.t('poll.closed_prefix').strip})\s?:/i
topic.title = topic.title.gsub(/^(#{I18n.t('poll.closed_prefix').strip})\s?:/i, I18n.t('poll.prefix') + ':')
end
topic.acting_user = current_user
topic.save!
render json: topic, serializer: BasicTopicSerializer
end
end end
end end
PollPlugin::Engine.routes.draw do PollPlugin::Engine.routes.draw do
put '/' => 'poll#vote' put '/' => 'poll#vote'
put '/toggle_close' => 'poll#toggle_close'
end end
Discourse::Application.routes.append do Discourse::Application.routes.append do
@ -147,4 +181,8 @@ register_css <<CSS
border: none; border: none;
} }
.poll-ui button i.fa {
margin-right: 2px;
}
CSS CSS

View File

@ -60,7 +60,7 @@ module ::PollPlugin
end end
def is_closed? def is_closed?
@post.topic.closed? || (@post.topic.title =~ /^#{I18n.t('poll.closed_prefix')}/i) === 0 @post.topic.closed? || @post.topic.archived? || (!SiteSetting.allow_user_locale? && (@post.topic.title =~ /^#{I18n.t('poll.closed_prefix')}/i) === 0)
end end
def options def options

View File

@ -2,52 +2,89 @@ require 'spec_helper'
describe PollPlugin::PollController, type: :controller do describe PollPlugin::PollController, type: :controller do
let(:topic) { create_topic(title: "Poll: Chitoge vs Onodera") } let(:topic) { create_topic(title: "Poll: Chitoge vs Onodera") }
let(:post) { create_post(topic: topic, raw: "Pick one.\n\n[poll]\n* Chitoge\n* Onodera\n[/poll]") } let!(:post) { create_post(topic: topic, raw: "Pick one.\n\n[poll]\n* Chitoge\n* Onodera\n[/poll]") }
let(:user1) { Fabricate(:user) } let(:user1) { Fabricate(:user) }
let(:user2) { Fabricate(:user) } let(:user2) { Fabricate(:user) }
let(:admin) { Fabricate(:admin) }
it "should return 403 if no user is logged in" do describe 'vote' do
xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll it "returns 403 if no user is logged in" do
response.should be_forbidden xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll
response.should be_forbidden
end
it "returns 400 if post_id or invalid option is not specified" do
log_in_user user1
xhr :put, :vote, use_route: :poll
response.status.should eq(400)
xhr :put, :vote, post_id: post.id, use_route: :poll
response.status.should eq(400)
xhr :put, :vote, option: "Chitoge", use_route: :poll
response.status.should eq(400)
xhr :put, :vote, post_id: post.id, option: "Tsugumi", use_route: :poll
response.status.should eq(400)
end
it "returns 400 if post_id doesn't correspond to a poll post" do
log_in_user user1
post2 = create_post(topic: topic, raw: "Generic reply")
xhr :put, :vote, post_id: post2.id, option: "Chitoge", use_route: :poll
end
it "saves votes correctly" do
MessageBus.expects(:publish).times(3)
log_in_user user1
xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll
PollPlugin::Poll.new(post).get_vote(user1).should eq("Chitoge")
log_in_user user2
xhr :put, :vote, post_id: post.id, option: "Onodera", use_route: :poll
PollPlugin::Poll.new(post).get_vote(user2).should eq("Onodera")
PollPlugin::Poll.new(post).details["Chitoge"].should eq(1)
PollPlugin::Poll.new(post).details["Onodera"].should eq(1)
xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll
PollPlugin::Poll.new(post).get_vote(user2).should eq("Chitoge")
PollPlugin::Poll.new(post).details["Chitoge"].should eq(2)
PollPlugin::Poll.new(post).details["Onodera"].should eq(0)
end
end end
it "should return 400 if post_id or invalid option is not specified" do describe 'toggle_close' do
log_in_user user1 it "returns 400 if post_id doesn't correspond to a poll post" do
xhr :put, :vote, use_route: :poll log_in_user admin
response.status.should eq(400) post2 = create_post(topic: topic, raw: "Generic reply")
xhr :put, :vote, post_id: post.id, use_route: :poll xhr :put, :toggle_close, post_id: post2.id, use_route: :poll
response.status.should eq(400) response.status.should eq(400)
xhr :put, :vote, option: "Chitoge", use_route: :poll end
response.status.should eq(400)
xhr :put, :vote, post_id: post.id, option: "Tsugumi", use_route: :poll
response.status.should eq(400)
end
it "should return 400 if post_id doesn't correspond to a poll post" do it "returns 400 if the topic is locked" do
log_in_user user1 log_in_user admin
post2 = create_post(topic: topic, raw: "Generic reply") topic.update_attributes closed: true
xhr :put, :vote, post_id: post2.id, option: "Chitoge", use_route: :poll xhr :put, :toggle_close, post_id: post.id, use_route: :poll
response.status.should eq(400) response.status.should eq(400)
end end
it "should save votes correctly" do it "raises Discourse::InvalidAccess is the user is not authorized" do
MessageBus.expects(:publish).times(4) log_in_user user1
expect do
xhr :put, :toggle_close, post_id: post.id, use_route: :poll
end.to raise_error(Discourse::InvalidAccess)
end
log_in_user user1 it "renames the topic" do
xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll I18n.stubs(:t).with('poll.prefix').returns("Poll ")
PollPlugin::Poll.new(post).get_vote(user1).should eq("Chitoge") I18n.stubs(:t).with('poll.closed_prefix').returns("Closed Poll ")
log_in_user admin
log_in_user user2 xhr :put, :toggle_close, post_id: post.id, use_route: :poll
xhr :put, :vote, post_id: post.id, option: "Onodera", use_route: :poll response.status.should eq(200)
PollPlugin::Poll.new(post).get_vote(user2).should eq("Onodera") topic.reload.title.should == "Closed Poll : Chitoge vs Onodera"
xhr :put, :toggle_close, post_id: post.id, use_route: :poll
PollPlugin::Poll.new(post).details["Chitoge"].should eq(1) response.status.should eq(200)
PollPlugin::Poll.new(post).details["Onodera"].should eq(1) topic.reload.title.should == "Poll : Chitoge vs Onodera"
end
xhr :put, :vote, post_id: post.id, option: "Chitoge", use_route: :poll
PollPlugin::Poll.new(post).get_vote(user2).should eq("Chitoge")
PollPlugin::Poll.new(post).details["Chitoge"].should eq(2)
PollPlugin::Poll.new(post).details["Onodera"].should eq(0)
end end
end end