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

@ -61,6 +61,7 @@ gem 'foodsoft_wiki', path: 'plugins/wiki'
gem 'foodsoft_messages', path: 'plugins/messages'
gem 'foodsoft_documents', path: 'plugins/documents'
gem 'foodsoft_discourse', path: 'plugins/discourse'
gem 'foodsoft_polls', path: 'plugins/polls'
# plugins not enabled by default
#gem 'foodsoft_current_orders', path: 'plugins/current_orders'

View file

@ -36,6 +36,13 @@ PATH
mail
rails
PATH
remote: plugins/polls
specs:
foodsoft_polls (0.0.1)
deface (~> 1.0)
rails
PATH
remote: plugins/wiki
specs:
@ -528,6 +535,7 @@ DEPENDENCIES
foodsoft_discourse!
foodsoft_documents!
foodsoft_messages!
foodsoft_polls!
foodsoft_wiki!
gaffe
haml (~> 4.0)

View file

@ -210,6 +210,12 @@ class User < ApplicationRecord
end
end
def self.custom_fields
fields = FoodsoftConfig[:custom_fields] && FoodsoftConfig[:custom_fields][:user]
return [] unless fields
fields.map(&:deep_symbolize_keys)
end
# XXX this is view-related; need to move out things like token_attributes
# then this can be removed
def display

View file

@ -0,0 +1,37 @@
class CreatePolls < ActiveRecord::Migration
def change
create_table :polls do |t|
t.integer :created_by_user_id, null: false
t.string :name, null: false
t.text :description
t.datetime :starts
t.datetime :ends
t.boolean :one_vote_per_ordergroup, default: false, null: false
t.text :required_ordergroup_custom_fields
t.text :required_user_custom_fields
t.integer :voting_method, null: false
t.string :choices, array: true, null: false
t.integer :final_choice, index: true
t.integer :multi_select_count, default: 0, null: false
t.integer :min_points
t.integer :max_points
t.timestamps
end
create_table :poll_votes do |t|
t.references :poll, null: false
t.references :user, null: false
t.references :ordergroup
t.text :note
t.timestamps
t.index [:poll_id, :user_id, :ordergroup_id], unique: true
end
create_table :poll_choices do |t|
t.references :poll_vote, null: false
t.integer :choice, null: false
t.integer :value, null: false
t.index [:poll_vote_id, :choice], unique: true
end
end
end

View file

@ -389,6 +389,46 @@ ActiveRecord::Schema.define(version: 20181201000200) do
t.datetime "updated_at", null: false
end
create_table "poll_choices", force: :cascade do |t|
t.integer "poll_vote_id", limit: 4, null: false
t.integer "choice", limit: 4, null: false
t.integer "value", limit: 4, null: false
end
add_index "poll_choices", ["poll_vote_id", "choice"], name: "index_poll_choices_on_poll_vote_id_and_choice", unique: true, using: :btree
create_table "poll_votes", force: :cascade do |t|
t.integer "poll_id", limit: 4, null: false
t.integer "user_id", limit: 4, null: false
t.integer "ordergroup_id", limit: 4
t.text "note", limit: 65535
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "poll_votes", ["poll_id", "user_id", "ordergroup_id"], name: "index_poll_votes_on_poll_id_and_user_id_and_ordergroup_id", unique: true, using: :btree
create_table "polls", force: :cascade do |t|
t.integer "created_by_user_id", limit: 4, null: false
t.string "name", limit: 255, null: false
t.text "description", limit: 65535
t.datetime "starts"
t.datetime "ends"
t.boolean "one_vote_per_ordergroup", default: false, null: false
t.text "required_ordergroup_custom_fields", limit: 65535
t.text "required_user_custom_fields", limit: 65535
t.integer "voting_method", limit: 4, null: false
t.string "choices", limit: 255, null: false
t.integer "final_choice", limit: 4
t.integer "multi_select_count", limit: 4, default: 0, null: false
t.integer "min_points", limit: 4
t.integer "max_points", limit: 4
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "polls", ["final_choice"], name: "index_polls_on_final_choice", using: :btree
create_table "printer_job_updates", force: :cascade do |t|
t.integer "printer_job_id", limit: 4, null: false
t.datetime "created_at", null: false

18
plugins/polls/README.md Normal file
View file

@ -0,0 +1,18 @@
FoodsoftPolls
=============
This plugin adds polls to foodsoft. A new 'Polls' menu entry is added below the 'Foodcoops' menu in the navigation bar.
This plugin is enabled by default in foodsoft, so you don't need to do anything
to install it. If you still want to, for example when it has been disabled,
add the following to foodsoft's Gemfile:
```Gemfile
gem 'foodsoft_polls', path: 'lib/foodsoft_polls'
```
This plugin introduces the foodcoop config option `use_polls`, which can be
set to `false` to disable polls. May be useful in multicoop deployments.
This plugin is part of the foodsoft package and uses the AGPL-3 license (see
foodsoft's LICENSE for the full license text).

40
plugins/polls/Rakefile Normal file
View file

@ -0,0 +1,40 @@
#!/usr/bin/env rake
begin
require 'bundler/setup'
rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end
begin
require 'rdoc/task'
rescue LoadError
require 'rdoc/rdoc'
require 'rake/rdoctask'
RDoc::Task = Rake::RDocTask
end
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'FoodsoftDocuments'
rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
Bundler::GemHelper.install_tasks
require 'rake/testtask'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = false
end
task :default => :test

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'

View file

@ -0,0 +1,67 @@
de:
activerecord:
attributes:
poll:
choices: Wahlmöglichkeiten
created_at: Erstellt am
created_by: Erstellt von
description: Beschreibung
ends: Endet am
name: Name
max_points: Maximalpunkte
min_points: Minimalpunkte
multi_select_count: Maximale Anzahl
one_vote_per_ordergroup: Nur eine Stimme pro Bestellgruppe
starts: Läuft vom
voting_method: Umfragetyp
voting_methods:
event: Termin
single_select: Einzelauswahl
multi_select: Mehrfachauswahl
points: Punkte
resistance_points: Widerstandspunkte
poll_vote:
name: Name
note: Notiz
updated_at: Zuletzt aktualisierst
models:
poll: Umfrage
config:
hints:
use_polls: Einfache Umfragen aktivieren
keys:
use_polls: Umfragen aktivieren
navigation:
polls: Umfragen
polls:
choice:
remove: Löschen
create:
error: 'Umfrage konnte nicht erstellt werden: %{error}'
notice: Umfrage wurde erstellt
edit:
title: Umfrage bearbeiten
form:
already_voted: Die Wahlmöglichkeiten können icht mehr geändert werden, da bereits Stimmen abgegeben wurden.
new_choice: Zusätzliche Wahlmöglichkeit
required_ordergroup_custom_field: '''%{label}'' von Bestellgruppe darf nicht leer sein'
required_user_custom_field: '''%{label}'' von Benutzer_in darf nicht leer sein'
destroy:
error: 'Umfrage konnte nicht gelöscht werden: %{error}'
no_right: Du hast nicht genügend Rechte um die Umfrage zu löschen
notice: Umfrage wurde gelöscht
index:
new_poll: Neue Umfrage
title: Umfragen
new:
title: Umfrage erstellen
polls:
vote: Abstimmen
show:
vote: Abstimmen
update:
error: 'Umfrage konnte nicht aktualisiert werden: %{error}'
notice: Umfrage wurde aktualisiert
vote:
submit: Abstimmen
no_right: Du kannst bei dieser Umfrage nicht teilnehmen

View file

@ -0,0 +1,67 @@
en:
activerecord:
attributes:
poll:
choices: Choices
created_at: Created at
created_by: Created by
description: Description
ends: Ends at
name: Name
max_points: Maximal points
min_points: Minimal points
multi_select_count: Maximal number
one_vote_per_ordergroup: Only one vote for every ordergroup
starts: Starts at
voting_method: Voting method
voting_methods:
event: Event
single_select: Single select
multi_select: Muliple selections
points: Points
resistance_points: Resistance points
poll_vote:
name: Name
note: Note
updated_at: Last updated
models:
poll: Poll
config:
hints:
use_polls: Enable simple polls.
keys:
use_polls: Enable polls
navigation:
polls: Polls
polls:
choice:
remove: Delete
create:
error: 'Poll could not be created: %{error}'
notice: Poll has been created
edit:
title: Edit poll
form:
already_voted: The choices can not be changed, since there have been votes already.
new_choice: Additional choice
required_ordergroup_custom_field: '''%{label}'' of ordergroup must not be empty'
required_user_custom_field: '''%{label}'' of use must not be empty'
destroy:
error: 'Poll could not be deleted: %{error}'
no_right: You do not have enough rights to delte this poll
notice: Poll has been deleted
index:
new_poll: New poll
title: Polls
new:
title: New poll
polls:
vote: Vote
show:
vote: Vote
update:
error: 'Poll could not be updated: %{error}'
notice: Poll has been updated
vote:
submit: Vote
no_right: You can not participate in this poll

View file

@ -0,0 +1,14 @@
Rails.application.routes.draw do
scope '/:foodcoop' do
resources :polls do
member do
get :vote
post :vote
end
end
end
end

View file

@ -0,0 +1,37 @@
class CreatePolls < ActiveRecord::Migration
def change
create_table :polls do |t|
t.integer :created_by_user_id, null: false
t.string :name, null: false
t.text :description
t.datetime :starts
t.datetime :ends
t.boolean :one_vote_per_ordergroup, default: false, null: false
t.text :required_ordergroup_custom_fields
t.text :required_user_custom_fields
t.integer :voting_method, null: false
t.string :choices, array: true, null: false
t.integer :final_choice, index: true
t.integer :multi_select_count, default: 0, null: false
t.integer :min_points
t.integer :max_points
t.timestamps
end
create_table :poll_votes do |t|
t.references :poll, null: false
t.references :user, null: false
t.references :ordergroup
t.text :note
t.timestamps
t.index [:poll_id, :user_id, :ordergroup_id], unique: true
end
create_table :poll_choices do |t|
t.references :poll_vote, null: false
t.integer :choice, null: false
t.integer :value, null: false
t.index [:poll_vote_id, :choice], unique: true
end
end
end

View file

@ -0,0 +1,21 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "foodsoft_polls/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "foodsoft_polls"
s.version = FoodsoftPolls::VERSION
s.authors = ["paroga"]
s.email = ["paroga@paroga.com"]
s.homepage = "https://github.com/foodcoops/foodsoft"
s.summary = "Polls plugin for foodsoft."
s.description = "Adds possibility to do polls with foodsoft."
s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"]
s.test_files = Dir["test/**/*"]
s.add_dependency "rails"
s.add_dependency "deface", "~> 1.0"
end

View file

@ -0,0 +1,9 @@
require 'foodsoft_polls/engine'
module FoodsoftPolls
# Return whether the polls are used or not.
# Enabled by default in {FoodsoftConfig} since it used to be part of the foodsoft core.
def self.enabled?
FoodsoftConfig[:use_polls]
end
end

View file

@ -0,0 +1,15 @@
module FoodsoftPolls
class Engine < ::Rails::Engine
def navigation(primary, context)
return unless FoodsoftPolls.enabled?
return if primary[:foodcoop].nil?
sub_nav = primary[:foodcoop].sub_navigation
sub_nav.items <<
SimpleNavigation::Item.new(primary, :polls, I18n.t('navigation.polls'), context.polls_path)
# move to right before tasks item
if i = sub_nav.items.index(sub_nav[:tasks])
sub_nav.items.insert(i, sub_nav.items.delete_at(-1))
end
end
end
end

View file

@ -0,0 +1,3 @@
module FoodsoftPolls
VERSION = "0.0.1"
end