bcms_polling 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/GPL.txt +674 -0
- data/Gemfile +4 -0
- data/README.markdown +8 -11
- data/{public/bcms/polling → app/assets}/README +0 -0
- data/app/assets/javascripts/bcms_polling.js +5 -0
- data/{public/bcms/polling → app/assets/javascripts/bcms_polling}/bcms-polling.js +0 -0
- data/app/assets/stylesheets/bcms_polling/application.css +13 -0
- data/{public/bcms/polling → app/assets/stylesheets/bcms_polling}/bcms-polling.css +0 -0
- data/app/controllers/bcms_polling/polling_controller.rb +46 -0
- data/app/controllers/bcms_polling/polls_controller.rb +2 -0
- data/app/helpers/bcms_polling/application_helper.rb +4 -0
- data/app/helpers/{cms → bcms_polling}/polls_helper.rb +1 -1
- data/app/models/bcms_polling/poll.rb +38 -0
- data/app/models/bcms_polling/poll_response.rb +9 -0
- data/app/views/{polling → bcms_polling/polling}/results.html.erb +1 -1
- data/app/views/{cms → bcms_polling}/polls/_form.html.erb +1 -2
- data/app/views/{cms → bcms_polling}/polls/render.html.erb +3 -3
- data/config/routes.rb +3 -0
- data/db/bcms_polling.seeds.rb +1 -0
- data/db/migrate/20110216195733_create_polls.rb +0 -4
- data/db/migrate/20120530182315_v110.rb +8 -0
- data/lib/bcms_polling.rb +5 -1
- data/lib/bcms_polling/engine.rb +27 -0
- data/lib/bcms_polling/route_extensions.rb +13 -0
- data/lib/bcms_polling/version.rb +3 -0
- data/lib/generators/bcms_polling/install/USAGE +3 -0
- data/lib/generators/bcms_polling/install/install_generator.rb +21 -0
- data/test/bcms_polling_test.rb +7 -0
- data/test/test_helper.rb +9 -32
- data/test/unit/models/bcms_polling/poll_response_test.rb +11 -0
- data/test/unit/models/bcms_polling/poll_test.rb +52 -0
- metadata +82 -66
- data/Rakefile +0 -42
- data/VERSION +0 -1
- data/app/controllers/cms/polls_controller.rb +0 -2
- data/app/controllers/polling_controller.rb +0 -44
- data/app/models/poll.rb +0 -37
- data/app/models/poll_response.rb +0 -4
- data/doc/README_FOR_APP +0 -2
- data/lib/bcms_polling/routes.rb +0 -12
- data/rails/init.rb +0 -5
- data/test/performance/browsing_test.rb +0 -9
- data/test/unit/models/poll_response_test.rb +0 -9
- data/test/unit/models/poll_test.rb +0 -41
data/Gemfile
ADDED
data/README.markdown
CHANGED
@@ -21,20 +21,18 @@ vote multiple times from multiple computers or just clear their cookies to revot
|
|
21
21
|
Installation:
|
22
22
|
=============
|
23
23
|
|
24
|
-
|
24
|
+
$ rails g cms:install bcms_polling
|
25
|
+
$ rake db:install
|
25
26
|
|
26
27
|
Follow standard module installation after that.
|
27
28
|
|
28
|
-
Finally, edit your project's app/helpers/application_helper.rb so it looks like:
|
29
|
-
|
30
|
-
module ApplicationHelper
|
31
|
-
include Cms::PollsHelper
|
32
|
-
end
|
33
|
-
|
34
29
|
Finally, add the default stylesheet for polls to your template, tweaking as necessary or inlining into the site's main stylesheet.
|
35
30
|
|
36
|
-
<%= stylesheet_link_tag '/bcms
|
31
|
+
<%= stylesheet_link_tag 'bcms_polling/bcms-polling' %>
|
37
32
|
|
33
|
+
Update your config/application.rb with the following:
|
34
|
+
|
35
|
+
Rails.application.routes.default_url_options[:host] = config.cms.site_domain
|
38
36
|
|
39
37
|
|
40
38
|
Bugs:
|
@@ -43,6 +41,5 @@ Bugs:
|
|
43
41
|
|
44
42
|
BrowserCMS Core Notes:
|
45
43
|
================
|
46
|
-
* Using nested_attributes doesn't work without some monkeying around. Specifically, BrowserCMS doesn't consider nested attributes as 'changes', so
|
47
|
-
|
48
|
-
nested_attributes and save correctly if they are present.
|
44
|
+
* Using nested_attributes doesn't work without some monkeying around. Specifically, BrowserCMS doesn't consider nested attributes as 'changes', so updates (and a new version) doesn't get saved. Can fix this by timestamping 'updated_at' but the core CMS should be aware of nested_attributes and save correctly if they are present.
|
45
|
+
|
File without changes
|
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
File without changes
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Handles the public submissions of answers to poll questions.
|
2
|
+
module BcmsPolling
|
3
|
+
class PollingController < ApplicationController
|
4
|
+
include BcmsPolling::PollsHelper
|
5
|
+
include Cms::Authentication::Controller
|
6
|
+
|
7
|
+
def update
|
8
|
+
@response = PollResponse.find(params[:id])
|
9
|
+
unique_key = cookie_key(@response.poll)
|
10
|
+
|
11
|
+
if !cookies[unique_key]
|
12
|
+
@response.votes += 1
|
13
|
+
@response.save!
|
14
|
+
|
15
|
+
cookies[unique_key] = {
|
16
|
+
:value => true,
|
17
|
+
:expires => 1.year.from_now
|
18
|
+
}
|
19
|
+
logger.warn "Saving cookie called '#{unique_key}'"
|
20
|
+
else
|
21
|
+
logger.warn "User attempted to submit another answer to the same poll."
|
22
|
+
end
|
23
|
+
|
24
|
+
render :nothing => true, :status => :ok
|
25
|
+
end
|
26
|
+
|
27
|
+
def results
|
28
|
+
@poll = Poll.find(params[:id])
|
29
|
+
render :layout => false
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset
|
33
|
+
@poll = Poll.find(params[:id])
|
34
|
+
|
35
|
+
cookie = cookie_key(@poll)
|
36
|
+
cookies.delete(cookie)
|
37
|
+
|
38
|
+
redirect_to "/"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def cookie_key(poll)
|
43
|
+
cookie_for(poll)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module BcmsPolling
|
2
|
+
class Poll < ActiveRecord::Base
|
3
|
+
acts_as_content_block
|
4
|
+
|
5
|
+
has_many :responses, :class_name => "PollResponse"
|
6
|
+
accepts_nested_attributes_for :responses, :allow_destroy => true
|
7
|
+
attr_accessible :responses_attributes
|
8
|
+
validates_presence_of :question
|
9
|
+
|
10
|
+
after_validation :ensure_responses_save
|
11
|
+
|
12
|
+
def total_votes
|
13
|
+
self.responses.inject(0) { |sum, r| sum += r.votes }
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
question
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# This is a work around for bugs in CMS 3.5.x where updates to nested_associations:
|
23
|
+
# 1. Do not count as 'changes' to the parent model (so they don't trigger a version save)
|
24
|
+
# 2. Associated blocks don't save and/or delete themselves like nested_attributes says they should.
|
25
|
+
#
|
26
|
+
# Both of these will require changes to the core CMS to fix.
|
27
|
+
def ensure_responses_save
|
28
|
+
responses.each do |r|
|
29
|
+
if r.changed?
|
30
|
+
r.save
|
31
|
+
elsif r.marked_for_destruction?
|
32
|
+
responses.delete(r)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<% end %>
|
7
7
|
</ul>
|
8
8
|
<% if cookies[cookie_for(@poll)] && current_user.able_to?(:edit_content)-%>
|
9
|
-
<%= link_to "Vote Again (Reset Cookie)", poll_reset_path(@poll) %>
|
9
|
+
<%= link_to "Vote Again (Reset Cookie)", main_app.poll_reset_path(@poll) %>
|
10
10
|
<% end %>
|
11
11
|
<% if params[:return] == 'true' %>
|
12
12
|
<%= link_to_function "Back to Poll", 'history.go(0);return false;' %>
|
@@ -51,8 +51,7 @@
|
|
51
51
|
|
52
52
|
<div id="responses">
|
53
53
|
<% count = 0 -%>
|
54
|
-
|
55
|
-
<% f.fields_for(:responses, available_responses) do |response_fields| %>
|
54
|
+
<%= f.fields_for :responses do |response_fields| %>
|
56
55
|
<%= response_fields.cms_text_field :answer, :label => "Response ##{count = count + 1}" %>
|
57
56
|
<%= link_to_function('Delete', 'removeResponse(this)', 'data-count-id'=>count-1, :style=>'position: relative; left: 465px; top: -25px;') %>
|
58
57
|
<%= response_fields.check_box :_destroy, :style=>"display:none" %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<% unless self.controller.instance_variable_defined? :@include_js_http_methods -%>
|
2
|
-
<%= javascript_include_tag '
|
2
|
+
<%= javascript_include_tag 'bcms_polling' %>
|
3
3
|
<%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? -%>
|
4
4
|
<% self.controller.instance_variable_set(:@include_js_http_methods, true) -%>
|
5
5
|
<% end -%>
|
@@ -9,10 +9,10 @@
|
|
9
9
|
<span class="question"><%= @content_block.question %></span>
|
10
10
|
|
11
11
|
<ul class="vote-choices">
|
12
|
-
<% for response in PollResponse.find(:all, :conditions => {:poll_id => @content_block.id}) %>
|
12
|
+
<% for response in BcmsPolling::PollResponse.find(:all, :conditions => {:poll_id => @content_block.id}) %>
|
13
13
|
<li>
|
14
14
|
<span class="response"><%= response.answer %></span>
|
15
|
-
<%= link_to "Vote!", poll_response_url(response), :class => "vote-this", 'data-poll-id'=>@content_block.id %>
|
15
|
+
<%= link_to "Vote!", main_app.poll_response_url(response), :class => "vote-this", 'data-poll-id'=>@content_block.id %>
|
16
16
|
</li>
|
17
17
|
<% end %>
|
18
18
|
</ul>
|
data/config/routes.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Cms::ContentType.create!(:name => "BcmsPolling::Poll", :group_name => "Polling")
|
@@ -11,13 +11,9 @@ class CreatePolls < ActiveRecord::Migration
|
|
11
11
|
t.belongs_to :poll
|
12
12
|
end
|
13
13
|
|
14
|
-
ContentType.create!(:name => "Poll", :group_name => "Polling")
|
15
14
|
end
|
16
15
|
|
17
16
|
def self.down
|
18
|
-
ContentType.delete_all(['name = ?', 'Poll'])
|
19
|
-
CategoryType.all(:conditions => ['name = ?', 'Poll']).each(&:destroy)
|
20
|
-
#If you aren't creating a versioned table, be sure to comment this out.
|
21
17
|
drop_table :poll_responses
|
22
18
|
drop_table :poll_versions
|
23
19
|
drop_table :polls
|
data/lib/bcms_polling.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'browsercms'
|
2
|
+
module BcmsPolling
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
isolate_namespace BcmsPolling
|
5
|
+
include Cms::Module
|
6
|
+
|
7
|
+
config.to_prepare do |app|
|
8
|
+
Cms::ViewContext.send(:include, BcmsPolling::MainAppPatch)
|
9
|
+
Cms::ViewContext.send(:include, BcmsPolling::PollsHelper)
|
10
|
+
|
11
|
+
::ApplicationHelper.send(:include, BcmsPolling::PollsHelper)
|
12
|
+
end
|
13
|
+
|
14
|
+
initializer 'bcms_polling.route_extensions', :after => 'action_dispatch.prepare_dispatcher' do |app|
|
15
|
+
ActionDispatch::Routing::Mapper.send :include, BcmsPolling::RouteExtensions
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Patches Core CMS so views can access the main_app routes.
|
20
|
+
module MainAppPatch
|
21
|
+
|
22
|
+
def main_app
|
23
|
+
Rails.application.routes.url_helpers
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module BcmsPolling::RouteExtensions
|
2
|
+
def mount_bcms_polling
|
3
|
+
|
4
|
+
# These routes are added to the main routes of the application, rather than under the engine
|
5
|
+
# In retrospect, its probably better to put them into the engine to reduce the chance of conflicts.
|
6
|
+
get '/polls/:id/results', to:"bcms_polling/polling#results", as: 'poll_results'
|
7
|
+
get '/polls/:id/reset', to:"bcms_polling/polling#reset", as: 'poll_reset'
|
8
|
+
put '/poll_responses/:id', to:"bcms_polling/polling#update", as: 'poll_response'
|
9
|
+
|
10
|
+
mount BcmsPolling::Engine => "/bcms_polling"
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'cms/module_installation'
|
2
|
+
|
3
|
+
class BcmsPolling::InstallGenerator < Cms::ModuleInstallation
|
4
|
+
add_migrations_directory_to_source_root __FILE__
|
5
|
+
|
6
|
+
|
7
|
+
def copy_migrations
|
8
|
+
rake 'bcms_polling:install:migrations'
|
9
|
+
end
|
10
|
+
|
11
|
+
# Uncomment to add module specific seed data to a project.
|
12
|
+
def add_seed_data_to_project
|
13
|
+
copy_file "../bcms_polling.seeds.rb", "db/bcms_polling.seeds.rb"
|
14
|
+
append_to_file "db/seeds.rb", "load File.expand_path('../bcms_polling.seeds.rb', __FILE__)\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_routes
|
18
|
+
route 'mount_bcms_polling'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,38 +1,15 @@
|
|
1
|
+
# Configure Rails Environment
|
1
2
|
ENV["RAILS_ENV"] = "test"
|
2
|
-
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
3
|
-
require 'test_help'
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
# in a transaction that's rolled back on completion. This ensures that the
|
8
|
-
# test database remains unchanged so your fixtures don't have to be reloaded
|
9
|
-
# between every test method. Fewer database queries means faster tests.
|
10
|
-
#
|
11
|
-
# Read Mike Clark's excellent walkthrough at
|
12
|
-
# http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
|
13
|
-
#
|
14
|
-
# Every Active Record database supports transactions except MyISAM tables
|
15
|
-
# in MySQL. Turn off transactional fixtures in this case; however, if you
|
16
|
-
# don't care one way or the other, switching from MyISAM to InnoDB tables
|
17
|
-
# is recommended.
|
18
|
-
#
|
19
|
-
# The only drawback to using transactional fixtures is when you actually
|
20
|
-
# need to test transactions. Since your test is bracketed by a transaction,
|
21
|
-
# any transactions started in your code will be automatically rolled back.
|
22
|
-
self.use_transactional_fixtures = true
|
4
|
+
require File.expand_path("../dummy/config/environment.rb", __FILE__)
|
5
|
+
require "rails/test_help"
|
23
6
|
|
24
|
-
|
25
|
-
# would need people(:david). If you don't want to migrate your existing
|
26
|
-
# test cases which use the @david style and don't mind the speed hit (each
|
27
|
-
# instantiated fixtures translates to a database query per test method),
|
28
|
-
# then set this back to true.
|
29
|
-
self.use_instantiated_fixtures = false
|
7
|
+
Rails.backtrace_cleaner.remove_silencers!
|
30
8
|
|
31
|
-
|
32
|
-
|
33
|
-
# Note: You'll currently still have to declare fixtures explicitly in integration tests
|
34
|
-
# -- they do not yet inherit this setting
|
35
|
-
fixtures :all
|
9
|
+
# Load support files
|
10
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
36
11
|
|
37
|
-
|
12
|
+
# Load fixtures from the engine
|
13
|
+
if ActiveSupport::TestCase.method_defined?(:fixture_path=)
|
14
|
+
ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
|
38
15
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module BcmsPolling
|
4
|
+
class PollTest < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
test "should be able to create new block" do
|
7
|
+
assert Poll.create!(:question=>"A?")
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
test "Should have responses" do
|
12
|
+
p = Poll.create!(:question=>"What color?")
|
13
|
+
|
14
|
+
p.responses << PollResponse.create!(:answer=>"Red")
|
15
|
+
p.responses << PollResponse.create!(:answer=>"Blue")
|
16
|
+
|
17
|
+
found = Poll.find(p.id)
|
18
|
+
assert_equal 2, p.responses.size
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
test "Should have responses unpublished" do
|
23
|
+
p = Poll.create!(:question=>"What color?")
|
24
|
+
|
25
|
+
p.responses << PollResponse.create!(:answer=>"Red")
|
26
|
+
p.responses << PollResponse.create!(:answer=>"Blue")
|
27
|
+
|
28
|
+
found = Poll.find(p.id)
|
29
|
+
found = found.as_of_draft_version
|
30
|
+
assert_equal 2, p.responses.size
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
test "Binding responses as nested attributes for new object" do
|
35
|
+
poll = Poll.create!(:question=>"Are you different?")
|
36
|
+
poll.update_attributes({ :responses_attributes=>{"0"=>{"answer"=>"Answer 1"}}})
|
37
|
+
assert_equal 1, poll.responses.size
|
38
|
+
assert_equal 1, PollResponse.count
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
test "Delete a response" do
|
43
|
+
poll = Poll.create!(:question=>"Are you different?")
|
44
|
+
poll.update_attributes({ :responses_attributes=>{"0"=>{"answer"=>"Answer 1"}}})
|
45
|
+
|
46
|
+
poll.update_attributes({ :responses_attributes=>{"0"=>{"id"=>PollResponse.first.id, "answer"=>"Answer 1", "_destroy" => "1"}}})
|
47
|
+
|
48
|
+
assert_equal 0, poll.responses.size
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|