e9_polls 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.rdoc +30 -0
- data/Rakefile +2 -0
- data/app/controllers/e9_polls/polls_controller.rb +125 -0
- data/app/helpers/e9_polls/polls_helper.rb +108 -0
- data/app/models/poll.rb +49 -0
- data/app/models/poll_answer.rb +20 -0
- data/app/views/e9_polls/poll_answers/_nested_attribute_template.html.haml +9 -0
- data/app/views/e9_polls/poll_answers/_poll_answer.html.haml +18 -0
- data/app/views/e9_polls/polls/_form.html.haml +26 -0
- data/app/views/e9_polls/polls/_poll.html.haml +15 -0
- data/app/views/e9_polls/polls/_results.html.haml +6 -0
- data/app/views/e9_polls/polls/_results_inner.html.haml +2 -0
- data/app/views/e9_polls/polls/_table.html.haml +18 -0
- data/app/views/e9_polls/polls/create.js.erb +6 -0
- data/app/views/e9_polls/polls/destroy.js.erb +3 -0
- data/app/views/e9_polls/polls/edit.html.haml +2 -0
- data/app/views/e9_polls/polls/index.html.haml +10 -0
- data/app/views/e9_polls/polls/index.js.erb +1 -0
- data/app/views/e9_polls/polls/new.html.haml +2 -0
- data/app/views/e9_polls/polls/results.html.haml +2 -0
- data/app/views/e9_polls/polls/show.html.haml +2 -0
- data/app/views/e9_polls/polls/update.js.erb +8 -0
- data/config/locales/en.yml +38 -0
- data/config/routes.rb +17 -0
- data/e9_polls.gemspec +26 -0
- data/e9_polls.js +44 -0
- data/lib/e9_polls/global_helper.rb +26 -0
- data/lib/e9_polls/model.rb +14 -0
- data/lib/e9_polls/version.rb +3 -0
- data/lib/e9_polls.rb +26 -0
- data/lib/generators/e9_polls/install_generator.rb +32 -0
- data/lib/generators/e9_polls/templates/initializer.rb +11 -0
- data/lib/generators/e9_polls/templates/javascript.js +115 -0
- data/lib/generators/e9_polls/templates/migration.rb +13 -0
- data/lib/generators/e9_polls/templates/stylesheet.css +24 -0
- metadata +124 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
** NOTE - This gem depends on e9_base, but does not reference it. It WILL NOT FUNCTION for apps which aren't built on the e9 Rails 3 CMS **
|
2
|
+
|
3
|
+
== E9Polls
|
4
|
+
|
5
|
+
Provites a Poll renderable for the e9 Rails 3 CMS.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
1. Include the gem and run the install generator to copy over the necessary files,
|
10
|
+
then migrate.
|
11
|
+
|
12
|
+
rails g e9_polls:install
|
13
|
+
|
14
|
+
This will install the db migration, the JS and CSS required for the plugin to
|
15
|
+
function properly, and an initializer.
|
16
|
+
|
17
|
+
Modify the CSS as you see fit and the JS as required (carefully).
|
18
|
+
|
19
|
+
Check out the initializer and modify if necessary. For non-Ajax fallbacks it uses
|
20
|
+
the 'application' layout. This should be changed if the app doesn't use application
|
21
|
+
layout as a sensible default.
|
22
|
+
|
23
|
+
2. Migrate the database.
|
24
|
+
|
25
|
+
rake db:migrate
|
26
|
+
|
27
|
+
3. Finally, include the generated javascript and css (e9_polls.js and e9_polls.css)
|
28
|
+
in the fashion suited to the app.
|
29
|
+
|
30
|
+
4. There is no #4.
|
data/Rakefile
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
class E9Polls::PollsController < AdminController
|
2
|
+
include E9Rails::Helpers::Translation
|
3
|
+
include E9Rails::Helpers::Title
|
4
|
+
include E9Rails::Helpers::ResourceErrorMessages
|
5
|
+
include E9Rails::Helpers::Pagination
|
6
|
+
include E9Rails::Controllers::Orderable
|
7
|
+
|
8
|
+
inherit_resources
|
9
|
+
|
10
|
+
respond_to :json, :only => [:answer, :show]
|
11
|
+
|
12
|
+
defaults :route_prefix => :admin, :resource_class => Poll
|
13
|
+
before_filter :except => [:show, :answer, :results] {|c| @route_scope = :admin }
|
14
|
+
skip_before_filter :authenticate_user!, :filter_access_filter, :only => [:show, :answer, :results]
|
15
|
+
|
16
|
+
add_resource_breadcrumbs
|
17
|
+
|
18
|
+
def create
|
19
|
+
create! { collection_path }
|
20
|
+
end
|
21
|
+
|
22
|
+
def update
|
23
|
+
update! do |format|
|
24
|
+
format.html { redirect_to collection_path }
|
25
|
+
format.js { resource.reload; render }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def answer
|
30
|
+
object = resource
|
31
|
+
|
32
|
+
if vote = params[resource_instance_name] && params[resource_instance_name][:vote]
|
33
|
+
if cookie[object.id].present?
|
34
|
+
# already voted error
|
35
|
+
object.errors.add(:vote, :already_voted)
|
36
|
+
else
|
37
|
+
# new vote OK!
|
38
|
+
object.vote = cookie[object.id] = vote
|
39
|
+
store_cookie
|
40
|
+
end
|
41
|
+
else
|
42
|
+
object.errors.add(:vote, :no_argument)
|
43
|
+
end
|
44
|
+
|
45
|
+
respond_with(object) do |format|
|
46
|
+
format.html { redirect_to poll_url(object) }
|
47
|
+
|
48
|
+
format.json do
|
49
|
+
if object.errors[:vote].present?
|
50
|
+
flash[:alert] = object.errors[:vote]
|
51
|
+
head 400
|
52
|
+
else
|
53
|
+
flash[:notice] = I18n.t(:success_message, :scope => :e9_polls)
|
54
|
+
render(:json => { :poll => object, :html => render_html_for_action('results') })
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def results
|
61
|
+
show! do |format|
|
62
|
+
format.json do
|
63
|
+
render(:json => { :poll => resource, :html => render_html_for_action })
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def show
|
69
|
+
show! do |format|
|
70
|
+
format.json do
|
71
|
+
render(:json => { :poll => resource, :html => render_html_for_action })
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def render_html_for_action(action = nil)
|
79
|
+
action ||= params[:action]
|
80
|
+
|
81
|
+
html = nil
|
82
|
+
|
83
|
+
lookup_context.update_details(:formats => [Mime::HTML.to_sym]) do
|
84
|
+
html = render_to_string(action, :layout => false)
|
85
|
+
end
|
86
|
+
|
87
|
+
html
|
88
|
+
end
|
89
|
+
|
90
|
+
def cookie
|
91
|
+
@_e9_polls_cookie ||= Marshal.load(cookies[E9Polls.cookie_name]) rescue {}
|
92
|
+
end
|
93
|
+
|
94
|
+
def store_cookie
|
95
|
+
cookies.permanent[E9Polls.cookie_name] = {
|
96
|
+
:value => Marshal.dump(cookie),
|
97
|
+
:expires => 1.year.from_now
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def parent; end
|
102
|
+
helper_method :parent
|
103
|
+
|
104
|
+
def collection
|
105
|
+
get_collection_ivar || set_collection_ivar(end_of_association_chain.paginate(pagination_parameters))
|
106
|
+
end
|
107
|
+
|
108
|
+
def default_ordered_on
|
109
|
+
'name'
|
110
|
+
end
|
111
|
+
|
112
|
+
def default_ordered_dir
|
113
|
+
'ASC'
|
114
|
+
end
|
115
|
+
|
116
|
+
def determine_layout
|
117
|
+
if request.xhr?
|
118
|
+
false
|
119
|
+
elsif %w(show results answer).member? params[:action]
|
120
|
+
E9Polls.fallback_html_layout
|
121
|
+
else
|
122
|
+
super
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module E9Polls::PollsHelper
|
2
|
+
|
3
|
+
##
|
4
|
+
# Util
|
5
|
+
#
|
6
|
+
|
7
|
+
def html_concat(*chunks)
|
8
|
+
''.html_safe.tap do |html|
|
9
|
+
chunks.each {|chunk| html.safe_concat("#{chunk}\n") }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def e9_polls_resource_link(action, record, opts = {})
|
14
|
+
opts.symbolize_keys!
|
15
|
+
|
16
|
+
# NOTE cascading backend for i18n would accomplish the lookup without defaults
|
17
|
+
|
18
|
+
action = action.to_sym
|
19
|
+
klass = record.is_a?(Class) ? record : record.class
|
20
|
+
scope = "e9_polls.#{klass.model_name.collection}"
|
21
|
+
|
22
|
+
# NOTE this assumes the definition of #parent, which is added by IR but only on controllers
|
23
|
+
# with polymorphic belongs_to relationships (by default).
|
24
|
+
scopes = [*(opts[:scope] || @route_scope), parent].compact
|
25
|
+
path = case action.to_sym
|
26
|
+
when :new; new_polymorphic_path(scopes << klass)
|
27
|
+
when :edit; edit_polymorphic_path(scopes << record)
|
28
|
+
else polymorphic_path(scopes << record)
|
29
|
+
end
|
30
|
+
|
31
|
+
if action == :destroy
|
32
|
+
opts[:method] = :delete
|
33
|
+
opts.reverse_merge!({
|
34
|
+
:confirm => t(:"#{scope}.confirm_destroy",
|
35
|
+
:default => :"e9_polls.confirm_destroy")
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
link_to t(:"#{scope}.#{action}", :default => :"e9_polls.#{action}"), path, opts
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Links
|
44
|
+
#
|
45
|
+
|
46
|
+
def records_table_links_for_record(record)
|
47
|
+
if respond_to?(method_name = "records_table_links_for_#{record.class.name.underscore}")
|
48
|
+
send(method_name, record)
|
49
|
+
else
|
50
|
+
html_concat(
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def link_to_add_nested_attribute(association_name)
|
56
|
+
link_to( t(:add_nested_attribute, :scope => :e9_polls), '#', :class => 'add-nested-association', 'data-association' => association_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
def link_to_destroy_nested_attribute
|
60
|
+
link_to( t(:destroy_nested_attribute, :scope => :e9_polls), '#', :class => 'destroy-nested-association')
|
61
|
+
end
|
62
|
+
|
63
|
+
def render_nested_attribute_association(association_name, form, options = {})
|
64
|
+
options.symbolize_keys!
|
65
|
+
|
66
|
+
association = resource.send(association_name)
|
67
|
+
|
68
|
+
unless association.empty?
|
69
|
+
form.fields_for(association_name) do |f|
|
70
|
+
concat nested_attribute_template(association_name, f, options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def nested_attribute_template_js(klass, association_name, options = {})
|
76
|
+
options.symbolize_keys!
|
77
|
+
options[:index] ||= 10000
|
78
|
+
|
79
|
+
template = nil
|
80
|
+
|
81
|
+
fields_for(klass.new) do |f|
|
82
|
+
f.object.send(association_name).build
|
83
|
+
f.fields_for(association_name, :child_index => options[:index]) do |ff|
|
84
|
+
template = "#{escape_javascript(nested_attribute_template(association_name, ff))}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
retv = <<-RETV
|
89
|
+
TEMPLATES = window.TEMPLATES || {};
|
90
|
+
TEMPLATES['#{association_name}'] = {
|
91
|
+
index: #{options[:index]},
|
92
|
+
template: "#{template}"
|
93
|
+
};
|
94
|
+
RETV
|
95
|
+
end
|
96
|
+
|
97
|
+
def nested_attribute_template(association_name, builder, options = {})
|
98
|
+
options.symbolize_keys!
|
99
|
+
partial = options[:partial] || File.join('e9_polls', builder.object.class.model_name.collection, 'nested_attribute_template')
|
100
|
+
render(:partial => partial, :locals => { :f => builder })
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_associated_resource(association_name)
|
104
|
+
params_method = "#{association_name}_build_parameters"
|
105
|
+
build_params = resource_class.send(params_method) if resource_class.respond_to?(params_method)
|
106
|
+
resource.send(association_name).build(build_params || {})
|
107
|
+
end
|
108
|
+
end
|
data/app/models/poll.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
class Poll < ::Renderable
|
2
|
+
include E9Polls::Model
|
3
|
+
|
4
|
+
has_many :poll_answers
|
5
|
+
|
6
|
+
accepts_nested_attributes_for :poll_answers, :allow_destroy => true, :reject_if => :reject_answer?
|
7
|
+
|
8
|
+
validates :template, :presence => true
|
9
|
+
|
10
|
+
def vote
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_json(options={})
|
15
|
+
{}.tap do |hash|
|
16
|
+
hash[:id] = self.id,
|
17
|
+
hash[:name] = self.name,
|
18
|
+
hash[:question] = self.question,
|
19
|
+
hash[:answers] = self.answers,
|
20
|
+
hash[:errors] = self.errors
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def question
|
25
|
+
template
|
26
|
+
end
|
27
|
+
|
28
|
+
def answers
|
29
|
+
poll_answers
|
30
|
+
end
|
31
|
+
|
32
|
+
def vote=(id)
|
33
|
+
poll_answers.find_by_id(id).try(:vote!)
|
34
|
+
end
|
35
|
+
|
36
|
+
def votes
|
37
|
+
poll_answers.sum(:votes)
|
38
|
+
end
|
39
|
+
|
40
|
+
def percentage_for(poll_answer)
|
41
|
+
votes.zero? ? 0 : (poll_answer.votes.to_f / votes * 100).round(2)
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def reject_answer?(attributes)
|
47
|
+
attributes.keys.member?(:value) && attributes[:value].blank?
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class PollAnswer < ActiveRecord::Base
|
2
|
+
include E9Polls::Model
|
3
|
+
belongs_to :poll
|
4
|
+
|
5
|
+
def as_json(options={})
|
6
|
+
{}.tap do |hash|
|
7
|
+
hash[:value] = self.value
|
8
|
+
hash[:votes] = self.votes
|
9
|
+
hash[:percentage] = self.percentage
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def vote!
|
14
|
+
increment!(:votes)
|
15
|
+
end
|
16
|
+
|
17
|
+
def percentage
|
18
|
+
poll.percentage_for(self)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
.poll-answer{:id => poll_answer.to_anchor}
|
2
|
+
- if f = local_assigns[:f]
|
3
|
+
- unless poll_answered?(poll_answer.poll)
|
4
|
+
= f.radio_button :vote, poll_answer.id, :id => "#{poll_answer.to_anchor}_input"
|
5
|
+
%label{:for => "#{poll_answer.to_anchor}_input"}
|
6
|
+
= poll_answer.value
|
7
|
+
- if poll_answered?(poll_answer.poll, poll_answer)
|
8
|
+
%span.poll-answer-voted
|
9
|
+
= t(:your_vote, :scope => :e9_polls)
|
10
|
+
- else
|
11
|
+
%span.poll-answer-value
|
12
|
+
= poll_answer.value
|
13
|
+
%span.poll-answer-bar-outer
|
14
|
+
%span.poll-answer-bar{:style => "width:#{poll_answer.percentage}%", :class => "pab-#{cycle(*(1..(E9Polls.bar_css_class_count)).to_a)}"}
|
15
|
+
%span.poll-answer-votes
|
16
|
+
= poll_answer.votes
|
17
|
+
%span.poll-answer-percentage
|
18
|
+
#{poll_answer.percentage}%
|
@@ -0,0 +1,26 @@
|
|
1
|
+
= form_for resource, :url => polymorphic_path([:admin, parent, resource]), :remote => request.xhr? do |f|
|
2
|
+
.errors= resource_error_messages!
|
3
|
+
|
4
|
+
.field
|
5
|
+
= f.label :name, nil, :class => :req
|
6
|
+
= f.text_field :name
|
7
|
+
|
8
|
+
.field
|
9
|
+
= f.label :template, nil, :class => :req
|
10
|
+
= f.text_field :template
|
11
|
+
|
12
|
+
%fieldset.poll-answers.nested-associations
|
13
|
+
%legend
|
14
|
+
%span= f.label(:poll_answers)
|
15
|
+
= link_to_add_nested_attribute(:poll_answers)
|
16
|
+
= render_nested_attribute_association(:poll_answers, f)
|
17
|
+
|
18
|
+
- unless request.xhr?
|
19
|
+
= render 'shared/admin/region_type_select', :f => f
|
20
|
+
|
21
|
+
.actions
|
22
|
+
= f.submit
|
23
|
+
|
24
|
+
-# the js templates for adding fields, it would probably be nice if this was rendered once, perhaps
|
25
|
+
:javascript
|
26
|
+
#{nested_attribute_template_js(Poll, :poll_answers)}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
.e9-poll
|
2
|
+
.poll-question
|
3
|
+
= poll.question
|
4
|
+
|
5
|
+
.poll-answers
|
6
|
+
- if poll_answered?(poll)
|
7
|
+
= render(:partial => 'e9_polls/polls/results_inner', :locals => { :poll => poll })
|
8
|
+
|
9
|
+
- else
|
10
|
+
= form_for poll, :url => answer_poll_path(poll) do |f|
|
11
|
+
= render(:partial => PollAnswer.model_name.partial_path, :collection => poll.poll_answers, :locals => { :f => f })
|
12
|
+
.actions
|
13
|
+
= f.submit t(:submit_answer, :scope => :e9_polls)
|
14
|
+
|
15
|
+
= poll_results_link(poll)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
%table.records
|
2
|
+
%thead
|
3
|
+
%tr
|
4
|
+
%th= resource_class.human_attribute_name(:name)
|
5
|
+
%th= e9_t(:actions)
|
6
|
+
%tbody
|
7
|
+
- if collection.empty?
|
8
|
+
%tr
|
9
|
+
%td{:colspan => 2}= e9_t(:no_records_text)
|
10
|
+
- else
|
11
|
+
- collection.each do |record|
|
12
|
+
%tr{:id => "ids_#{record.id}", :class => cycle('odd', 'even')}
|
13
|
+
%td.text
|
14
|
+
= record.name
|
15
|
+
%td.links
|
16
|
+
= poll_results_link(record)
|
17
|
+
= e9_polls_resource_link(:edit, record)
|
18
|
+
= e9_polls_resource_link(:destroy, record)
|
@@ -0,0 +1 @@
|
|
1
|
+
$("table.records").replaceWith("<%= escape_javascript(render('table', :collection => collection)) %>");
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<% if resource.errors.any? %>
|
2
|
+
$('form .errors').html("<%= escape_javascript(resource_error_messages!) %>");
|
3
|
+
<% else %>
|
4
|
+
$('#poll_<%= resource.id %>').html("<%= escape_javascript(render(:partial => Poll.model_name.partial_path, :object => resource)) %>");
|
5
|
+
$('#poll_<%= resource.id %>').quick_edit(true);
|
6
|
+
|
7
|
+
try { $.colorbox.close() } catch(e) {}
|
8
|
+
<% end %>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
en:
|
2
|
+
e9_polls:
|
3
|
+
destroy: Delete
|
4
|
+
edit: Edit
|
5
|
+
show: View
|
6
|
+
new: New
|
7
|
+
confirm_destroy: Are you sure? This cannot be undone.
|
8
|
+
add_nested_attribute: Add
|
9
|
+
destroy_nested_attribute: Remove
|
10
|
+
poll_results_link: View the results!
|
11
|
+
poll_form_link: Answer now!
|
12
|
+
poll_show_link_title: Submit your Answer
|
13
|
+
poll_results_link_title: Results of this Poll
|
14
|
+
already_voted: You've already voted.
|
15
|
+
submit_answer: Submit!
|
16
|
+
your_vote: (Your answer)
|
17
|
+
success_message: Thanks for participating in the poll!
|
18
|
+
polls:
|
19
|
+
new: Add a Poll
|
20
|
+
show: View Results
|
21
|
+
|
22
|
+
# compat with e9_t translation
|
23
|
+
e9:
|
24
|
+
e9_polls:
|
25
|
+
polls:
|
26
|
+
results_title: Poll Results
|
27
|
+
show_title: Viewing Poll
|
28
|
+
|
29
|
+
|
30
|
+
# AR record translations
|
31
|
+
activerecord:
|
32
|
+
errors:
|
33
|
+
models:
|
34
|
+
poll:
|
35
|
+
attributes:
|
36
|
+
vote:
|
37
|
+
already_voted: "Sorry but you've already voted on this poll!"
|
38
|
+
no_argument: "Oops, sorry but your vote was not able to be processed!"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
scope :module => :e9_polls do
|
3
|
+
scope :path => :admin, :as => :admin do
|
4
|
+
resources :polls, :except => :show, :controller => 'polls'
|
5
|
+
end
|
6
|
+
|
7
|
+
resources :polls, :only => :show, :controller => 'polls' do
|
8
|
+
member do
|
9
|
+
get :results
|
10
|
+
put :answer
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# redirect admin show url to edit
|
15
|
+
get "/admin/polls/:id", :to => redirect("/admin/polls/%{id}/edit"), :constraints => { :id => /\d+/ }
|
16
|
+
end
|
17
|
+
end
|
data/e9_polls.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "e9_polls/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "e9_polls"
|
7
|
+
s.version = E9Polls::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Travis Cox"]
|
10
|
+
s.email = ["travis@e9digital.com"]
|
11
|
+
s.homepage = "http://github.com/e9digital/e9_polls"
|
12
|
+
s.summary = %q{Polls module for the e9 Rails 3 cms}
|
13
|
+
s.description = File.open('README.rdoc').read rescue nil
|
14
|
+
|
15
|
+
s.rubyforge_project = "e9_polls"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# NOTE This gem depends on e9_base ~> 1.3, but cannot reference it
|
23
|
+
# because it is a private repository.
|
24
|
+
#
|
25
|
+
# s.add_dependency("e9_base", "~> 1.3.0")
|
26
|
+
end
|
data/e9_polls.js
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
;jQuery(function($) {
|
2
|
+
var selector_prefix = 'body.controller-e9-polls-polls',
|
3
|
+
$selector = $(selector_prefix);
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Adds a new nested assocation. Depends on the nested association
|
7
|
+
* js templates being loaded.
|
8
|
+
*/
|
9
|
+
$('a.add-nested-association').click(function(e) {
|
10
|
+
e.preventDefault();
|
11
|
+
|
12
|
+
var $this = $(this),
|
13
|
+
$parent = $this.closest('.nested-associations'),
|
14
|
+
obj,
|
15
|
+
template,
|
16
|
+
index;
|
17
|
+
|
18
|
+
try {
|
19
|
+
obj = TEMPLATES[this.getAttribute('data-association')];
|
20
|
+
} catch(e) { return }
|
21
|
+
|
22
|
+
template = obj.template.replace(new RegExp(obj.index++, 'g'), obj.index);
|
23
|
+
|
24
|
+
$(template).appendTo($parent);
|
25
|
+
});
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Effectively destroys an added nested association, removing the container
|
29
|
+
* the association is not persisted, or hiding it and setting the _destroy
|
30
|
+
* parameter for the association if it is.
|
31
|
+
*/
|
32
|
+
$('a.destroy-nested-association').live('click', function(e) {
|
33
|
+
e.preventDefault();
|
34
|
+
|
35
|
+
var $parent = $(this).closest('.nested-association').hide(),
|
36
|
+
$destro = $parent.find('input[id$=__destroy]');
|
37
|
+
|
38
|
+
if ($destro.length) {
|
39
|
+
$destro.val('1');
|
40
|
+
} else {
|
41
|
+
$parent.remove();
|
42
|
+
}
|
43
|
+
});
|
44
|
+
});
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module E9Polls
|
2
|
+
module GlobalHelper
|
3
|
+
def poll_answered?(poll, answer = nil)
|
4
|
+
if cookie = e9_polls_cookie
|
5
|
+
cookie[poll.id] && !answer || cookie[poll.id].to_s == answer.id.to_s
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def poll_results_link(poll, options = {})
|
10
|
+
text = I18n.t(:poll_results_link, :scope => :e9_polls)
|
11
|
+
options.reverse_merge! :title => t(:poll_results_link_title, :scope => :e9_polls)
|
12
|
+
link_to text, options.delete(:url) || results_poll_path(poll), options.merge(:class => 'view-poll-results')
|
13
|
+
end
|
14
|
+
|
15
|
+
def poll_form_link(poll, options = {})
|
16
|
+
text = I18n.t(:poll_form_link, :scope => :e9_polls)
|
17
|
+
options.reverse_merge! :title => t(:poll_show_link_title, :scope => :e9_polls)
|
18
|
+
link_to text, options.delete(:url) || poll_path(poll), options.merge(:class => 'view-poll-form')
|
19
|
+
end
|
20
|
+
|
21
|
+
def e9_polls_cookie
|
22
|
+
return if @_e9_polls_cookie == false
|
23
|
+
@_e9_polls_cookie ||= Marshal.load(cookies[E9Polls.cookie_name]) rescue false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module E9Polls
|
2
|
+
#
|
3
|
+
# Very simple module to alter the model_name.partial_path of E9Polls models
|
4
|
+
#
|
5
|
+
module Model
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
unless self.model_name.partial_path =~ /^e9_polls/
|
10
|
+
self.model_name.instance_variable_set('@partial_path', File.join('e9_polls', self.model_name.partial_path).freeze)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/e9_polls.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'e9_base'
|
2
|
+
|
3
|
+
module E9Polls
|
4
|
+
autoload :VERSION, 'e9_polls/version'
|
5
|
+
autoload :Model, 'e9_polls/model'
|
6
|
+
|
7
|
+
mattr_accessor :fallback_html_layout
|
8
|
+
@@fallback_html_layout = 'application'
|
9
|
+
|
10
|
+
mattr_accessor :cookie_name
|
11
|
+
@@cookie_name = 'e9_polls'
|
12
|
+
|
13
|
+
mattr_accessor :bar_css_class_count
|
14
|
+
@@bar_css_class_count = 5
|
15
|
+
|
16
|
+
class Engine < ::Rails::Engine
|
17
|
+
config.e9_crm = E9Polls
|
18
|
+
|
19
|
+
initializer 'e9_polls.include_base_helper' do
|
20
|
+
ActiveSupport.on_load(:action_view) do
|
21
|
+
require 'e9_polls/global_helper'
|
22
|
+
include E9Polls::GlobalHelper
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module E9Polls
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
File.join(File.dirname(__FILE__), 'templates')
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.next_migration_number(dirname) #:nodoc:
|
14
|
+
if ActiveRecord::Base.timestamped_migrations
|
15
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
16
|
+
else
|
17
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_migration
|
22
|
+
migration_template 'migration.rb', 'db/migrate/create_e9_polls.rb'
|
23
|
+
end
|
24
|
+
|
25
|
+
def copy_over_files
|
26
|
+
copy_file 'initializer.rb', 'config/initializers/e9_polls.rb'
|
27
|
+
copy_file 'javascript.js', 'public/javascripts/e9_polls.js'
|
28
|
+
copy_file 'stylesheet.css', 'public/stylesheets/e9_polls.css'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'e9_polls'
|
2
|
+
|
3
|
+
# the fallback HTML layout for polls views (typically JS renders)
|
4
|
+
E9Polls.fallback_html_layout = 'application'
|
5
|
+
|
6
|
+
# the name of the cookie which tracks client votes
|
7
|
+
E9Polls.cookie_name = 'e9_polls'
|
8
|
+
|
9
|
+
# the number of css classes for the "bars" in the results view that
|
10
|
+
# represent the answer count, defaults to 5, cycling (.pab-1, .pab-2, etc.)
|
11
|
+
E9Polls.bar_css_class_count = 5
|
@@ -0,0 +1,115 @@
|
|
1
|
+
;jQuery(function($) {
|
2
|
+
$('body.admin table.records a.view-poll-results').colorbox({
|
3
|
+
transition: 'none',
|
4
|
+
width: '35%',
|
5
|
+
height: '50%',
|
6
|
+
onComplete: function() {
|
7
|
+
$('#cboxLoadedContent div.poll-question')
|
8
|
+
.replaceWith(function(i, content) {
|
9
|
+
return "<h1>" + content + "</h1>";
|
10
|
+
});
|
11
|
+
}
|
12
|
+
});
|
13
|
+
|
14
|
+
$('.renderable.poll form').live('submit', function(e) {
|
15
|
+
e.preventDefault();
|
16
|
+
var el = $(this);
|
17
|
+
|
18
|
+
// return if no selection
|
19
|
+
if (!$("input[@name='poll[vote]']:checked").val()) return;
|
20
|
+
|
21
|
+
$.ajax({
|
22
|
+
url: el.attr('action'),
|
23
|
+
data: el.serializeArray(),
|
24
|
+
dataType: 'json',
|
25
|
+
type: 'POST',
|
26
|
+
success: function(data, status, xhr) {
|
27
|
+
el.closest('.renderable.poll').html(data.html);
|
28
|
+
}
|
29
|
+
});
|
30
|
+
});
|
31
|
+
|
32
|
+
$('.poll .view-poll-results, .poll .view-poll-form').live('click', function(e) {
|
33
|
+
e.preventDefault();
|
34
|
+
var el = $(this);
|
35
|
+
|
36
|
+
$.ajax({
|
37
|
+
url: el.attr('href'),
|
38
|
+
dataType: 'json',
|
39
|
+
type: 'GET',
|
40
|
+
success: function(data, status, xhr) {
|
41
|
+
el.closest('.renderable.poll').html(data.html);
|
42
|
+
}
|
43
|
+
});
|
44
|
+
});
|
45
|
+
|
46
|
+
/*
|
47
|
+
* add poll class handler to quick_edit.
|
48
|
+
*
|
49
|
+
* TODO Come up with a nicer method of setting defaults
|
50
|
+
*
|
51
|
+
* NOTE It's probably overkill and unnecessary to worry about load order,
|
52
|
+
* which is the complicating factor here.
|
53
|
+
*/
|
54
|
+
$.quick_edit = $.quick_edit || {};
|
55
|
+
$.quick_edit.class_handlers = $.extend({
|
56
|
+
'poll' : function(el) {
|
57
|
+
var path = el.attr('data-renderable-path'),
|
58
|
+
npath = el.attr('data-update-node-path'),
|
59
|
+
//rpath = path + '/replace?node_id=' + el.attr('data-node'),
|
60
|
+
epath = path + '/edit';
|
61
|
+
|
62
|
+
return '<a class="qe-qelink" href="'+ epath +'">Edit</a>' +
|
63
|
+
'<a class="qe-ulink" href="'+ npath +'">Switch</a>' +
|
64
|
+
'<a class="qe-elink" href="'+ epath +'">Admin</a>';
|
65
|
+
}
|
66
|
+
}, $.quick_edit.class_handlers);
|
67
|
+
|
68
|
+
|
69
|
+
var selector_prefix = 'body.controller-e9-polls-polls',
|
70
|
+
$selector = $(selector_prefix);
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Adds a new nested assocation. Depends on the nested association
|
74
|
+
* js templates being loaded.
|
75
|
+
*/
|
76
|
+
$('a.add-nested-association').live('click', function(e) {
|
77
|
+
e.preventDefault();
|
78
|
+
|
79
|
+
var $this = $(this),
|
80
|
+
$parent = $this.closest('.nested-associations'),
|
81
|
+
obj,
|
82
|
+
template,
|
83
|
+
index;
|
84
|
+
|
85
|
+
try {
|
86
|
+
obj = TEMPLATES[this.getAttribute('data-association')];
|
87
|
+
} catch(e) { return }
|
88
|
+
|
89
|
+
template = obj.template.replace(new RegExp(obj.index++, 'g'), obj.index);
|
90
|
+
|
91
|
+
$(template).appendTo($parent);
|
92
|
+
|
93
|
+
try { $.colorbox.resize() } catch(e) {}
|
94
|
+
});
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Effectively destroys an added nested association, removing the container
|
98
|
+
* the association is not persisted, or hiding it and setting the _destroy
|
99
|
+
* parameter for the association if it is.
|
100
|
+
*/
|
101
|
+
$('a.destroy-nested-association').live('click', function(e) {
|
102
|
+
e.preventDefault();
|
103
|
+
|
104
|
+
var $parent = $(this).closest('.nested-association').hide(),
|
105
|
+
$destro = $parent.find('input[id$=__destroy]');
|
106
|
+
|
107
|
+
if ($destro.length) {
|
108
|
+
$destro.val('1');
|
109
|
+
} else {
|
110
|
+
$parent.remove();
|
111
|
+
}
|
112
|
+
|
113
|
+
try { $.colorbox.resize() } catch(e) {}
|
114
|
+
});
|
115
|
+
});
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateE9Polls < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :poll_answers, :force => true do |t|
|
4
|
+
t.references :poll
|
5
|
+
t.integer :votes, :default => 0
|
6
|
+
t.text :value
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down
|
11
|
+
drop_table :poll_answers
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
.e9-poll { }
|
2
|
+
.poll-question { font-size: large; font-weight: bold; padding: 0 1.2em; }
|
3
|
+
.poll-answers { padding: 0 1.2em; margin-bottom: 1em; }
|
4
|
+
.poll-answer-bar { height: 15px; display: block; }
|
5
|
+
.poll-answer-bar-outer { display: block; background-color: #eee; }
|
6
|
+
|
7
|
+
.poll-answer { margin: 1em 0; clear: both; }
|
8
|
+
.poll-answer textarea { height: 3em; margin-bottom: 0; }
|
9
|
+
.poll-answer .field { margin-bottom: 0; }
|
10
|
+
form.edit-poll .actions { margin-top: 1em; }
|
11
|
+
|
12
|
+
/* Poll answers or poll percentage. */
|
13
|
+
.poll-answer-votes { display: none; }
|
14
|
+
.poll-answer-percentage {}
|
15
|
+
|
16
|
+
/*
|
17
|
+
* Individual bar styles
|
18
|
+
* (the default is 5 rotating classes, but this can be changed in config).
|
19
|
+
*/
|
20
|
+
.pab-1 { background-color: Green; }
|
21
|
+
.pab-2 { background-color: Maroon; }
|
22
|
+
.pab-3 { background-color: LightSalmon; }
|
23
|
+
.pab-4 { background-color: LightSeaGreen; }
|
24
|
+
.pab-5 { background-color: HotPink; }
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: e9_polls
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Travis Cox
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-29 00:00:00 -04:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: |
|
18
|
+
** NOTE - This gem depends on e9_base, but does not reference it. It WILL NOT FUNCTION for apps which aren't built on the e9 Rails 3 CMS **
|
19
|
+
|
20
|
+
== E9Polls
|
21
|
+
|
22
|
+
Provites a Poll renderable for the e9 Rails 3 CMS.
|
23
|
+
|
24
|
+
== Installation
|
25
|
+
|
26
|
+
1. Include the gem and run the install generator to copy over the necessary files,
|
27
|
+
then migrate.
|
28
|
+
|
29
|
+
rails g e9_polls:install
|
30
|
+
|
31
|
+
This will install the db migration, the JS and CSS required for the plugin to
|
32
|
+
function properly, and an initializer.
|
33
|
+
|
34
|
+
Modify the CSS as you see fit and the JS as required (carefully).
|
35
|
+
|
36
|
+
Check out the initializer and modify if necessary. For non-Ajax fallbacks it uses
|
37
|
+
the 'application' layout. This should be changed if the app doesn't use application
|
38
|
+
layout as a sensible default.
|
39
|
+
|
40
|
+
2. Migrate the database.
|
41
|
+
|
42
|
+
rake db:migrate
|
43
|
+
|
44
|
+
3. Finally, include the generated javascript and css (e9_polls.js and e9_polls.css)
|
45
|
+
in the fashion suited to the app.
|
46
|
+
|
47
|
+
4. There is no #4.
|
48
|
+
|
49
|
+
email:
|
50
|
+
- travis@e9digital.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- README.rdoc
|
61
|
+
- Rakefile
|
62
|
+
- app/controllers/e9_polls/polls_controller.rb
|
63
|
+
- app/helpers/e9_polls/polls_helper.rb
|
64
|
+
- app/models/poll.rb
|
65
|
+
- app/models/poll_answer.rb
|
66
|
+
- app/views/e9_polls/poll_answers/_nested_attribute_template.html.haml
|
67
|
+
- app/views/e9_polls/poll_answers/_poll_answer.html.haml
|
68
|
+
- app/views/e9_polls/polls/_form.html.haml
|
69
|
+
- app/views/e9_polls/polls/_poll.html.haml
|
70
|
+
- app/views/e9_polls/polls/_results.html.haml
|
71
|
+
- app/views/e9_polls/polls/_results_inner.html.haml
|
72
|
+
- app/views/e9_polls/polls/_table.html.haml
|
73
|
+
- app/views/e9_polls/polls/create.js.erb
|
74
|
+
- app/views/e9_polls/polls/destroy.js.erb
|
75
|
+
- app/views/e9_polls/polls/edit.html.haml
|
76
|
+
- app/views/e9_polls/polls/index.html.haml
|
77
|
+
- app/views/e9_polls/polls/index.js.erb
|
78
|
+
- app/views/e9_polls/polls/new.html.haml
|
79
|
+
- app/views/e9_polls/polls/results.html.haml
|
80
|
+
- app/views/e9_polls/polls/show.html.haml
|
81
|
+
- app/views/e9_polls/polls/update.js.erb
|
82
|
+
- config/locales/en.yml
|
83
|
+
- config/routes.rb
|
84
|
+
- e9_polls.gemspec
|
85
|
+
- e9_polls.js
|
86
|
+
- lib/e9_polls.rb
|
87
|
+
- lib/e9_polls/global_helper.rb
|
88
|
+
- lib/e9_polls/model.rb
|
89
|
+
- lib/e9_polls/version.rb
|
90
|
+
- lib/generators/e9_polls/install_generator.rb
|
91
|
+
- lib/generators/e9_polls/templates/initializer.rb
|
92
|
+
- lib/generators/e9_polls/templates/javascript.js
|
93
|
+
- lib/generators/e9_polls/templates/migration.rb
|
94
|
+
- lib/generators/e9_polls/templates/stylesheet.css
|
95
|
+
has_rdoc: true
|
96
|
+
homepage: http://github.com/e9digital/e9_polls
|
97
|
+
licenses: []
|
98
|
+
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: "0"
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: "0"
|
116
|
+
requirements: []
|
117
|
+
|
118
|
+
rubyforge_project: e9_polls
|
119
|
+
rubygems_version: 1.6.2
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: Polls module for the e9 Rails 3 cms
|
123
|
+
test_files: []
|
124
|
+
|