Add polls plugin

This commit is contained in:
Patrick Gansterer 2017-11-05 22:16:32 +01:00
parent 42e0ce86a8
commit d476993321
29 changed files with 787 additions and 0 deletions

View file

@ -0,0 +1,108 @@
class PollsController < ApplicationController
before_filter -> { require_plugin_enabled FoodsoftPolls }
def index
@polls = Poll.page(params[:page]).per(@per_page).order(created_at: :desc)
end
def show
@poll = Poll.find(params[:id])
end
def new
@poll = Poll.new
end
def create
@poll = Poll.new(poll_params)
@poll.created_by = current_user
if @poll.save
redirect_to @poll, notice: t('.notice')
else
render action: 'edit'
end
end
def edit
@poll = Poll.find(params[:id])
if user_has_no_right
redirect_to polls_path, alert: t('.no_right')
end
end
def update
@poll = Poll.find(params[:id])
if user_has_no_right
redirect_to polls_path, alert: t('.no_right')
elsif @poll.update_attributes(poll_params)
redirect_to @poll, notice: t('.notice')
else
render action: 'edit'
end
end
def destroy
@poll = Poll.find(params[:id])
if user_has_no_right
redirect_to polls_path, alert: t('.no_right')
else
@poll.destroy
redirect_to polls_path, notice: t('.notice')
end
rescue => error
redirect_to polls_path, alert: t('.error', error: error.message)
end
def vote
@poll = Poll.find(params[:id])
if @poll.one_vote_per_ordergroup
ordergroup = current_user.ordergroup
return redirect_to polls_path, alert: t('.no_ordergroup') unless ordergroup
attributes = { ordergroup: ordergroup }
else
attributes = { user: current_user }
end
redirect_to @poll, alert: t('.no_right') unless @poll.user_can_vote?(current_user)
@poll_vote = @poll.poll_votes.where(attributes).first_or_initialize
if request.post?
@poll_vote.update!(note: params[:note], user: current_user)
if @poll.single_select?
choices = { params[:choice] => '1' }
else
choices = params[:choices] || {}
end
@poll_vote.poll_choices = choices.map do |choice, value|
poll_choice = @poll_vote.poll_choices.where(choice: choice).first_or_initialize
poll_choice.update!(value: value)
poll_choice
end
redirect_to @poll
end
end
private
def user_has_no_right
@poll.created_by != current_user && !current_user.role_admin?
end
def poll_params
params
.require(:poll)
.permit(:name, :starts_date_value, :starts_time_value, :ends_date_value,
:ends_time_value, :description, :one_vote_per_ordergroup, :voting_method,
:multi_select_count, :min_points, :max_points, choices: [],
required_ordergroup_custom_fields: [], required_user_custom_fields: [])
end
end

View file

@ -0,0 +1,50 @@
class Poll < ActiveRecord::Base
# @!attribute required_ordergroup_custom_fields
# A list of custom_fileds, which are required to poll.
# If the required field on the ordergroup of the user
# is empty the user will not be able to place a vote.
# @return [Array<String>] Required field names.
# @!attribute required_user_custom_fields
# A list of custom_fileds, which are required to poll.
# If the required field on the user is empty the user
# will not be able to place a vote.
# @return [Array<String>] Required field names.
belongs_to :created_by, class_name: 'User', foreign_key: 'created_by_user_id'
has_many :poll_votes, dependent: :destroy
validates_presence_of :name, :choices
serialize :choices, Array
serialize :required_ordergroup_custom_fields, Array
serialize :required_user_custom_fields, Array
enum voting_method: { event: 0, single_select: 1, multi_select: 2, points: 3, resistance_points: 4 }
include DateTimeAttributeValidate
date_time_attribute :starts, :ends
def available_points
return 0...0 if min_points.nil? || max_points.nil?
min_points..max_points
end
def user_can_edit?(user)
created_by == user || user.role_admin?
end
def user_can_vote?(user)
Ordergroup.custom_fields.each do |field|
name = field[:name]
next unless required_ordergroup_custom_fields.include? name
return false if user.ordergroup.nil? || user.ordergroup.settings.custom_fields[name].blank?
end
User.custom_fields.each do |field|
name = field[:name]
next unless required_user_custom_fields.include? name
return false if user.settings.custom_fields[name].blank?
end
true
end
end

View file

@ -0,0 +1,3 @@
class PollChoice < ActiveRecord::Base
belongs_to :poll_vote, touch: true
end

View file

@ -0,0 +1,6 @@
class PollVote < ActiveRecord::Base
belongs_to :poll
belongs_to :ordergroup
belongs_to :user
has_many :poll_choices, dependent: :destroy
end

View file

@ -0,0 +1,2 @@
/ insert_before ':root:first-child'
= config_input form, :use_polls, as: :boolean

View file

@ -0,0 +1,4 @@
%div
= text_field_tag 'poll[choices][]', value
= link_to t('.remove'), "#", title: t('.remove_choice'), 'data-remove-choice' => true,
class: 'btn btn-small'

View file

@ -0,0 +1,76 @@
- content_for :javascript do
:javascript
var choice = $($.parseHTML("#{escape_javascript(render(partial: 'choice', locals: {value: ''}))}"));
$(function() {
function updateSlideState() {
switch($('select[name="poll[voting_method]"]').val()) {
case 'event':
case 'single_select':
$('#multi_select_count').slideUp();
$('#min_max_points').slideUp();
break;
case 'multi_select':
$('#multi_select_count').slideDown();
$('#min_max_points').slideUp();
break;
case 'points':
case 'resistance_points':
$('#multi_select_count').slideUp();
$('#min_max_points').slideDown();
break;
}
}
$('select[name="poll[voting_method]"]').on('change', updateSlideState);
$('a[data-remove-choice]').on('click', function() {
$(this).parent().remove();
return false;
});
$('a[data-add-choice]').on('touchclick', function() {
choice.clone().appendTo('#choices');
return false;
});
updateSlideState();
});
= simple_form_for @poll do |f|
= f.input :name
.fold-line
= f.input :starts, as: :date_picker_time
= f.input :ends, as: :date_picker_time
= f.input :description, input_html: {rows: 2, class: 'input-xxlarge'}
- if @poll.poll_votes.any?
= f.input :voting_method, label: false do
= f.hint t('.already_voted')
- else
= f.input :one_vote_per_ordergroup, inline_label: true, label: false
- if Ordergroup.custom_fields.any?
= f.input :required_ordergroup_custom_fields, as: :check_boxes, label: false,
collection: Ordergroup.custom_fields.map{|f| [t('.required_ordergroup_custom_field', label: f[:label]), f[:name]]}.to_h
- if User.custom_fields.any?
= f.input :required_user_custom_fields, as: :check_boxes, label: false,
collection: User.custom_fields.map{|f| [t('.required_user_custom_field', label: f[:label]), f[:name]]}.to_h
= f.input :voting_method, collection: Poll.voting_methods,include_blank: false,
input_html: {class: 'input-xxlarge'}, value_method: ->(k){ k.first },
label_method: ->(k){ t("activerecord.attributes.poll.voting_methods.#{k.first}") }
#multi_select_count
= f.input :multi_select_count, input_html: { min: 0 }
#min_max_points
.fold-line
= f.input :min_points
= f.input :max_points
= f.input :choices do
#choices
= render partial: 'choice', collection: @poll.choices, as: :value
- if @poll.choices.empty?
= render partial: 'choice', locals: { value: '' }
= link_to t('.new_choice'), '#', 'data-add-choice' => true, class: 'btn'
.form-actions
= f.button :submit
= link_to t('ui.or_cancel'), :back

View file

@ -0,0 +1,23 @@
- if Poll.count > 20
= items_per_page
= pagination_links_remote @polls
%table.table.table-striped
%thead
%tr
%th= heading_helper Poll, :name
%th= heading_helper Poll, :starts
%th= heading_helper Poll, :ends
%th= t 'ui.actions'
%tbody
- for poll in @polls
%tr{:class => cycle('even','odd', :name => 'polls')}
%td= link_to poll.name, poll
%td= format_date poll.starts
%td= format_date poll.ends
%td
- if poll.user_can_vote?(current_user)
= link_to t('.vote'), vote_poll_path(poll), class: 'btn btn-mini btn-success'
- if poll.user_can_edit?(current_user)
= link_to t('ui.edit'), edit_poll_path(poll), class: 'btn btn-mini'
= link_to t('ui.delete'), poll, :data => {:confirm => t('ui.confirm_delete', name: poll.name)},
:method => :delete, class: 'btn btn-mini btn-danger'

View file

@ -0,0 +1,3 @@
- title t '.title'
= render 'form'

View file

@ -0,0 +1,7 @@
- title t('.title')
- content_for :actionbar do
= link_to t('.new_poll'), new_poll_path, class: 'btn btn-primary'
#polls
= render 'polls'

View file

@ -0,0 +1 @@
$('#polls').html('#{escape_javascript(render("polls"))}');

View file

@ -0,0 +1,3 @@
- title t '.title'
= render 'form'

View file

@ -0,0 +1,69 @@
- title @poll.name
- content_for :actionbar do
- if @poll.user_can_vote?(current_user)
= link_to t('.vote'), vote_poll_path(@poll), class: 'btn btn-success'
- if @poll.user_can_edit?(current_user)
= link_to t('ui.edit'), edit_poll_path(@poll), class: 'btn'
= link_to t('ui.delete'), @poll, :data => {:confirm => t('.confirm')}, :method => :delete, class: 'btn btn-danger'
%p= simple_format @poll.description
- sums = []
%table.table.table-striped
%thead
%tr
%th= heading_helper PollVote, :name
- for choice in @poll.choices
- sums << 0
%th= choice
%th= heading_helper PollVote, :updated_at
%tbody
- for vote in @poll.poll_votes.includes(:poll_choices)
%tr
%td
- if vote.ordergroup.nil?
= show_user vote.user
- else
= "#{vote.ordergroup.name} (#{show_user vote.user})"
- @poll.choices.size.times do |idx|
- if choice = vote.poll_choices.find { |choice| choice.choice == idx }
- if @poll.event?
- if choice.value == 0
%td{style:'background-color:#eed3d7'}= "\u2715"
- elsif choice.value == 1
- sums[idx] += 1
%td{style:'background-color:#d6e9c6'}= "\u2714"
- else
- sums[idx] += 0.5
%td{style:'background-color:#fcf8e3'}= "?"
- elsif @poll.single_select? || @poll.multi_select?
- sums[idx] += 1
%td= "\u2717"
- else
- sums[idx] += choice.value
%td= choice.value
- else
%td
%td= format_time vote.updated_at
%tfoot
%tr
%td
- for sum in sums
%td
- best_sum = @poll.resistance_points? ? sums.min : sums.max
- if sum == best_sum
%strong= sum
- else
= sum
%td
- for vote in @poll.poll_votes
- unless vote.note.empty?
.comment
%strong
- if vote.ordergroup.nil?
= show_user vote.user
- else
= "#{vote.ordergroup.name} (#{show_user vote.user})"
= simple_format(vote.note)

View file

@ -0,0 +1,49 @@
- title @poll.name
%p= simple_format @poll.description
= form_tag(vote_poll_path(@poll)) do
- if @poll.event?
%table.table.table-striped
%thead
%tr
%th
%th= "\u2714"
%th= "?"
%th= "\u2715"
%tbody
- @poll.choices.each_with_index do |choice, idx|
%tr
%td= @poll.choices[idx]
%td= radio_button_tag "choices[#{idx}]", '1', @poll_vote.poll_choices.where(choice: idx, value: '1').any?
%td= radio_button_tag "choices[#{idx}]", '2', @poll_vote.poll_choices.where(choice: idx, value: '2').any?
%td= radio_button_tag "choices[#{idx}]", '0', @poll_vote.poll_choices.where(choice: idx, value: '0').any?
- elsif @poll.single_select?
- @poll.choices.each_with_index do |choice, idx|
= radio_button_tag 'choice', idx, @poll_vote.poll_choices.where(choice: idx, value: 1).any?, style: 'float:left'
= label_tag "choices[#{idx}]", choice
- elsif @poll.multi_select?
- @poll.choices.each_with_index do |choice, idx|
= check_box_tag "choices[#{idx}]", '1', @poll_vote.poll_choices.where(choice: idx, value: 1).any?, style: 'float:left'
= label_tag "choices[#{idx}]", choice
- elsif @poll.points? || @poll.resistance_points?
%table.table.table-striped
%thead
%tr
%th
- @poll.available_points.each do |point|
%th= point
%tbody
- @poll.choices.each_with_index do |choice, idx|
%tr
%td= @poll.choices[idx]
- @poll.available_points.each do |point|
%th= radio_button_tag "choices[#{idx}]", point, @poll_vote.poll_choices.where(choice: idx, value: point).any?
%p
%b= heading_helper(PollVote, :note) + ':'
%p
= text_area_tag 'note', @poll_vote.note
%p
= submit_tag t('.submit'), class: 'btn btn-success'
= link_to t('ui.back'), @poll, class: 'btn'