e9_polls 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|