diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 288f730d..caeb641f 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -20,4 +20,9 @@ //= require bootstrap //= require osem //= require d3.v3 -//= require d3.v3.min \ No newline at end of file +//= require d3.v3.min +$(document).ready(function() { + $('a[disabled=disabled]').click(function(event){ + return false; + }); +}); \ No newline at end of file diff --git a/app/controllers/admin/questions_controller.rb b/app/controllers/admin/questions_controller.rb new file mode 100644 index 00000000..7cac1e0e --- /dev/null +++ b/app/controllers/admin/questions_controller.rb @@ -0,0 +1,96 @@ +class Admin::QuestionsController < ApplicationController + before_filter :verify_organizer + + def index + @conference = Conference.find_all_by_short_title(params[:conference_id]).first + @questions = Question.where(:global => true).all | Question.where(:conference_id => @conference.id) + @questions_conference = @conference.questions + @new_question = @conference.questions.new + end + + def new + @conference = Conference.find_all_by_short_title(params[:conference_id]).first + @new_question = @conference.questions.new + end + + def create + @conference = Conference.find_all_by_short_title(params[:conference_id]).first + @question = @conference.questions.new(params[:question]) + @question.conference_id = @conference.id + + respond_to do |format| + if @conference.save + format.html { redirect_to admin_conference_questions_path, notice: 'Question was successfully created.' } + else + flash[:error] = "Oops, couldn't save. Question and answer(s) have titles?" + format.html { redirect_to admin_conference_questions_path } + end + end + end + + # GET questions/1/edit + def edit + @conference = Conference.find_all_by_short_title(params[:conference_id]).first + @question = Question.find(params[:id]) + + if @question.global == true && !has_role?(current_user, "Admin") + redirect_to(admin_conference_questions_path(:conference_id => @conference.short_title), :alert => "Sorry, you cannot edit global questions. Create a new one.") + end + end + + # PUT questions/1 + def update + + @conference = Conference.find_all_by_short_title(params[:conference_id]).first + @question = Question.find(params[:id]) + + if @question.update_attributes(params[:question]) + redirect_to(admin_conference_questions_path(:conference_id => @conference.short_title), :notice => "Question '#{@question.title}' for #{@conference.short_title} successfully updated.") + else + redirect_to(admin_conference_questions_path(:conference_id => @conference.short_title), :notice => "Update of questions for #{@conference.short_title} failed.") + end + end + + # Update questions used for the conference + def update_conference + @conference = Conference.find_all_by_short_title(params[:conference_id]).first + + if @conference.update_attributes(params[:conference]) + redirect_to(admin_conference_questions_path(:conference_id => @conference.short_title), :notice => "Questions for #{@conference.short_title} successfully updated.") + else + redirect_to(admin_conference_questions_path(:conference_id => @conference.short_title), :notice => "Update of questions for #{@conference.short_title} failed.") + end + end + + # DELETE questions/1 + def destroy + if has_role?(current_user, "Admin") + @question = Question.find(params[:id]) + + # Do not delete global questions + if @question.global == false + + # Delete question and its answers + begin + Question.transaction do + + @question.delete + @question.answers.each do |a| + a.delete + end + flash[:notice] = "Deleted question: #{@question.title} and its answers: #{@question.answers.map {|a| a.title}.join ','}" + end + rescue ActiveRecord::RecordInvalid + flash[:error] = "Could not delete question." + end + else + flash[:error] = "You cannot delete global questions." + end + else + flash[:error] = "You must be an admin to delete a question." + end + + @questions = Question.where(:global => true).all | Question.where(:conference_id => @conference.id) + @questions_conference = @conference.questions + end +end \ No newline at end of file diff --git a/app/controllers/admin/registrations_controller.rb b/app/controllers/admin/registrations_controller.rb index 891cd126..8eebf85f 100644 --- a/app/controllers/admin/registrations_controller.rb +++ b/app/controllers/admin/registrations_controller.rb @@ -6,7 +6,7 @@ class Admin::RegistrationsController < ApplicationController @pdf_filename = "#{@conference.title}.pdf" @registrations = @conference.registrations.includes(:person).order("registrations.created_at ASC") @attended = @conference.registrations.where("attended = ?", true).count - @headers = %w[name email attending_social_events attending_with_partner need_access other_needs arrival departure attended] + @headers = %w[first_name last_name email irc_nickname other_needs arrival departure attended] end def change_field @@ -128,4 +128,4 @@ class Admin::RegistrationsController < ApplicationController redirect_to(admin_conference_registrations_path(@conference.short_title), :alert => 'You must be an admin to delete a registration.') end end -end +end \ No newline at end of file diff --git a/app/models/answer.rb b/app/models/answer.rb new file mode 100644 index 00000000..da91c47a --- /dev/null +++ b/app/models/answer.rb @@ -0,0 +1,8 @@ +class Answer < ActiveRecord::Base + attr_accessible :title + + has_many :qanswers + has_many :questions, :through => :qanswers + + validates :title, :presence => true +end diff --git a/app/models/conference.rb b/app/models/conference.rb index 28c7a1a0..46cec030 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -2,10 +2,12 @@ class Conference < ActiveRecord::Base attr_accessible :title, :short_title, :social_tag, :contact_email, :timezone, :html_export_path, :start_date, :end_date, :rooms_attributes, :tracks_attributes, :dietary_choices_attributes, :use_dietary_choices, :use_supporter_levels, :supporter_levels_attributes, :social_events_attributes, - :event_types_attributes, :difficulty_levels_attributes, :use_difficulty_levels, :registration_start_date, :registration_end_date, :logo + :event_types_attributes, :registration_start_date, :registration_end_date, :logo, :questions_attributes, :question_ids, :answers_attributes, :answer_ids, :difficulty_levels_attributes, :use_difficulty_levels has_paper_trail + has_and_belongs_to_many :questions + has_one :email_settings, :dependent => :destroy has_one :call_for_papers, :dependent => :destroy has_many :social_events, :dependent => :destroy @@ -30,6 +32,7 @@ class Conference < ActiveRecord::Base accepts_nested_attributes_for :supporter_levels, :allow_destroy => true accepts_nested_attributes_for :event_types, :allow_destroy => true accepts_nested_attributes_for :email_settings + accepts_nested_attributes_for :questions, :allow_destroy => true has_attached_file :logo, :styles => {:thumb => "100x100>", :large => "300x300>" } @@ -119,4 +122,4 @@ class Conference < ActiveRecord::Base self.guid = guid end -end \ No newline at end of file +end diff --git a/app/models/qanswer.rb b/app/models/qanswer.rb new file mode 100644 index 00000000..f2628c8a --- /dev/null +++ b/app/models/qanswer.rb @@ -0,0 +1,8 @@ +class Qanswer < ActiveRecord::Base + attr_accessible :question_id, :answer_id + + belongs_to :question + belongs_to :answer, :dependent => :delete + + has_and_belongs_to_many :registrations +end diff --git a/app/models/question.rb b/app/models/question.rb new file mode 100644 index 00000000..69ceade7 --- /dev/null +++ b/app/models/question.rb @@ -0,0 +1,14 @@ +class Question < ActiveRecord::Base + attr_accessible :title, :global, :answers_attributes, :answer_ids, :question_type_id + + belongs_to :question_type + has_and_belongs_to_many :conferences + + has_many :qanswers, :dependent => :delete_all + has_many :answers, :through => :qanswers, :dependent => :delete_all + + validates :title, :presence => true + validates :answers, :presence => true + + accepts_nested_attributes_for :answers, :allow_destroy => true +end \ No newline at end of file diff --git a/app/models/question_type.rb b/app/models/question_type.rb new file mode 100644 index 00000000..1326c5b2 --- /dev/null +++ b/app/models/question_type.rb @@ -0,0 +1,5 @@ +class QuestionType < ActiveRecord::Base + attr_accessible :title, :description + + has_many :questions +end diff --git a/app/models/registration.rb b/app/models/registration.rb index 92778f07..fd9324a2 100644 --- a/app/models/registration.rb +++ b/app/models/registration.rb @@ -7,15 +7,16 @@ class Registration < ActiveRecord::Base has_one :supporter_level, :through => :supporter_registration has_and_belongs_to_many :social_events has_and_belongs_to_many :events + has_and_belongs_to_many :qanswers attr_accessible :person_id, :conference_id, :attending_social_events, :attending_with_partner, :using_affiliated_lodging, :arrival, :departure, :person_attributes, :other_dietary_choice, :dietary_choice_id, - :handicapped_access_required, :supporter_registration_attributes, :social_event_ids, :other_special_needs, - :event_ids, :attended + :handicapped_access_required, :supporter_registration_attributes, :social_event_ids, :other_special_needs, :event_ids, :attended, :qanswer_ids, :qanswers_attributes accepts_nested_attributes_for :person accepts_nested_attributes_for :supporter_registration accepts_nested_attributes_for :social_events + accepts_nested_attributes_for :qanswers delegate :first_name, :to => :person delegate :last_name, :to => :person @@ -24,7 +25,5 @@ class Registration < ActiveRecord::Base delegate :irc_nickname, :to => :person delegate :company, :to => :person - alias_attribute :with_partner, :attending_with_partner - alias_attribute :need_access, :handicapped_access_required alias_attribute :other_needs, :other_special_needs end diff --git a/app/views/admin/conference/_sidebar.html.haml b/app/views/admin/conference/_sidebar.html.haml index e082fb9f..9102cfb2 100644 --- a/app/views/admin/conference/_sidebar.html.haml +++ b/app/views/admin/conference/_sidebar.html.haml @@ -47,4 +47,7 @@ %li.active= link_to "Dietary Choices", "#" - else %li= link_to "Dietary Choices", admin_conference_dietary_list_path(@conference.short_title) - + - if activated == "Questions" + %li.active= link_to "Questions", "#" + - else + %li= link_to "Questions", admin_conference_questions_path(@conference.short_title) diff --git a/app/views/admin/questions/_answer_fields.html.haml b/app/views/admin/questions/_answer_fields.html.haml new file mode 100644 index 00000000..5bffc9bc --- /dev/null +++ b/app/views/admin/questions/_answer_fields.html.haml @@ -0,0 +1,4 @@ +.nested-fields + = f.inputs do + = f.input :title + = remove_association_link :answer, f diff --git a/app/views/admin/questions/_form.html.haml b/app/views/admin/questions/_form.html.haml new file mode 100644 index 00000000..96a445e2 --- /dev/null +++ b/app/views/admin/questions/_form.html.haml @@ -0,0 +1,8 @@ +.row-fluid + .span6 + %legend Question + = f.input :title, :label => "Your Question" + = f.input :question_type + = f.input :global, :label => "Make Global", :hint => "(Global questions are available for selection to all conferences)" + .span6 + = dynamic_association :answers, "Answers", f, :hint => "Insert your answers in the order you want them to appear" \ No newline at end of file diff --git a/app/views/admin/questions/_questions.html.haml b/app/views/admin/questions/_questions.html.haml new file mode 100644 index 00000000..3638d6d0 --- /dev/null +++ b/app/views/admin/questions/_questions.html.haml @@ -0,0 +1,20 @@ +%h2 + Questions for #{@conference.short_title} + - if @questions_conference.count + = "(#{@questions_conference.count})" +%i The questions will appear in the registration page of the conference + +%table.table + - @questions.each do |q| + %tr + %td + = hidden_field_tag "conference[question_ids][]", nil + = check_box_tag "conference[question_ids][]", q.id, @conference.question_ids.include?(q.id), id: dom_id(q) + %td + = label_tag dom_id(q), q.title + = label_tag dom_id(q), "Type: #{q.question_type.title}" + = label_tag dom_id(q), "Answers: #{q.answers.map {|a| a.title}.join(', ')}" + + %td= link_to "Edit", edit_admin_conference_question_path(@conference.short_title, q), :class => "btn btn-primary", :disabled => q.global == true && !has_role?(current_user, "Admin") + + %td= link_to "Delete", admin_conference_question_path(@conference.short_title, q), :method => :delete, :remote => true, :class => "btn btn-danger", :confirm => "Delete question '#{q.title}'?", :disabled => q.global == true && !has_role?(current_user, "Admin") \ No newline at end of file diff --git a/app/views/admin/questions/destroy.js.erb b/app/views/admin/questions/destroy.js.erb new file mode 100644 index 00000000..dbae0ed8 --- /dev/null +++ b/app/views/admin/questions/destroy.js.erb @@ -0,0 +1 @@ +$('#myquestions').html("<%= escape_javascript(render :partial => 'questions') %>"); diff --git a/app/views/admin/questions/edit.html.haml b/app/views/admin/questions/edit.html.haml new file mode 100644 index 00000000..826c9dfb --- /dev/null +++ b/app/views/admin/questions/edit.html.haml @@ -0,0 +1,9 @@ +%h2 + Edit Question +%h3 + "#{@question.title}" += semantic_form_for(@question, :url => admin_conference_question_path(@conference.short_title, @question.id)) do |f| + + = render :partial => "form", :locals => {:f => f} + + = f.submit "Save", :class => "btn btn-primary", :confirm => "Are you sure you want to make these changes?" \ No newline at end of file diff --git a/app/views/admin/questions/index.html.haml b/app/views/admin/questions/index.html.haml new file mode 100644 index 00000000..f0631d97 --- /dev/null +++ b/app/views/admin/questions/index.html.haml @@ -0,0 +1,31 @@ +.container-fluid + .row-fluid + .span3 + = render 'admin/conference/sidebar', :activated => "Questions" + .span9 + .pull-right + %b= link_to "Create New Question","#", "data-toggle" => "modal", "data-target" => "#new-question", :class => "btn btn-success" + %br + %br + + - if @questions.count > 0 + + = semantic_form_for(@conference, :url => admin_conference_questions_update_conference_path(@conference.short_title), :method => 'put') do |f| + + .questions{:id => "myquestions"} + = render :partial => "questions" + + = f.submit "Update Questions for #{@conference.short_title}", :class => "btn btn-primary", :confirm => "Are you sure you want to make these changes?" + + .modal.hide.fade{:id => "new-question", "role" => "dialog", "aria-hidden" => "true"} + .modal-header + %h3{:id => "new-question-header"} + Add new question + .modal-body + = semantic_form_for(@new_question, :url => admin_conference_questions_path(@conference.short_title), :method => :post) do |f| + = render :partial => "form", :locals => {:f => f} + %button{:class => "btn btn-primary"} + Save + .pull-right + %button{:class=> "btn btn-danger", "data-dismiss"=> "modal", "aria-hidden"=>"true"} + Cancel \ No newline at end of file diff --git a/app/views/admin/registrations/_questions.html.haml b/app/views/admin/registrations/_questions.html.haml new file mode 100644 index 00000000..f82a1213 --- /dev/null +++ b/app/views/admin/registrations/_questions.html.haml @@ -0,0 +1,9 @@ +- @conference.questions.each do |q| + + %b Q: + = q.title + + %b A: + - registration.qanswers.where(:question_id => q.id).each do |qa| + = qa.answer.title + %br \ No newline at end of file diff --git a/app/views/admin/registrations/show.html.haml b/app/views/admin/registrations/show.html.haml index 675b8360..119ba901 100644 --- a/app/views/admin/registrations/show.html.haml +++ b/app/views/admin/registrations/show.html.haml @@ -11,7 +11,7 @@ - if @registrations = "(#{@registrations.length})" = " - Attended (#{@attended})" - %table.table.table-striped.table-bordered.table-hover#registrations + %table.table.table-bordered.table-striped.table-hover#registrations %thead %th No @@ -21,6 +21,7 @@ = h.capitalize %th %th + %th - counter = 0 - @registrations.each do |registration| %tr @@ -45,11 +46,17 @@ - else = registration.send(field.to_sym) + %td + = link_to "Questions", "#", :class => "questions btn btn-success", :id => counter, :onclick => "toggle('row#{counter}')" + %td = link_to "Edit", admin_conference_registrations_edit_path(@conference.short_title, :id => registration.id), :method => :get, :class => "btn btn-primary" %td = link_to "Delete", admin_conference_registrations_path(@conference.short_title, :id => registration.id), :method => :delete, :class => "btn btn-danger", :confirm => "Really delete registration for #{registration.public_name} #{registration.email}?" + %tr{:id => "row#{counter}", :style=>"display:none;"} + %td{:colspan=>13} + = render :partial => "questions", :locals => {:registration => registration} :javascript $(document).ready(function() { @@ -58,3 +65,11 @@ "bLengthChange": false } ); } ); + + function toggle(rowid) { + if( document.getElementById(rowid).style.display=='none' ){ + document.getElementById(rowid).style.display = ''; + }else{ + document.getElementById(rowid).style.display = 'none'; + } + }; \ No newline at end of file diff --git a/app/views/conference_registration/_questions.html.haml b/app/views/conference_registration/_questions.html.haml new file mode 100644 index 00000000..50c0fb09 --- /dev/null +++ b/app/views/conference_registration/_questions.html.haml @@ -0,0 +1,13 @@ +- @conference.questions.each do |q| + %h5 + = "Q: #{q.title}" + - if q.question_type.id == 1 || q.question_type.id == 2 # yes/no or single choice + = f.input :qanswers, :collection => q.qanswers, :as => :select, :input_html => { :multiple => false }, :label => false, :include_blank => "Please make your choice", + :member_label => Proc.new {|a| a.answer.title} + + - if q.question_type.id == 3 # multiple choice + = f.input :qanswers, :collection => q.qanswers, :as => :check_boxes, :label => false, + :member_label => Proc.new {|a| a.answer.title} + + + diff --git a/app/views/conference_registration/register.html.haml b/app/views/conference_registration/register.html.haml index 0304c80c..0b532ae8 100644 --- a/app/views/conference_registration/register.html.haml +++ b/app/views/conference_registration/register.html.haml @@ -17,15 +17,18 @@ %br = f.fields_for :person do |p| = p.text_field :public_name, :for => :person - - if @conference.use_supporter_levels? + + - if @conference.questions + = render :partial => "questions", :locals => {:f => f} + %br + + - if @conference.use_supporter_levels? && @conference.supporter_levels.count > 0 = f.semantic_fields_for :supporter_registration do |reg| = reg.input :supporter_level, :as => :select, :collection => @conference.supporter_levels = reg.input :code, :label => "Confirmation or registration code (if applicable)" %span#supporter-link.help-block - = f.input :attending_with_partner, :label => false - = f.input :using_affiliated_lodging, :label => false - = f.input :handicapped_access_required, :label => false - = f.input :other_special_needs, :label => "Any other special needs?", :input_html => {:rows => 2} + + = f.input :other_special_needs, :label => "Any other needs or comments?", :input_html => {:rows => 2, :class => "span6"} - if @workshops.count > 0 =f.inputs "Register to workshops" do diff --git a/config/routes.rb b/config/routes.rb index 27847b88..42627da9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,6 +37,8 @@ Osem::Application.routes.draw do put "/event_types" => "eventtype#update", :as => "eventtype_update" put "/difficulty_levels" => "difficulty_levels#update" resources :difficulty_levels + put "/questions/update_conference" => "questions#update_conference" + resources :questions resources :events do member do post :comment diff --git a/db/migrate/20140109190643_create_questions.rb b/db/migrate/20140109190643_create_questions.rb new file mode 100644 index 00000000..e0272cdb --- /dev/null +++ b/db/migrate/20140109190643_create_questions.rb @@ -0,0 +1,12 @@ +class CreateQuestions < ActiveRecord::Migration + def change + create_table :questions do |t| + t.string :title + t.references :question_type + t.integer :conference_id + t.boolean :global + + t.timestamps + end + end +end diff --git a/db/migrate/20140109190844_create_question_types.rb b/db/migrate/20140109190844_create_question_types.rb new file mode 100644 index 00000000..3a2582fd --- /dev/null +++ b/db/migrate/20140109190844_create_question_types.rb @@ -0,0 +1,9 @@ +class CreateQuestionTypes < ActiveRecord::Migration + def change + create_table :question_types do |t| + t.string :title + + t.timestamps + end + end +end diff --git a/db/migrate/20140109191042_create_answers.rb b/db/migrate/20140109191042_create_answers.rb new file mode 100644 index 00000000..35394d79 --- /dev/null +++ b/db/migrate/20140109191042_create_answers.rb @@ -0,0 +1,9 @@ +class CreateAnswers < ActiveRecord::Migration + def change + create_table :answers do |t| + t.string :title + + t.timestamps + end + end +end diff --git a/db/migrate/20140109191145_create_qanswers.rb b/db/migrate/20140109191145_create_qanswers.rb new file mode 100644 index 00000000..4b31744f --- /dev/null +++ b/db/migrate/20140109191145_create_qanswers.rb @@ -0,0 +1,8 @@ +class CreateQanswers < ActiveRecord::Migration + def change + create_table :qanswers do |t| + t.references :question + t.references :answer + end + end +end diff --git a/db/migrate/20140109191321_create_conferences_questions.rb b/db/migrate/20140109191321_create_conferences_questions.rb new file mode 100644 index 00000000..4a46952b --- /dev/null +++ b/db/migrate/20140109191321_create_conferences_questions.rb @@ -0,0 +1,8 @@ +class CreateConferencesQuestions < ActiveRecord::Migration + def change + create_table :conferences_questions, :id => false do |t| + t.references :conference + t.references :question + end + end +end diff --git a/db/migrate/20140112192801_create_qanswers_registrations.rb b/db/migrate/20140112192801_create_qanswers_registrations.rb new file mode 100644 index 00000000..b1a6b79c --- /dev/null +++ b/db/migrate/20140112192801_create_qanswers_registrations.rb @@ -0,0 +1,8 @@ +class CreateQanswersRegistrations < ActiveRecord::Migration + def change + create_table :qanswers_registrations, :id => false do |t| + t.references :registration, :null => false + t.references :qanswer, :null => false + end + end +end diff --git a/db/seeds.rb b/db/seeds.rb index bd19ebaa..6b7063dc 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -8,3 +8,19 @@ Role.create(:name => "Participant") Role.create(:name => "Organizer") Role.create(:name => "Admin") + +qtype_yesno = QuestionType.create(:title => "Yes/No") +qtype_single = QuestionType.create(:title => "Single Choice") +qtype_multiple = QuestionType.create(:title => "Multiple Choice") + +answer_yes = Answer.create(:title => "Yes") +answer_no = Answer.create(:title => "No") + +questions_yes_no = ["Do you need handicapped access to the venue?", "Are you attending with partner?", "Will you attend the social event(s)?", "Will you stay at suggested hotel?"] + +for i in questions_yes_no do + q = Question.create(:title => i, :question_type_id => qtype_yesno.id, :global => true) + + Qanswer.create(:question_id => q.id, :answer_id => answer_no.id) + Qanswer.create(:question_id => q.id, :answer_id => answer_yes.id) +end \ No newline at end of file