From 90b30db9e886e3e10735e43a397d08e8feb959d1 Mon Sep 17 00:00:00 2001 From: Matt Barringer Date: Mon, 7 Jan 2013 09:04:09 +0100 Subject: [PATCH] First checkin --- Gemfile | 50 + Gemfile.lock | 183 + README.rdoc | 261 + Rakefile | 7 + app/assets/images/rails.png | Bin 0 -> 6646 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_55_fbec88_40x100.png | Bin 0 -> 182 bytes .../images/ui-bg_glass_75_d0e5f5_1x400.png | Bin 0 -> 124 bytes .../images/ui-bg_glass_85_dfeffc_1x400.png | Bin 0 -> 123 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 149 bytes .../ui-bg_gloss-wave_55_5c9ccc_500x100.png | Bin 0 -> 3457 bytes .../ui-bg_inset-hard_100_f5f8f9_1x100.png | Bin 0 -> 104 bytes .../ui-bg_inset-hard_100_fcfdfd_1x100.png | Bin 0 -> 88 bytes app/assets/images/ui-icons_217bc0_256x240.png | Bin 0 -> 4369 bytes app/assets/images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes app/assets/images/ui-icons_469bdd_256x240.png | Bin 0 -> 4369 bytes app/assets/images/ui-icons_6da8d5_256x240.png | Bin 0 -> 4369 bytes app/assets/images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes app/assets/images/ui-icons_d8e7f3_256x240.png | Bin 0 -> 4369 bytes app/assets/images/ui-icons_f9bd01_256x240.png | Bin 0 -> 5355 bytes app/assets/javascripts/admin/admin.js.coffee | 3 + .../admin/call_for_papers.js.coffee | 3 + .../javascripts/admin/conference.js.coffee | 3 + app/assets/javascripts/admin/people.js.coffee | 3 + app/assets/javascripts/admin/users.js.coffee | 3 + app/assets/javascripts/admin/venue.js.coffee | 3 + app/assets/javascripts/application.js | 74 + app/assets/javascripts/bootstrap.js.coffee | 4 + app/assets/javascripts/home.js.coffee | 3 + app/assets/stylesheets/admin/admin.css.scss | 3 + .../admin/call_for_papers.css.scss | 3 + .../stylesheets/admin/conference.css.scss | 3 + app/assets/stylesheets/admin/people.css.scss | 3 + app/assets/stylesheets/admin/users.css.scss | 3 + app/assets/stylesheets/admin/venue.css.scss | 3 + app/assets/stylesheets/application.css | 7 + .../stylesheets/bootstrap_and_overrides.css | 7 + .../bootstrap_and_overrides.css.less | 33 + app/assets/stylesheets/home.css.scss | 3 + .../stylesheets/jquery-ui-1.9.2.custom.css | 325 + app/assets/stylesheets/osem.css | 102 + .../admin/callforpapers_controller.rb | 28 + .../admin/conference_controller.rb | 36 + app/controllers/admin/events_controller.rb | 41 + app/controllers/admin/eventtype_controller.rb | 17 + app/controllers/admin/people_controller.rb | 24 + .../admin/registrations_controller.rb | 16 + app/controllers/admin/rooms_controller.rb | 18 + app/controllers/admin/tracks_controller.rb | 18 + app/controllers/admin/users_controller.rb | 20 + app/controllers/admin/venue_controller.rb | 24 + app/controllers/application_controller.rb | 42 + .../conference_registration_controller.rb | 51 + .../event_attachments_controller.rb | 97 + app/controllers/home_controller.rb | 7 + app/controllers/proposal_controller.rb | 97 + app/controllers/registrations_controller.rb | 52 + app/helpers/admin/call_for_papers_helper.rb | 2 + app/helpers/admin/conference_helper.rb | 2 + app/helpers/admin/people_helper.rb | 2 + app/helpers/admin/users_helper.rb | 2 + app/helpers/admin/venue_helper.rb | 2 + app/helpers/application_helper.rb | 43 + app/helpers/home_helper.rb | 2 + app/mailers/.gitkeep | 0 app/models/.gitkeep | 0 app/models/ability.rb | 28 + app/models/admin_ability.rb | 17 + app/models/call_for_papers.rb | 7 + app/models/comment.rb | 48 + app/models/conference.rb | 87 + app/models/event.rb | 101 + app/models/event_attachment.rb | 28 + app/models/event_person.rb | 8 + app/models/event_type.rb | 5 + app/models/person.rb | 71 + app/models/registration.rb | 9 + app/models/role.rb | 4 + app/models/room.rb | 18 + app/models/track.rb | 17 + app/models/user.rb | 55 + app/models/venue.rb | 14 + app/views/admin/callforpapers/show.html.haml | 11 + app/views/admin/conference/_sidebar.html.haml | 26 + app/views/admin/conference/index.html.haml | 3 + app/views/admin/conference/new.html.haml | 13 + app/views/admin/conference/show.html.haml | 17 + .../admin/events/_person_fields.html.haml | 6 + app/views/admin/events/edit.html.haml | 2 + app/views/admin/events/index.html.haml | 48 + app/views/admin/events/show.html.haml | 112 + .../eventtype/_event_type_fields.html.erb | 7 + .../admin/eventtype/event_type_list.html.haml | 8 + app/views/admin/people/index.html.haml | 18 + app/views/admin/people/show.html.haml | 49 + app/views/admin/registrations/show.html.haml | 48 + app/views/admin/registrations/show.pdf.prawn | 38 + app/views/admin/rooms/_room_fields.html.erb | 8 + app/views/admin/rooms/rooms_list.html.haml | 8 + app/views/admin/tracks/_track_fields.html.erb | 8 + app/views/admin/tracks/tracks_list.html.haml | 8 + app/views/admin/users/index.html.haml | 57 + app/views/admin/venue/venue_info.html.haml | 11 + .../register.html.haml | 22 + app/views/devise/confirmations/new.html.erb | 12 + .../mailer/confirmation_instructions.html.erb | 5 + .../reset_password_instructions.html.erb | 8 + .../mailer/unlock_instructions.html.erb | 7 + app/views/devise/passwords/edit.html.erb | 16 + app/views/devise/passwords/new.html.erb | 12 + .../registrations/_edit_account.html.erb | 21 + .../registrations/_edit_profile.html.erb | 20 + .../devise/registrations/edit.html.erb-backup | 18 + app/views/devise/registrations/edit.html.haml | 19 + app/views/devise/registrations/new.html.erb | 18 + app/views/devise/sessions/new.html.erb-backup | 18 + app/views/devise/sessions/new.html.haml | 13 + app/views/devise/shared/_links.erb | 25 + app/views/devise/unlocks/new.html.erb | 12 + app/views/home/index.html.haml | 38 + app/views/layouts/_admin_navigation.html.haml | 44 + app/views/layouts/_messages.html.haml | 5 + app/views/layouts/_navigation.html.haml | 11 + app/views/layouts/admin.html.haml | 27 + app/views/layouts/application.html.haml | 35 + app/views/proposal/_form.html.haml | 142 + app/views/proposal/edit.html.haml | 2 + app/views/proposal/index.html.haml | 18 + app/views/proposal/new.html.haml | 2 + .../shared/_dynamic_association.html.haml | 10 + config.ru | 4 + config/application.rb | 63 + config/boot.rb | 6 + config/database.yml | 25 + config/deploy.rb | 16 + config/environment.rb | 5 + config/environments/development.rb | 39 + config/environments/production.rb | 67 + config/environments/test.rb | 37 + config/initializers/backtrace_silencers.rb | 7 + config/initializers/devise.rb | 232 + config/initializers/formtastic.rb | 79 + config/initializers/inflections.rb | 15 + config/initializers/mime_types.rb | 5 + config/initializers/secret_token.rb | 7 + config/initializers/session_store.rb | 8 + config/initializers/wrap_parameters.rb | 14 + config/locales/devise.en.yml | 58 + config/locales/en.yml | 5 + config/routes.rb | 44 + .../20121223110942_devise_create_users.rb | 49 + ...20121223115038_create_conferences_table.rb | 24 + .../20121223115106_create_people_table.rb | 24 + .../20121223115117_create_rooms_table.rb | 15 + .../20121223115125_create_tracks_table.rb | 16 + .../20121223115135_create_events_table.rb | 31 + .../20121223120413_create_event_types.rb | 13 + ...0121223122307_create_event_people_table.rb | 18 + .../20121223122842_create_venue_table.rb | 20 + .../20121223135240_create_roles_table.rb | 12 + db/migrate/20121223135447_user_roles_table.rb | 12 + db/migrate/20121224144728_create_cfp_table.rb | 17 + ...130103134212_create_registrations_table.rb | 19 + db/migrate/20130104142209_create_comments.rb | 19 + ...9_acts_as_commentable_upgrade_migration.rb | 19 + ...05115603_create_event_attachments_table.rb | 18 + db/schema.rb | 208 + db/seeds.rb | 10 + doc/README_FOR_APP | 2 + lib/assets/.gitkeep | 0 lib/tasks/.gitkeep | 0 lib/templates/erb/scaffold/_form.html.erb | 11 + public/404.html | 26 + public/422.html | 26 + public/500.html | 25 + ...ation-a5bafc9682813b77f6e8431d24eb3e48.css | 1 + ...on-a5bafc9682813b77f6e8431d24eb3e48.css.gz | Bin 0 -> 22535 bytes ...cation-d6a9b6cf72852a187374417f72027216.js | 25 + ...ion-d6a9b6cf72852a187374417f72027216.js.gz | Bin 0 -> 103697 bytes public/assets/application.css | 1615 + public/assets/application.css.gz | Bin 0 -> 28160 bytes public/assets/application.js | 30303 ++++++++++++++++ public/assets/application.js.gz | Bin 0 -> 230211 bytes ...font-8255009c90d86ee3458a13f5db9a6ed0.woff | Bin 0 -> 41752 bytes ...bfont-86a6da438b901860c3a54e21f9098bf8.eot | Bin 0 -> 38708 bytes ...bfont-89ca9bd89cea7c74302ae94f18d4ec3e.ttf | Bin 0 -> 68476 bytes ...bfont-c3f3594758768fbb43eae746d18b3b50.svg | 255 + public/assets/fontawesome-webfont.eot | Bin 0 -> 38708 bytes public/assets/fontawesome-webfont.svg | 255 + public/assets/fontawesome-webfont.ttf | Bin 0 -> 68476 bytes public/assets/fontawesome-webfont.woff | Bin 0 -> 41752 bytes ...rails-a3386665c05a2d82f711a4aaa72d247c.png | Bin 0 -> 6646 bytes public/assets/images/rails.png | Bin 0 -> 6646 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_55_fbec88_40x100.png | Bin 0 -> 182 bytes .../images/ui-bg_glass_75_d0e5f5_1x400.png | Bin 0 -> 124 bytes .../images/ui-bg_glass_85_dfeffc_1x400.png | Bin 0 -> 123 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 149 bytes .../ui-bg_gloss-wave_55_5c9ccc_500x100.png | Bin 0 -> 3457 bytes .../ui-bg_inset-hard_100_f5f8f9_1x100.png | Bin 0 -> 104 bytes .../ui-bg_inset-hard_100_fcfdfd_1x100.png | Bin 0 -> 88 bytes .../assets/images/ui-icons_217bc0_256x240.png | Bin 0 -> 4369 bytes .../assets/images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../assets/images/ui-icons_469bdd_256x240.png | Bin 0 -> 4369 bytes .../assets/images/ui-icons_6da8d5_256x240.png | Bin 0 -> 4369 bytes .../assets/images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../assets/images/ui-icons_d8e7f3_256x240.png | Bin 0 -> 4369 bytes .../assets/images/ui-icons_f9bd01_256x240.png | Bin 0 -> 5355 bytes public/assets/loading.gif | Bin 0 -> 3897 bytes public/assets/manifest.yml | 53 + public/assets/progressbar.gif | Bin 0 -> 3323 bytes ...rails-a3386665c05a2d82f711a4aaa72d247c.png | Bin 0 -> 6646 bytes public/assets/rails.png | Bin 0 -> 6646 bytes ...lings-2851b489e8c39f8fad44fc10efb99c3e.png | Bin 0 -> 12799 bytes ...white-6cccd17a7aed91dbc0157d343c68c0d9.png | Bin 0 -> 8777 bytes .../bootstrap/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes .../bootstrap/glyphicons-halflings.png | Bin 0 -> 12799 bytes public/assets/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes public/assets/ui-bg_flat_55_fbec88_40x100.png | Bin 0 -> 182 bytes public/assets/ui-bg_glass_75_d0e5f5_1x400.png | Bin 0 -> 124 bytes public/assets/ui-bg_glass_85_dfeffc_1x400.png | Bin 0 -> 123 bytes public/assets/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 149 bytes .../ui-bg_gloss-wave_55_5c9ccc_500x100.png | Bin 0 -> 3457 bytes .../ui-bg_inset-hard_100_f5f8f9_1x100.png | Bin 0 -> 104 bytes .../ui-bg_inset-hard_100_fcfdfd_1x100.png | Bin 0 -> 88 bytes public/assets/ui-icons_217bc0_256x240.png | Bin 0 -> 4369 bytes public/assets/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes public/assets/ui-icons_469bdd_256x240.png | Bin 0 -> 4369 bytes public/assets/ui-icons_6da8d5_256x240.png | Bin 0 -> 4369 bytes public/assets/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes public/assets/ui-icons_d8e7f3_256x240.png | Bin 0 -> 4369 bytes public/assets/ui-icons_f9bd01_256x240.png | Bin 0 -> 5355 bytes public/favicon.ico | 0 public/robots.txt | 5 + script/rails | 6 + test/fixtures/.gitkeep | 0 test/fixtures/users.yml | 11 + test/functional/.gitkeep | 0 .../functional/admin/admin_controller_test.rb | 7 + .../admin/call_for_papers_controller_test.rb | 7 + .../admin/conference_controller_test.rb | 7 + .../admin/people_controller_test.rb | 7 + .../functional/admin/users_controller_test.rb | 7 + .../functional/admin/venue_controller_test.rb | 7 + test/functional/home_controller_test.rb | 7 + test/integration/.gitkeep | 0 test/performance/browsing_test.rb | 12 + test/test_helper.rb | 13 + test/unit/.gitkeep | 0 test/unit/helpers/admin/admin_helper_test.rb | 4 + .../admin/call_for_papers_helper_test.rb | 4 + .../helpers/admin/conference_helper_test.rb | 4 + test/unit/helpers/admin/people_helper_test.rb | 4 + test/unit/helpers/admin/users_helper_test.rb | 4 + test/unit/helpers/admin/venue_helper_test.rb | 4 + test/unit/helpers/home_helper_test.rb | 4 + test/unit/user_test.rb | 7 + vendor/assets/javascripts/.gitkeep | 0 vendor/assets/stylesheets/.gitkeep | 0 vendor/plugins/.gitkeep | 0 260 files changed, 37349 insertions(+) create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 README.rdoc create mode 100644 Rakefile create mode 100644 app/assets/images/rails.png create mode 100644 app/assets/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 app/assets/images/ui-bg_flat_55_fbec88_40x100.png create mode 100644 app/assets/images/ui-bg_glass_75_d0e5f5_1x400.png create mode 100644 app/assets/images/ui-bg_glass_85_dfeffc_1x400.png create mode 100644 app/assets/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 app/assets/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png create mode 100644 app/assets/images/ui-bg_inset-hard_100_f5f8f9_1x100.png create mode 100644 app/assets/images/ui-bg_inset-hard_100_fcfdfd_1x100.png create mode 100644 app/assets/images/ui-icons_217bc0_256x240.png create mode 100644 app/assets/images/ui-icons_2e83ff_256x240.png create mode 100644 app/assets/images/ui-icons_469bdd_256x240.png create mode 100644 app/assets/images/ui-icons_6da8d5_256x240.png create mode 100644 app/assets/images/ui-icons_cd0a0a_256x240.png create mode 100644 app/assets/images/ui-icons_d8e7f3_256x240.png create mode 100644 app/assets/images/ui-icons_f9bd01_256x240.png create mode 100644 app/assets/javascripts/admin/admin.js.coffee create mode 100644 app/assets/javascripts/admin/call_for_papers.js.coffee create mode 100644 app/assets/javascripts/admin/conference.js.coffee create mode 100644 app/assets/javascripts/admin/people.js.coffee create mode 100644 app/assets/javascripts/admin/users.js.coffee create mode 100644 app/assets/javascripts/admin/venue.js.coffee create mode 100644 app/assets/javascripts/application.js create mode 100644 app/assets/javascripts/bootstrap.js.coffee create mode 100644 app/assets/javascripts/home.js.coffee create mode 100644 app/assets/stylesheets/admin/admin.css.scss create mode 100644 app/assets/stylesheets/admin/call_for_papers.css.scss create mode 100644 app/assets/stylesheets/admin/conference.css.scss create mode 100644 app/assets/stylesheets/admin/people.css.scss create mode 100644 app/assets/stylesheets/admin/users.css.scss create mode 100644 app/assets/stylesheets/admin/venue.css.scss create mode 100644 app/assets/stylesheets/application.css create mode 100644 app/assets/stylesheets/bootstrap_and_overrides.css create mode 100644 app/assets/stylesheets/bootstrap_and_overrides.css.less create mode 100644 app/assets/stylesheets/home.css.scss create mode 100644 app/assets/stylesheets/jquery-ui-1.9.2.custom.css create mode 100644 app/assets/stylesheets/osem.css create mode 100644 app/controllers/admin/callforpapers_controller.rb create mode 100644 app/controllers/admin/conference_controller.rb create mode 100644 app/controllers/admin/events_controller.rb create mode 100644 app/controllers/admin/eventtype_controller.rb create mode 100644 app/controllers/admin/people_controller.rb create mode 100644 app/controllers/admin/registrations_controller.rb create mode 100644 app/controllers/admin/rooms_controller.rb create mode 100644 app/controllers/admin/tracks_controller.rb create mode 100644 app/controllers/admin/users_controller.rb create mode 100644 app/controllers/admin/venue_controller.rb create mode 100644 app/controllers/application_controller.rb create mode 100644 app/controllers/conference_registration_controller.rb create mode 100644 app/controllers/event_attachments_controller.rb create mode 100644 app/controllers/home_controller.rb create mode 100644 app/controllers/proposal_controller.rb create mode 100644 app/controllers/registrations_controller.rb create mode 100644 app/helpers/admin/call_for_papers_helper.rb create mode 100644 app/helpers/admin/conference_helper.rb create mode 100644 app/helpers/admin/people_helper.rb create mode 100644 app/helpers/admin/users_helper.rb create mode 100644 app/helpers/admin/venue_helper.rb create mode 100644 app/helpers/application_helper.rb create mode 100644 app/helpers/home_helper.rb create mode 100644 app/mailers/.gitkeep create mode 100644 app/models/.gitkeep create mode 100644 app/models/ability.rb create mode 100644 app/models/admin_ability.rb create mode 100644 app/models/call_for_papers.rb create mode 100644 app/models/comment.rb create mode 100644 app/models/conference.rb create mode 100644 app/models/event.rb create mode 100644 app/models/event_attachment.rb create mode 100644 app/models/event_person.rb create mode 100644 app/models/event_type.rb create mode 100644 app/models/person.rb create mode 100644 app/models/registration.rb create mode 100644 app/models/role.rb create mode 100644 app/models/room.rb create mode 100644 app/models/track.rb create mode 100644 app/models/user.rb create mode 100644 app/models/venue.rb create mode 100644 app/views/admin/callforpapers/show.html.haml create mode 100644 app/views/admin/conference/_sidebar.html.haml create mode 100644 app/views/admin/conference/index.html.haml create mode 100644 app/views/admin/conference/new.html.haml create mode 100644 app/views/admin/conference/show.html.haml create mode 100644 app/views/admin/events/_person_fields.html.haml create mode 100644 app/views/admin/events/edit.html.haml create mode 100644 app/views/admin/events/index.html.haml create mode 100644 app/views/admin/events/show.html.haml create mode 100644 app/views/admin/eventtype/_event_type_fields.html.erb create mode 100644 app/views/admin/eventtype/event_type_list.html.haml create mode 100644 app/views/admin/people/index.html.haml create mode 100644 app/views/admin/people/show.html.haml create mode 100644 app/views/admin/registrations/show.html.haml create mode 100644 app/views/admin/registrations/show.pdf.prawn create mode 100644 app/views/admin/rooms/_room_fields.html.erb create mode 100644 app/views/admin/rooms/rooms_list.html.haml create mode 100644 app/views/admin/tracks/_track_fields.html.erb create mode 100644 app/views/admin/tracks/tracks_list.html.haml create mode 100644 app/views/admin/users/index.html.haml create mode 100644 app/views/admin/venue/venue_info.html.haml create mode 100644 app/views/conference_registration/register.html.haml create mode 100644 app/views/devise/confirmations/new.html.erb create mode 100644 app/views/devise/mailer/confirmation_instructions.html.erb create mode 100644 app/views/devise/mailer/reset_password_instructions.html.erb create mode 100644 app/views/devise/mailer/unlock_instructions.html.erb create mode 100644 app/views/devise/passwords/edit.html.erb create mode 100644 app/views/devise/passwords/new.html.erb create mode 100644 app/views/devise/registrations/_edit_account.html.erb create mode 100644 app/views/devise/registrations/_edit_profile.html.erb create mode 100644 app/views/devise/registrations/edit.html.erb-backup create mode 100644 app/views/devise/registrations/edit.html.haml create mode 100644 app/views/devise/registrations/new.html.erb create mode 100644 app/views/devise/sessions/new.html.erb-backup create mode 100644 app/views/devise/sessions/new.html.haml create mode 100644 app/views/devise/shared/_links.erb create mode 100644 app/views/devise/unlocks/new.html.erb create mode 100644 app/views/home/index.html.haml create mode 100644 app/views/layouts/_admin_navigation.html.haml create mode 100644 app/views/layouts/_messages.html.haml create mode 100644 app/views/layouts/_navigation.html.haml create mode 100644 app/views/layouts/admin.html.haml create mode 100644 app/views/layouts/application.html.haml create mode 100644 app/views/proposal/_form.html.haml create mode 100644 app/views/proposal/edit.html.haml create mode 100644 app/views/proposal/index.html.haml create mode 100644 app/views/proposal/new.html.haml create mode 100644 app/views/shared/_dynamic_association.html.haml create mode 100644 config.ru create mode 100644 config/application.rb create mode 100644 config/boot.rb create mode 100644 config/database.yml create mode 100644 config/deploy.rb create mode 100644 config/environment.rb create mode 100644 config/environments/development.rb create mode 100644 config/environments/production.rb create mode 100644 config/environments/test.rb create mode 100644 config/initializers/backtrace_silencers.rb create mode 100644 config/initializers/devise.rb create mode 100644 config/initializers/formtastic.rb create mode 100644 config/initializers/inflections.rb create mode 100644 config/initializers/mime_types.rb create mode 100644 config/initializers/secret_token.rb create mode 100644 config/initializers/session_store.rb create mode 100644 config/initializers/wrap_parameters.rb create mode 100644 config/locales/devise.en.yml create mode 100644 config/locales/en.yml create mode 100644 config/routes.rb create mode 100644 db/migrate/20121223110942_devise_create_users.rb create mode 100644 db/migrate/20121223115038_create_conferences_table.rb create mode 100644 db/migrate/20121223115106_create_people_table.rb create mode 100644 db/migrate/20121223115117_create_rooms_table.rb create mode 100644 db/migrate/20121223115125_create_tracks_table.rb create mode 100644 db/migrate/20121223115135_create_events_table.rb create mode 100644 db/migrate/20121223120413_create_event_types.rb create mode 100644 db/migrate/20121223122307_create_event_people_table.rb create mode 100644 db/migrate/20121223122842_create_venue_table.rb create mode 100644 db/migrate/20121223135240_create_roles_table.rb create mode 100644 db/migrate/20121223135447_user_roles_table.rb create mode 100644 db/migrate/20121224144728_create_cfp_table.rb create mode 100644 db/migrate/20130103134212_create_registrations_table.rb create mode 100644 db/migrate/20130104142209_create_comments.rb create mode 100644 db/migrate/20130104142839_acts_as_commentable_upgrade_migration.rb create mode 100644 db/migrate/20130105115603_create_event_attachments_table.rb create mode 100644 db/schema.rb create mode 100644 db/seeds.rb create mode 100644 doc/README_FOR_APP create mode 100644 lib/assets/.gitkeep create mode 100644 lib/tasks/.gitkeep create mode 100644 lib/templates/erb/scaffold/_form.html.erb create mode 100644 public/404.html create mode 100644 public/422.html create mode 100644 public/500.html create mode 100644 public/assets/application-a5bafc9682813b77f6e8431d24eb3e48.css create mode 100644 public/assets/application-a5bafc9682813b77f6e8431d24eb3e48.css.gz create mode 100644 public/assets/application-d6a9b6cf72852a187374417f72027216.js create mode 100644 public/assets/application-d6a9b6cf72852a187374417f72027216.js.gz create mode 100644 public/assets/application.css create mode 100644 public/assets/application.css.gz create mode 100644 public/assets/application.js create mode 100644 public/assets/application.js.gz create mode 100755 public/assets/fontawesome-webfont-8255009c90d86ee3458a13f5db9a6ed0.woff create mode 100755 public/assets/fontawesome-webfont-86a6da438b901860c3a54e21f9098bf8.eot create mode 100755 public/assets/fontawesome-webfont-89ca9bd89cea7c74302ae94f18d4ec3e.ttf create mode 100755 public/assets/fontawesome-webfont-c3f3594758768fbb43eae746d18b3b50.svg create mode 100755 public/assets/fontawesome-webfont.eot create mode 100755 public/assets/fontawesome-webfont.svg create mode 100755 public/assets/fontawesome-webfont.ttf create mode 100755 public/assets/fontawesome-webfont.woff create mode 100644 public/assets/images/rails-a3386665c05a2d82f711a4aaa72d247c.png create mode 100644 public/assets/images/rails.png create mode 100644 public/assets/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 public/assets/images/ui-bg_flat_55_fbec88_40x100.png create mode 100644 public/assets/images/ui-bg_glass_75_d0e5f5_1x400.png create mode 100644 public/assets/images/ui-bg_glass_85_dfeffc_1x400.png create mode 100644 public/assets/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 public/assets/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png create mode 100644 public/assets/images/ui-bg_inset-hard_100_f5f8f9_1x100.png create mode 100644 public/assets/images/ui-bg_inset-hard_100_fcfdfd_1x100.png create mode 100644 public/assets/images/ui-icons_217bc0_256x240.png create mode 100644 public/assets/images/ui-icons_2e83ff_256x240.png create mode 100644 public/assets/images/ui-icons_469bdd_256x240.png create mode 100644 public/assets/images/ui-icons_6da8d5_256x240.png create mode 100644 public/assets/images/ui-icons_cd0a0a_256x240.png create mode 100644 public/assets/images/ui-icons_d8e7f3_256x240.png create mode 100644 public/assets/images/ui-icons_f9bd01_256x240.png create mode 100644 public/assets/loading.gif create mode 100644 public/assets/manifest.yml create mode 100644 public/assets/progressbar.gif create mode 100644 public/assets/rails-a3386665c05a2d82f711a4aaa72d247c.png create mode 100644 public/assets/rails.png create mode 100644 public/assets/twitter/bootstrap/glyphicons-halflings-2851b489e8c39f8fad44fc10efb99c3e.png create mode 100644 public/assets/twitter/bootstrap/glyphicons-halflings-white-6cccd17a7aed91dbc0157d343c68c0d9.png create mode 100644 public/assets/twitter/bootstrap/glyphicons-halflings-white.png create mode 100644 public/assets/twitter/bootstrap/glyphicons-halflings.png create mode 100644 public/assets/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 public/assets/ui-bg_flat_55_fbec88_40x100.png create mode 100644 public/assets/ui-bg_glass_75_d0e5f5_1x400.png create mode 100644 public/assets/ui-bg_glass_85_dfeffc_1x400.png create mode 100644 public/assets/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 public/assets/ui-bg_gloss-wave_55_5c9ccc_500x100.png create mode 100644 public/assets/ui-bg_inset-hard_100_f5f8f9_1x100.png create mode 100644 public/assets/ui-bg_inset-hard_100_fcfdfd_1x100.png create mode 100644 public/assets/ui-icons_217bc0_256x240.png create mode 100644 public/assets/ui-icons_2e83ff_256x240.png create mode 100644 public/assets/ui-icons_469bdd_256x240.png create mode 100644 public/assets/ui-icons_6da8d5_256x240.png create mode 100644 public/assets/ui-icons_cd0a0a_256x240.png create mode 100644 public/assets/ui-icons_d8e7f3_256x240.png create mode 100644 public/assets/ui-icons_f9bd01_256x240.png create mode 100644 public/favicon.ico create mode 100644 public/robots.txt create mode 100755 script/rails create mode 100644 test/fixtures/.gitkeep create mode 100644 test/fixtures/users.yml create mode 100644 test/functional/.gitkeep create mode 100644 test/functional/admin/admin_controller_test.rb create mode 100644 test/functional/admin/call_for_papers_controller_test.rb create mode 100644 test/functional/admin/conference_controller_test.rb create mode 100644 test/functional/admin/people_controller_test.rb create mode 100644 test/functional/admin/users_controller_test.rb create mode 100644 test/functional/admin/venue_controller_test.rb create mode 100644 test/functional/home_controller_test.rb create mode 100644 test/integration/.gitkeep create mode 100644 test/performance/browsing_test.rb create mode 100644 test/test_helper.rb create mode 100644 test/unit/.gitkeep create mode 100644 test/unit/helpers/admin/admin_helper_test.rb create mode 100644 test/unit/helpers/admin/call_for_papers_helper_test.rb create mode 100644 test/unit/helpers/admin/conference_helper_test.rb create mode 100644 test/unit/helpers/admin/people_helper_test.rb create mode 100644 test/unit/helpers/admin/users_helper_test.rb create mode 100644 test/unit/helpers/admin/venue_helper_test.rb create mode 100644 test/unit/helpers/home_helper_test.rb create mode 100644 test/unit/user_test.rb create mode 100644 vendor/assets/javascripts/.gitkeep create mode 100644 vendor/assets/stylesheets/.gitkeep create mode 100644 vendor/plugins/.gitkeep diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..8722ffd8 --- /dev/null +++ b/Gemfile @@ -0,0 +1,50 @@ +source 'https://rubygems.org' + +gem 'rails', '3.2.9' + +# Bundle edge Rails instead: +# gem 'rails', :git => 'git://github.com/rails/rails.git' + +gem 'sqlite3' + + +# Gems used only for assets and not required +# in production environments by default. +group :assets do + gem 'sass-rails', '~> 3.2.3' + gem 'coffee-rails', '~> 3.2.1' + + # See https://github.com/sstephenson/execjs#readme for more supported runtimes + # gem 'therubyracer', :platforms => :ruby + + gem 'uglifier', '>= 1.0.3' +end + +gem 'paperclip', '~> 3.0' +gem 'jquery-rails' +gem 'jquery_datepicker' +gem 'jquery-fileupload-rails' +gem 'devise' +gem 'cancan' +gem "haml" +gem 'bcrypt-ruby', '~> 3.0.0' +gem 'node' +gem 'twitter-bootstrap-rails' +gem 'formtastic-bootstrap' +gem 'cocoon' +gem 'transitions', :require => ["transitions", "active_record/transitions"] +gem 'acts_as_commentable_with_threading' +gem 'prawn' +gem 'prawn_rails' + +# To use Jbuilder templates for JSON +# gem 'jbuilder' + +# Use unicorn as the app server +# gem 'unicorn' + +# Deploy with Capistrano +# gem 'capistrano' + +# To use debugger +# gem 'debugger' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..087506f4 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,183 @@ +GEM + remote: https://rubygems.org/ + specs: + Ascii85 (1.0.2) + actionmailer (3.2.9) + actionpack (= 3.2.9) + mail (~> 2.4.4) + actionpack (3.2.9) + activemodel (= 3.2.9) + activesupport (= 3.2.9) + builder (~> 3.0.0) + erubis (~> 2.7.0) + journey (~> 1.0.4) + rack (~> 1.4.0) + rack-cache (~> 1.2) + rack-test (~> 0.6.1) + sprockets (~> 2.2.1) + activemodel (3.2.9) + activesupport (= 3.2.9) + builder (~> 3.0.0) + activerecord (3.2.9) + activemodel (= 3.2.9) + activesupport (= 3.2.9) + arel (~> 3.0.2) + tzinfo (~> 0.3.29) + activeresource (3.2.9) + activemodel (= 3.2.9) + activesupport (= 3.2.9) + activesupport (3.2.9) + i18n (~> 0.6) + multi_json (~> 1.0) + acts_as_commentable_with_threading (1.1.2) + activerecord (>= 3.0) + activesupport (~> 3.0) + awesome_nested_set (>= 2.0) + arel (3.0.2) + awesome_nested_set (2.1.5) + activerecord (>= 3.0.0) + bcrypt-ruby (3.0.1) + builder (3.0.4) + cancan (1.6.8) + cocaine (0.4.2) + cocoon (1.1.1) + coffee-rails (3.2.2) + coffee-script (>= 2.2.0) + railties (~> 3.2.0) + coffee-script (2.2.0) + coffee-script-source + execjs + coffee-script-source (1.4.0) + devise (2.1.2) + bcrypt-ruby (~> 3.0) + orm_adapter (~> 0.1) + railties (~> 3.1) + warden (~> 1.2.1) + erubis (2.7.0) + eventmachine (1.0.0) + execjs (1.4.0) + multi_json (~> 1.0) + formtastic (2.2.1) + actionpack (>= 3.0) + formtastic-bootstrap (2.0.0) + formtastic (~> 2.2) + formtastic-bootstrap + haml (3.1.7) + hashery (2.1.0) + hike (1.2.1) + i18n (0.6.1) + journey (1.0.4) + jquery-fileupload-rails (0.4.0) + actionpack (>= 3.1) + railties (>= 3.1) + jquery-rails (2.1.4) + railties (>= 3.0, < 5.0) + thor (>= 0.14, < 2.0) + jquery_datepicker (0.4) + json (1.7.5) + mail (2.4.4) + i18n (>= 0.4.0) + mime-types (~> 1.16) + treetop (~> 1.4.8) + mime-types (1.19) + multi_json (1.5.0) + node (0.0.2) + eventmachine + orm_adapter (0.4.0) + paperclip (3.4.0) + activemodel (>= 3.0.0) + activerecord (>= 3.0.0) + activesupport (>= 3.0.0) + cocaine (~> 0.4.0) + mime-types + pdf-reader (1.2.0) + Ascii85 (~> 1.0.0) + hashery (~> 2.0) + ruby-rc4 + polyglot (0.3.3) + prawn (0.12.0) + pdf-reader (>= 0.9.0) + ttfunk (~> 1.0.2) + prawn_rails (0.0.11) + prawn (>= 0.11.1) + railties (>= 3.0.0) + rack (1.4.1) + rack-cache (1.2) + rack (>= 0.4) + rack-ssl (1.3.2) + rack + rack-test (0.6.2) + rack (>= 1.0) + rails (3.2.9) + actionmailer (= 3.2.9) + actionpack (= 3.2.9) + activerecord (= 3.2.9) + activeresource (= 3.2.9) + activesupport (= 3.2.9) + bundler (~> 1.0) + railties (= 3.2.9) + railties (3.2.9) + actionpack (= 3.2.9) + activesupport (= 3.2.9) + rack-ssl (~> 1.3.2) + rake (>= 0.8.7) + rdoc (~> 3.4) + thor (>= 0.14.6, < 2.0) + rake (10.0.3) + rdoc (3.12) + json (~> 1.4) + ruby-rc4 (0.1.5) + sass (3.2.4) + sass-rails (3.2.5) + railties (~> 3.2.0) + sass (>= 3.1.10) + tilt (~> 1.3) + sprockets (2.2.2) + hike (~> 1.2) + multi_json (~> 1.0) + rack (~> 1.0) + tilt (~> 1.1, != 1.3.0) + sqlite3 (1.3.6) + thor (0.16.0) + tilt (1.3.3) + transitions (0.1.4) + treetop (1.4.12) + polyglot + polyglot (>= 0.3.1) + ttfunk (1.0.3) + twitter-bootstrap-rails (2.1.9) + actionpack (>= 3.1) + execjs + railties (>= 3.1) + tzinfo (0.3.35) + uglifier (1.3.0) + execjs (>= 0.3.0) + multi_json (~> 1.0, >= 1.0.2) + warden (1.2.1) + rack (>= 1.0) + +PLATFORMS + ruby + +DEPENDENCIES + acts_as_commentable_with_threading + bcrypt-ruby (~> 3.0.0) + cancan + cocoon + coffee-rails (~> 3.2.1) + devise + formtastic-bootstrap + haml + jquery-fileupload-rails + jquery-rails + jquery_datepicker + node + paperclip (~> 3.0) + prawn + prawn_rails + rails (= 3.2.9) + sass-rails (~> 3.2.3) + sqlite3 + transitions + twitter-bootstrap-rails + uglifier (>= 1.0.3) diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 00000000..7c36f235 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,261 @@ +== Welcome to Rails + +Rails is a web-application framework that includes everything needed to create +database-backed web applications according to the Model-View-Control pattern. + +This pattern splits the view (also called the presentation) into "dumb" +templates that are primarily responsible for inserting pre-built data in between +HTML tags. The model contains the "smart" domain objects (such as Account, +Product, Person, Post) that holds all the business logic and knows how to +persist themselves to a database. The controller handles the incoming requests +(such as Save New Account, Update Product, Show Post) by manipulating the model +and directing data to the view. + +In Rails, the model is handled by what's called an object-relational mapping +layer entitled Active Record. This layer allows you to present the data from +database rows as objects and embellish these data objects with business logic +methods. You can read more about Active Record in +link:files/vendor/rails/activerecord/README.html. + +The controller and view are handled by the Action Pack, which handles both +layers by its two parts: Action View and Action Controller. These two layers +are bundled in a single package due to their heavy interdependence. This is +unlike the relationship between the Active Record and Action Pack that is much +more separate. Each of these packages can be used independently outside of +Rails. You can read more about Action Pack in +link:files/vendor/rails/actionpack/README.html. + + +== Getting Started + +1. At the command prompt, create a new Rails application: + rails new myapp (where myapp is the application name) + +2. Change directory to myapp and start the web server: + cd myapp; rails server (run with --help for options) + +3. Go to http://localhost:3000/ and you'll see: + "Welcome aboard: You're riding Ruby on Rails!" + +4. Follow the guidelines to start developing your application. You can find +the following resources handy: + +* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html +* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ + + +== Debugging Rails + +Sometimes your application goes wrong. Fortunately there are a lot of tools that +will help you debug it and get it back on the rails. + +First area to check is the application log files. Have "tail -f" commands +running on the server.log and development.log. Rails will automatically display +debugging and runtime information to these files. Debugging info will also be +shown in the browser on requests from 127.0.0.1. + +You can also log your own messages directly into the log file from your code +using the Ruby logger class from inside your controllers. Example: + + class WeblogController < ActionController::Base + def destroy + @weblog = Weblog.find(params[:id]) + @weblog.destroy + logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") + end + end + +The result will be a message in your log file along the lines of: + + Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! + +More information on how to use the logger is at http://www.ruby-doc.org/core/ + +Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are +several books available online as well: + +* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) +* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) + +These two books will bring you up to speed on the Ruby language and also on +programming in general. + + +== Debugger + +Debugger support is available through the debugger command when you start your +Mongrel or WEBrick server with --debugger. This means that you can break out of +execution at any point in the code, investigate and change the model, and then, +resume execution! You need to install ruby-debug to run the server in debugging +mode. With gems, use sudo gem install ruby-debug. Example: + + class WeblogController < ActionController::Base + def index + @posts = Post.all + debugger + end + end + +So the controller will accept the action, run the first line, then present you +with a IRB prompt in the server window. Here you can do things like: + + >> @posts.inspect + => "[#nil, "body"=>nil, "id"=>"1"}>, + #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" + >> @posts.first.title = "hello from a debugger" + => "hello from a debugger" + +...and even better, you can examine how your runtime objects actually work: + + >> f = @posts.first + => #nil, "body"=>nil, "id"=>"1"}> + >> f. + Display all 152 possibilities? (y or n) + +Finally, when you're ready to resume execution, you can enter "cont". + + +== Console + +The console is a Ruby shell, which allows you to interact with your +application's domain model. Here you'll have all parts of the application +configured, just like it is when the application is running. You can inspect +domain models, change values, and save to the database. Starting the script +without arguments will launch it in the development environment. + +To start the console, run rails console from the application +directory. + +Options: + +* Passing the -s, --sandbox argument will rollback any modifications + made to the database. +* Passing an environment name as an argument will load the corresponding + environment. Example: rails console production. + +To reload your controllers and models after launching the console run +reload! + +More information about irb can be found at: +link:http://www.rubycentral.org/pickaxe/irb.html + + +== dbconsole + +You can go to the command line of your database directly through rails +dbconsole. You would be connected to the database with the credentials +defined in database.yml. Starting the script without arguments will connect you +to the development database. Passing an argument will connect you to a different +database, like rails dbconsole production. Currently works for MySQL, +PostgreSQL and SQLite 3. + +== Description of Contents + +The default directory structure of a generated Ruby on Rails application: + + |-- app + | |-- assets + | |-- images + | |-- javascripts + | `-- stylesheets + | |-- controllers + | |-- helpers + | |-- mailers + | |-- models + | `-- views + | `-- layouts + |-- config + | |-- environments + | |-- initializers + | `-- locales + |-- db + |-- doc + |-- lib + | `-- tasks + |-- log + |-- public + |-- script + |-- test + | |-- fixtures + | |-- functional + | |-- integration + | |-- performance + | `-- unit + |-- tmp + | |-- cache + | |-- pids + | |-- sessions + | `-- sockets + `-- vendor + |-- assets + `-- stylesheets + `-- plugins + +app + Holds all the code that's specific to this particular application. + +app/assets + Contains subdirectories for images, stylesheets, and JavaScript files. + +app/controllers + Holds controllers that should be named like weblogs_controller.rb for + automated URL mapping. All controllers should descend from + ApplicationController which itself descends from ActionController::Base. + +app/models + Holds models that should be named like post.rb. Models descend from + ActiveRecord::Base by default. + +app/views + Holds the template files for the view that should be named like + weblogs/index.html.erb for the WeblogsController#index action. All views use + eRuby syntax by default. + +app/views/layouts + Holds the template files for layouts to be used with views. This models the + common header/footer method of wrapping views. In your views, define a layout + using the layout :default and create a file named default.html.erb. + Inside default.html.erb, call <% yield %> to render the view using this + layout. + +app/helpers + Holds view helpers that should be named like weblogs_helper.rb. These are + generated for you automatically when using generators for controllers. + Helpers can be used to wrap functionality for your views into methods. + +config + Configuration files for the Rails environment, the routing map, the database, + and other dependencies. + +db + Contains the database schema in schema.rb. db/migrate contains all the + sequence of Migrations for your schema. + +doc + This directory is where your application documentation will be stored when + generated using rake doc:app + +lib + Application specific libraries. Basically, any kind of custom code that + doesn't belong under controllers, models, or helpers. This directory is in + the load path. + +public + The directory available for the web server. Also contains the dispatchers and the + default HTML files. This should be set as the DOCUMENT_ROOT of your web + server. + +script + Helper scripts for automation and generation. + +test + Unit and functional tests along with fixtures. When using the rails generate + command, template test files will be generated for you and placed in this + directory. + +vendor + External libraries that the application depends on. Also includes the plugins + subdirectory. If the app has frozen rails, those gems also go here, under + vendor/rails/. This directory is in the load path. diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..f29eb6f1 --- /dev/null +++ b/Rakefile @@ -0,0 +1,7 @@ +#!/usr/bin/env rake +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require File.expand_path('../config/application', __FILE__) + +Osem::Application.load_tasks diff --git a/app/assets/images/rails.png b/app/assets/images/rails.png new file mode 100644 index 0000000000000000000000000000000000000000..d5edc04e65f555e3ba4dcdaad39dc352e75b575e GIT binary patch literal 6646 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 AQ~&?~ literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-bg_flat_0_aaaaaa_40x100.png b/app/assets/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5dab2ab7b1c50dea9cfe73dc5a269a92d2d4b4 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FscKIb$B>N1x91EQ4=4yQ7#`R^ z$vje}bP0l+XkK DSH>_4 literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-bg_flat_55_fbec88_40x100.png b/app/assets/images/ui-bg_flat_55_fbec88_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..47acaadd737478ddb090f47f618810712163317b GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*Fsaj7L$B>N1x91EQ8x$BA993)+ za~~)OO5|O5sDCi_{N8&XlRv*c;OQ6|AR59NN?mFzWBXJVGojypu|S6~c)I$ztaD0e F0syyrGF|`x literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-bg_glass_75_d0e5f5_1x400.png b/app/assets/images/ui-bg_glass_75_d0e5f5_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb564f8d0a117f17aa6b844490309dadbd94821 GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouq?|on978O6-=0_GYj6;7zWBfT zzjhI`OjAO{6(N>+Em!s|xjZW|^1EO|(5d{JeUmv{p6fa-GJh;t>KCH4`R~7(L8qj} Y_egNRQF(If70@^aPgg&ebxsLQ0Qgob)Bpeg literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-bg_glass_85_dfeffc_1x400.png b/app/assets/images/ui-bg_glass_85_dfeffc_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..014951529c315d6042e72febc310a4d2db5b4a82 GIT binary patch literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnouq?|lm978O6-<~(*YA_IRxoBVf zfAX@vsV!R#l$@#*eLnw)_Sv|_?i7P!ORnX)SxaXh+BPpZ!Fw~yjr&#G|Jw^YMHDhV X&EsZx`7bsSXc~j3tDnm{r-UW|&(SK+ literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-bg_glass_95_fef1ec_1x400.png b/app/assets/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..8f28bc99ce22db960f2fd75640c290a6b85d935f GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAGVZ#9R3S1!6p1978O6-(E4~YcSwpIrvpg z@PECw5K|L}sK0`ylX0u`{gab+Zr{pj&{CCfnpJt~xzm!j|E%(eoY1jz7XMN?k5e5l zQ)^bepZW9I5fVt)XuC7JvopV${pbC@&olEr?>nFQTyMtr zt`4e4w2lA(097YPI}ZRrWlMPjVS53Hs9(fjYkM{>RDl)}YR#{PI{UAXZZ)e7~Wr)BPK4TRcVqm-}EA=rOqdBHQ7fG}5`;N!#WGTYp3F`bEb2my*vF(>I zKqcn9+(yT|Zo>xNL6U)j@WJ-m|9JBc{X&|g06KY<5Vn-3g!f3!7zIEeDwx{*>rJf?MGbRV3&=hgpu4$Sz=YF`qNtN`$D^h1QdwMxGr% zZ3amx2KVP-^P=*M9Hjn*h$;!RZn7^TdN8I-D@%_o4G@Cv=J?bBDXND0bn~jt$r97v z`wte$jnvS&pZ6PMetmn99+6T9P7(Oj-P$m%4B#~atw`D|}>FjiMd#aasA=AiC!kx=f!;*(7XLHJ;FfclH-IIS2+{z=mLvYTEdt#Y}|;8MFIF zHGfd?g;afd-z(1Bl5m@6k`^rcueYCndy(aRcp#_C+6}fQTXhe`zQ)K`HhX)OaU9xCZ_0{kd zB3o7D{o6=8lfJK*$+0~T+UBP6<0EMGw``EV;9(wBBe^{RlHOt$hMu!u4W7%_MCLo9s-?$$rb)w; zDo_c$xHPv1A-TWmTka<+F!#-PR(N!bZqy5-kymvzt+}*y(v|n7^ZikoLW-T=oswho zY0G;K`#%Tk23+#XV@=VfkYQ&_SaQLOvYw(8OkM!2&4xv}0<*9|t515=TqrAX^Y^8X zhQ=u666u7SkBaJkr!OsKTT^f$0pe-6B?01p*;z(P3vGEi2RoOfK(5EIvkEQyS5vr) z)`6aVPW*sg$c?E?)_mb&;sJOiYsi6k)R}5QaBM{Yt#g?lD}HfVNJ4yN7eXTX57kzY zA&dN6R3?GaQ~5Bv7jEaC%z4i6@sfp^02e2;SQ=;g?9E(ZSZBTSh3rC**wVV2>$@Wc zmCO|s-InBMs}XWmuUZoW2#Ox9%r*Vtrv6%EPC|p5E}>k6+!^UXUvB>YExTrrIP+d0 z@zP{o$yU`2ae$H7ty|oFUm!vNi_Gr`sQ+Mq=H+d4%qVIkI>8)(1%RmZr zFBTjIZk7Ah`yYc2h^?-N^xFi;(uzm&Fc&-11QBVFN zlDzAlF}Xa!IaN;%tl;Y4bCxxq{2D>+x>Q#S+6xL1Lgxy`er;oR)@h6#1*OO=+^Cxk z<}cRUBMX-&8L>yfue%wld&E%zj}Cd41RtLZqr9XT3KN`_PO_`l7JO}*!Hl$rN)MkR zN^stHb6!J*uZ$FXY3yFM*ZT7z`9i`woFRodIsd4LcfJBWamv*MFk=&V4eJFyvPPlb zxEKy|pGcIS5HK2_xH)`uy0?`;K6fgpl0=`_k7hRJi$_-QuUm0dB!ONw*G5D29#ibZ1R? zsGL((=KR|&B3^!dV4`0avoJ7@qiR1DQ~hin`rb-{UwM)g4=xpjG&1RIt84O6;;y;4 zn~?#9?S)IZJ~|vL0HFK<<4Jpzj?)dFa{-yIm!NMZ?8V1Rzc&tN+Q;Pm;sNY&B58(|A}8 zI!;7h)hD5l#{)^z4=&rzKEqOa9pcLIG?_P!tl4}GGSTL3gW%WP$$3l|hW8)|{!1T{jBfHF3gp50 z!s>p`h;Ph?T9tNEIlfUz{r1BO{N%ls(-ojZW%Js#_@VbhJ@_;A1m>0#A1P~u*Q-C0 zZYKFdKl|n0&G*3oAM~=jK7RDUQ1J)#m*z1}FudlR-%M;0rO3v@KZ}%=TIiqx$eRMLP8buA!H{z0{I$a=Y_&JgXnwdW9(26fjVHP#uYm>|0(Tqv_zQk*@iV*s6box`l# zsWn(Z%0l9D(<{@$D;EDKM1Q*Z%!v=>^3OIj93?rVrTpxqnPFH2+KVgU96SxOor-p5 z1z(S_ehrVo8*jCkX|k6d-eY6g(>1=qHn-avlCyf8z~O00j7qTmY>j#WO?=)`{xv^2AxjfI6 zQtwjz+u;O*wyv^NHzftX*P*ZQU-Z zJ!I~SvPUm)V~iTy*cD{R1uKr?VG(j4SL?)9bGz(3bbknGhpOD*>^`F-7tK$IOhv#Q z5IPW%I(RyG^9}D%Wj7Ffdq?(WDxbZ9a%cUT_;39?olYP2-@q^TiA&OMX&RT01)BWm zm6fr?+1NG3VChXc^I*p6Y17!m;YR9PcbcV%WjQ5c(WbD8xpF6fOEmy?nZjM{*TaoB z_N~rgpNpuc8u1g|1nnTiT6HQtH-lR6_JvH88n4yQy2Jck9DKf_b(RZSFo50p3I{^_9#FH@g zg*dDNvGk3SHk&VTv&!)=AqYe}B&9CWHGltuWdHF8BiQRId=K(;*}1R+}Z$C%HZkh=d#Wzp$Pz8A{XNT literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-icons_217bc0_256x240.png b/app/assets/images/ui-icons_217bc0_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..6f4bd87c041453ef037c4a9a1a698f0105d02eba GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-icons_2e83ff_256x240.png b/app/assets/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..09d1cdc856c292c4ab6dd818c7543ac0828bd616 GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-icons_469bdd_256x240.png b/app/assets/images/ui-icons_469bdd_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..bd2cf079add1ca236adeb509698adabbffb08acb GIT binary patch literal 4369 zcmd^?`8O2)_s3^p#%>tc^56h z`;7ykFJNMJN#e#ybz9|Ft@x`UI}T5QRij?pZ}6v#Srs793k0w~#4dRsO_y8vaKB*UbCk3l9Lh&v zS5!q|FV83GvJ|wlWy2IQI27&mA~vn>kbZHR1lRB?uEUiLWJ2Rgpr(9;PtX|H61Y%8 z>>Yvu=(<$fHnjpCX`E;Qw8u0=3KGsNhap}(`ul7lx-)UB6U7Rt{a^<^*Xbmf7)2^xf*8T2&U<6)1vO~m1F!2^L zin5`}H)*h3_*XzG*7fMOwuHkuK2hW)$!EE#jpyRaiy2tEzf~(B-PTBkPS$@K|y8w%~JYu8>vRGGA=Z$>guC|z6 zYkPw1&xf?FV0;xWt*`eV2oI-ePL2>on#}}WB8O9XBtD6GWYHw9TuY06(#pZ&TR3xK zNc7;n$4wnDC1?2MVtE1Zp2zT~^LboWF^niS1c$xMo}Gq?!`2q?IncFGB{AFxiTH7M zW6Wg6!H-Orl|zm+8G{^~&Fg2IE-7Q;uqGzAXEz)n_H1kYekmQLMJ)H_N1Ou8dug}I zg*SK#Fw;Fagf;H2=cerAvd2^*^YFJ_1850U&t}@Ts z-Ut9ox+Q;6E(XDZh@X=Gp(SPg)l4tQCH^(ZRf@E#KwlZPL;7ULUU0tSrvtn6Xt=Bl zG)w2|kn&t0Rld8d(t&f+-Jt5c7!Jl(SI2y<(E*K?=rQ%uV%4h0>FKm&7~0UnkICBc z3tgbbnW=GN@m656hHUzj6+go+`f^?6f@&?MiRslUz(!JYo`t%GZBP|O5#B?8Q!s!E z9^Ae>??aVeK~d<8G-`&+;~iK=r$D=se~1hP`y1FFARfPyp)iel=Nft8 znC=6UJHKKc>@v6^BHUgm$;1MCFRkRU9c7-T4r93DR+husFU7$gur@@f0$OZ1L9tGX zFTXe+OLbvyc&y1PF}4L`4x@XUJmE|_sn56h!ty42=@$~}wrWyVWoN^*yMa(A8bATs zAQRl8t3PnEeTy?M>ryqZSZwydvk3EmU|_Uk0Qsgqf@$HLqZ+||@PwmP+C~J3t-;t^A+ZQlqV5wK z%GQPfh`B@R4>AFJqdaImV^e(7#NPh2=V`CA9k=gtO&aqe{dJo=cvqPvaG92p)a~Xp z00|*>BOjuss)}zZTg6iEpZ?)}$XnxQ1Qg_)cP)Z6UQ6-ntKI-zNkl5kLs$#d)vS?t#w z!8oVgTG*33YBWB19B(GJxaF`p4zLTN+P(%31kt_<`l{r>rZ!6_mdb zQ2G)orW{~?O-?TSj+obv!+*!zpy&O)wRPJ8Pk81{)Oy2}-GFV2upGunf@d9Zj*xDj z7qF*O&^J3$XB&xT{P@0?J=lOEoWxAgO<1qa2@7S(ulwn5`u0ZIhxiRM`xz@Lwi5}} zFmUKSu+FHdbWSZRbH=Njjqlg3bI?_^<)xC@N6|xn{jq-rBH;45p?jA-NO#)90~=We z`1WnuC0t?^F?mXMxB<>OFqVHH<;)^|gPGvusmW>aZ#v=NEbmy8<+L~aEq zb?!#AginWl{)d^|4v}nB`B(4jVKZ7Iy1CIhSv^hQOhf!s#z}J5u3$Wazo9+lhXzoV zU?V3N$vi_HH+tN(o4dYLvo%axH{x=B;;WvxFYfHT^zTRZS-)ilGp4vP-#pjR+3 z0%AL(^7El8`jyby7DPOXkyc9c@x89GcL(I`x;OT9C2(7J_wbGq>f4s{1-f8d15uu8 z8f6E6ysykf?j%`qVZfG_d47Alp4Qq)&Ed7VJi!ZzB~Xpz+p&9z!3a}h*ZhBHMI8ME z`sT7cRIrw++gd-2I&ZoXq5sH{RaSX(4>Xgl28_+db^7dda<7Wp{^21-MnKeV;U}j1 zJlbMKy?iK~xdXZZeWGbO-RdG-&TvR$TLq8$SdU1N2V4uxE|G#`^e#F>j_3sou4UZn z{C$_N4Ze9WA?dkJU0fKh9qCKOiFvSv``rOim|N#5oQb^^FtmwEeS9tP@DabN`@-&g zimf*(7!$`vRmhu|WqK+rjfNHtN5|W0pW_z?HkS*h88fw>@(*n6h;?a81CT{n{I7>- zw)`=8;Bv=1(tJ@D7qPxosVY+7!w>N=h7e~49~ZKrd98AX6llP7)?3wvc|(^&|FRC# zm9&_;h5z)KIJl{%c3uuW{QBtIlSS~S52Hh?4HeeoZjq-G;6Cq;^mUA?2&V}!)H5jT zKrwiWx-cfD+5-NhGnt}u5wMMwtfXC-yRp|6MTzZFAQItktp4`(v7X4^_2{~i;(sv8 zGkpL3!V-Ai-ycXut#0|8oe4TJ7QUV~Do&p{zVG3v90J>;eENX2w? z$`}Ppr0ft|Zp)w~g{!onDe?@5CcjhC($cq8IM%2O?{Sub8>170^%I69aO+A8&Z&BD zgG+l-HBZPNSO59Ce~-or33^w(Q*U1mHc-Y7c>~Y9et7S1V$SEVbmSSq9Wv|A@EF?V zoP27TfvhVv%A0&@V8B4UGLGc+dc9a4FJBD)l_bZ##HH_vnc z5uC}#FmQiORque`?w?#K6-*)a9uAKX-OqHY?AUdoQYTafr%B>#SB>Q67K{M@<(#;PhLl`o?5`vwPv z;YkLv3FfS>7&%-e=_!*VvjMU8a!T+$b_h1o9(Qs@^ircOb^M0YY-y!n>Di)^q4Cgj z5IOL{sLD(nyg859i=2xJ;iPM|R!#N0a|vH zI}K@UZv9M*&=i}!VrxAmUNEWCy|T3%5~+mC9{NYcI*9J?VqXjh+Egl5Pm-Gb*!~SO zzW+D8H$3YhoTXOmc=gtYw!k@=oeiMmKJaz8r)%e;z1ORe$@QRI4oCa8Imz(dcoLo8 z^y{}ols#&09(EWKFND_xL z&4gxpi)Mk9t&j{}^_frnHu6jB_}_d{Fugq2t)_RvnL%6WY5;D&m?%xbpLEisZuPhT|(X^A|G5mlj0d)w-`54(J%ZTcC-Ajq!3AfU8Dx90^_ zp3}MKjJzYC+`T(&egFXQ#9Ek{*oVAaa!zrZtmlRFnwQPRJXH<%pkK2*eP`pT=lwD7 zifq+4BY_rUTa+U|2#&?i7>PVvD?7R4ZfOLPT{e9G~G!Ls3s8JtQE`jMM9wl2V9&Q+K2DHW0M+uQmEr%nYJ^7cK?uIpU-)=wn71ZZ-=@ar0;3^AY z5+TI{2b(e%t{2PZ^HKF*vu@+Xr&BAc@2BC4 z_vCgww#i=)ea5Vo$glEEVBBg_VPBj!)OO>)f@}#dg6ULOeC>LBHz<;*5Y;YfE0lNx zg{N+4@lO~ozxpF69qV@VOGnc248Iuag4C1T)P^(hWkpP!{h!JekX}m^Q#b2B4f1oT zIjsGz)4}-$rQ*-tSuc%qG>%<4xM#E& zN)7lRK~^2VdiloY4>;#}A!yHOAXEmEi^+eA#05pawGXs>!z)gSoDuI#>bRCq-qjJe zZ)r=A`*EMX6+)~er1kdv1L^)0-PsAEM7JF$O6G8>496$24lkOSR^RTfUuIz%iSfn5b-t!##cs7sQI);gdAvqmn_v|%I9k;fCPl0Z)R1+hNQONJN zH%3jT9sOq*a`LF*MiY=zlSSQZ;{_FL9M07A=In+O!~wR}=bzGEQpk2!Vc0p)qKAH? zOk{(%06W#)DdICQ_S%Q@<0Y+!?9%#$gWJ%)EO->^YZP{<`oB4~9xh zL9-0*c4@B#O2ylYs_g`Ky$zb~v!M`NRaMNFYF*Gsu|7)=JyyMHjFC=HhGUE@{aI|B zJ~ITXU052%7jFb5Ys#fhS_?4kqc7H0EU49B8(Chg0&JzU=Gka#xOz1)H0d4m7ZnRA z=M^tdY|U6T!fmte{W?_r8H~qdq|q{5AMU_2It1I4143n~xL?4&K#BOB48l9_Rdm!(c^C?JU;tF0 zEh@o1y6Qa_>}#AwX{VY+`C^kNkxhgb1P5cB0%xupAXyg9NO=SnXrJUE?rQg{Lcsn+ zAZKctGLfbK_B#^&Nev|0^fB&?DN=ak8|0!np524LD25=s84BP8Vl(3=jflNp{X>e@ z637Ri5xx;&JNl+XYImA|{;XR~P*svYDEWYJ6I5!6uO~2twFC1ZQevB7#3z~(apxn& z^J@>Mc`>PJair{yT`iuan-V+i%|Ho-pA<1?V-k^R2Q<5;Co%XxmL` z018t4T0TTwO^w)Gx{9OSJ^9_|kgwX`7%0Rw!PO~@?xvnfUehvN;2Rc;^l>3kfbtk3 z8{j7p;S&{uTlTe9&HTc38q@%_KQFk<&n{vmrN7y&Cz{etcE->rq!6HL)2F!aa=0%! zM%Bwo!7TQ5t;@a_#Q}sjk{UebWQZ8{cp&HN^$*JfH#8spkhk{R@CVBiPuP@yEhu{} zsQfuhTqV%rioATpEphMfhyRYbVfVW`YwLFXUWm-===J(byMf!5;W^CV1g~2194Xx) zFK|z{pm%n-)-DRe{Qhk(d!QaoI*y%Wn6h7<6A{i*Sob&B^y|Spg!&J$`kN>zwUJ3x zaB$ciu*0FJKg}T ztgnh)ASF8njz5>h6?f#{c=*Yr4W_34$GmVIo8OLWjcZK4a0`+Yv-!*}9 zBwKm;DAsA(nDI-`iH@;`=gP+m{lgFLHK3m$W@?)&dGhDA_Z2xOzI0$p(ZJtH$vCxE zj>+kYNBJzs-TlSx!tSH}%I9fQv)mc!C7X0bKlZv4f&}C3+O-4k7AmVO|KYZ9ydP%(N1^uisV8y;~p`x4qFXD?!_OyN9=w(Od6W; zGrT?G;l2v@Ob5k^8w<9w%Jbjb^|H}PYKo}I~bobd!XrTbzp2Zp~H8lgJ)I3?l&(bDiWf8gE&6b z>)9GB=Iu-6%I((+>=jGP>CzD8c0oWITFZGgM!Q7|JrUYq4#^Y(vuDu-a>OWDa4Y4} z5a_*lW#IL_aVf8L+Ty}c&2VojLEIA-;eQK6Wo?xAuK>i;1VWx3c=!s2;j_*iRHOsb*>6-CgcYP+Ho=L@XLd*j~2ln-;WHg)|cCixksH$K={5rGSD@yB%LI|(NCc8 z1Er8H+QO)~S~K{g?nH|2dB8SKs)BxQ?%G}}o*LV!NG2m*TmR|pWj~g`>)ClJCE#F$ zcj)fBg(dKOKmc$Cy}IRlasngIR>z~kP&WW~9cC951{AKmnZ~ZMsqup6QQf7J0T1;C zK9*Qd5*(HxW=tl|RfjO>nkoW#AU3t>JkuzWxy4-l?xmTv15_r1X@p@dz^{&j&;{Mq z$^0$0q&y?kbdZh)kZ+NfXfqLTG}Q^j>qHlUH4VEK`3y^-z6Y<6O88Hf4v^;}!{t-a zDWg;znYu%6zA1~A5~w?fxO~i8-Ib(^02{c4pXjhDI^2 zXB1LP4dvWuc%PXQ{r!d#6>${rm+M8EJM8yf#!H$Kp8AxwUXm5`7Tu-J$mHeCG>vw|&Ay415}_1w&*9K8+2d3v1N+@a$|820o4u60Tj@u&kI!~q2V9X; z>tMvQDI|O$#m+m2O**ZHq`_{#8)ry6`&5s~2k{O4Du16Fn0P;&_(0!e5%Bel){nU0 zJX~<8U6hoI%yx}qGY_1Tq7YKDJ)ETOCs&W)TiCrK*1%DE*vXdD-7hwE*LUgjeHRM` z&@pkhTi>m#Kc+QIK+2Ybn9-sFVKNHyIgfob4H_77yYh))Rq$7Pw|+aD6&yZ|ki9 z8Zb6s{oBt1G+PgfIcxd}{m@~1nzhe;LH)5;!gS8@ddyabpdBc?7JVl?tS+<#bPSMT z2@0uYdsWN(;Ww)n-PlA-0r+62@bYkEa`k{0s})fJgYZ#5=DmIdEvok7aZJRi{w-|} zkea&6X}ZA3b7&vbDb7)v8CuI(+zzSf3z&P2eOrPNP?D~ zf zn0@)0h;~5F&BG5vOFU!=woW&ZSl~nrs{?1w>nWfW_dnpTd z4qvLDYJ*ft>Sp%M(^_xCZpNBnc66JX}A|ZL9IENM`U>`ph7d<+RQiI}@E8Y)70s zMC*_&))}GlmR}@{v9*nm)29-=rn`Q$rc^4G)GVQHlTr6BpGxtHuU(8AF7Ffh54?5w zj+EYT9>x)PWL-iQ@RNmT?R+|c@=FOmj)5Za6_ z@DkVy4l^L>Z3#SI@s_eVwd3D)<^Ivq8a~J{|4mhOL^<7M4D8){ut;GIqqn`oqCk|x pNh;Wa$C0(mdpqYz&F>xK-uVD=DT5%Jzh8ZT#aXmjr70%*{{Z4(c-8;_ literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-icons_cd0a0a_256x240.png b/app/assets/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..2ab019b73ec11a485fa09378f3a0e155194f6a5d GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/ui-icons_f9bd01_256x240.png b/app/assets/images/ui-icons_f9bd01_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..c7c53cb11904843e176e4ce1f4e7247cd90b3590 GIT binary patch literal 5355 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 0) r++; + } + + $('#' + divId).text(r); +} \ No newline at end of file diff --git a/app/assets/javascripts/bootstrap.js.coffee b/app/assets/javascripts/bootstrap.js.coffee new file mode 100644 index 00000000..c9404a8e --- /dev/null +++ b/app/assets/javascripts/bootstrap.js.coffee @@ -0,0 +1,4 @@ +jQuery -> + $("a[rel=popover]").popover() + $(".tooltip").tooltip() + $("a[rel=tooltip]").tooltip() \ No newline at end of file diff --git a/app/assets/javascripts/home.js.coffee b/app/assets/javascripts/home.js.coffee new file mode 100644 index 00000000..76156794 --- /dev/null +++ b/app/assets/javascripts/home.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/stylesheets/admin/admin.css.scss b/app/assets/stylesheets/admin/admin.css.scss new file mode 100644 index 00000000..914a004c --- /dev/null +++ b/app/assets/stylesheets/admin/admin.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admin/admin controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/admin/call_for_papers.css.scss b/app/assets/stylesheets/admin/call_for_papers.css.scss new file mode 100644 index 00000000..80170c0a --- /dev/null +++ b/app/assets/stylesheets/admin/call_for_papers.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admin/call_for_papers controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/admin/conference.css.scss b/app/assets/stylesheets/admin/conference.css.scss new file mode 100644 index 00000000..82491508 --- /dev/null +++ b/app/assets/stylesheets/admin/conference.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admin/conference controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/admin/people.css.scss b/app/assets/stylesheets/admin/people.css.scss new file mode 100644 index 00000000..de8637ff --- /dev/null +++ b/app/assets/stylesheets/admin/people.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admin/people controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/admin/users.css.scss b/app/assets/stylesheets/admin/users.css.scss new file mode 100644 index 00000000..925e3122 --- /dev/null +++ b/app/assets/stylesheets/admin/users.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admin/users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/admin/venue.css.scss b/app/assets/stylesheets/admin/venue.css.scss new file mode 100644 index 00000000..6a29a24d --- /dev/null +++ b/app/assets/stylesheets/admin/venue.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admin/venue controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 00000000..95b82e02 --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,7 @@ +/* + *= require formtastic-bootstrap + *= require bootstrap_and_overrides + *= require osem + *= require jquery-ui-1.9.2.custom + *= require jquery.fileupload-ui +*/ diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css b/app/assets/stylesheets/bootstrap_and_overrides.css new file mode 100644 index 00000000..131fcfd7 --- /dev/null +++ b/app/assets/stylesheets/bootstrap_and_overrides.css @@ -0,0 +1,7 @@ +/* + =require twitter-bootstrap-static/bootstrap + + Use Font Awesome icons (default) + To use Glyphicons sprites instead of Font Awesome, replace with "require twitter-bootstrap-static/sprites" + =require twitter-bootstrap-static/fontawesome + */ \ No newline at end of file diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less new file mode 100644 index 00000000..67299b01 --- /dev/null +++ b/app/assets/stylesheets/bootstrap_and_overrides.css.less @@ -0,0 +1,33 @@ +@import "twitter/bootstrap/bootstrap"; +@import "twitter/bootstrap/responsive"; + +// Set the correct sprite paths +@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings"); +@iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white"); + +// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines) +// Note: If you use asset_path() here, your compiled bootstrap_and_overrides.css will not +// have the proper paths. So for now we use the absolute path. +@fontAwesomeEotPath: asset-path("fontawesome-webfont.eot"); +@fontAwesomeEotPath_iefix: asset-path("fontawesome-webfont.eot#iefix"); +@fontAwesomeWoffPath: asset-path("fontawesome-webfont.woff"); +@fontAwesomeTtfPath: asset-path("fontawesome-webfont.ttf"); +@fontAwesomeSvgPath: asset-path("fontawesome-webfont.svg"); + +// Font Awesome +@import "fontawesome"; + +// Glyphicons +//@import "twitter/bootstrap/sprites.less"; + +// Your custom LESS stylesheets goes here +// +// Since bootstrap was imported above you have access to its mixins which +// you may use and inherit here +// +// If you'd like to override bootstrap's own variables, you can do so here as well +// See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation +// +// Example: +// @linkColor: #ff0000; +@navbarBackground: #000000; \ No newline at end of file diff --git a/app/assets/stylesheets/home.css.scss b/app/assets/stylesheets/home.css.scss new file mode 100644 index 00000000..f0ddc684 --- /dev/null +++ b/app/assets/stylesheets/home.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the home controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/jquery-ui-1.9.2.custom.css b/app/assets/stylesheets/jquery-ui-1.9.2.custom.css new file mode 100644 index 00000000..0234adb1 --- /dev/null +++ b/app/assets/stylesheets/jquery-ui-1.9.2.custom.css @@ -0,0 +1,325 @@ +/*! jQuery UI - v1.9.2 - 2012-12-28 +* http://jqueryui.com +* Includes: jquery.ui.core.css, jquery.ui.datepicker.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande%2CLucida%20Sans%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=5c9ccc&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=55&borderColorHeader=4297d7&fcHeader=ffffff&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=06_inset_hard.png&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=469bdd&bgColorDefault=dfeffc&bgTextureDefault=02_glass.png&bgImgOpacityDefault=85&borderColorDefault=c5dbec&fcDefault=2e6e9e&iconColorDefault=6da8d5&bgColorHover=d0e5f5&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=79b7e7&fcHover=1d5987&iconColorHover=217bc0&bgColorActive=f5f8f9&bgTextureActive=06_inset_hard.png&bgImgOpacityActive=100&borderColorActive=79b7e7&fcActive=e17009&iconColorActive=f9bd01&bgColorHighlight=fbec88&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=fad42e&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +* Copyright (c) 2012 jQuery Foundation and other contributors Licensed MIT */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; } +.ui-helper-clearfix:after { clear: both; } +.ui-helper-clearfix { zoom: 1; } +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } +.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +} +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Lucida Grande,Lucida Sans,Arial,sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Lucida Grande,Lucida Sans,Arial,sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #a6c9e2; background: #fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x; color: #222222; } +.ui-widget-content a { color: #222222; } +.ui-widget-header { border: 1px solid #4297d7; background: #5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } +.ui-widget-header a { color: #ffffff; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #c5dbec; background: #dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #2e6e9e; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #2e6e9e; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #79b7e7; background: #d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1d5987; } +.ui-state-hover a, .ui-state-hover a:hover, .ui-state-hover a:link, .ui-state-hover a:visited { color: #1d5987; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #79b7e7; background: #f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #e17009; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #e17009; text-decoration: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fad42e; background: #fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } +.ui-state-disabled .ui-icon { filter:Alpha(Opacity=35); } /* For IE8 - See #6059 */ + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_469bdd_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_469bdd_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_d8e7f3_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_6da8d5_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_217bc0_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_f9bd01_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -khtml-border-top-left-radius: 5px; border-top-left-radius: 5px; } +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; -khtml-border-top-right-radius: 5px; border-top-right-radius: 5px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; -khtml-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; -khtml-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; } + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .3;filter:Alpha(Opacity=30); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .3;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; } \ No newline at end of file diff --git a/app/assets/stylesheets/osem.css b/app/assets/stylesheets/osem.css new file mode 100644 index 00000000..56bb4479 --- /dev/null +++ b/app/assets/stylesheets/osem.css @@ -0,0 +1,102 @@ +html, body,.container,.content { + height: 100%; +} + +#push, +#footer { + height: 60px; +} + +.container, .content { + position: relative; +} +#inner-content { + padding-top: 60px; +} + +#wrap { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -60px; +} + +#footer { + position: relative; + background-color: #ffffff; +} +.navbar-inner { + background: #299a0b; /* Old browsers */ + background: -moz-linear-gradient(top, #299a0b 0%, #299a0b 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#299a0b), color-stop(100%,#299a0b)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #299a0b 0%,#299a0b 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #299a0b 0%,#299a0b 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #299a0b 0%,#299a0b 100%); /* IE10+ */ + background: linear-gradient(to bottom, #299a0b 0%,#299a0b 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#299a0b', endColorstr='#299a0b',GradientType=0 ); /* IE6-9 */ +} + +.navbar .nav .active > a, .navbar .nav .active > a:hover { + background-color: #DFE0DF; +} + +.navbar .divider-vertical { + background-color: #b4e391; + border-right: 1px solid #eab92d; +} + +.navbar-fixed-top .brand { + color: #ffffff; + text-shadow:none; +} + +.navbar .nav > li > a { + float: none; + padding: 10px 15px 10px; + color: white; + text-decoration: none; + text-shadow: none; +} + +.center { + margin:0 auto; + float:none; +} + +.center-text { + text-align: center +} + +textarea { + -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */ + -moz-box-sizing: border-box; /* Firefox, other Gecko */ + box-sizing: border-box; /* Opera/IE 8+ */ + width:100%; +} + +.mainbutton { + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #9dce2c), color-stop(1, #8cb82b) ); + background:-moz-linear-gradient( center top, #9dce2c 5%, #8cb82b 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9dce2c', endColorstr='#8cb82b'); + background-color:#9dce2c; + -moz-border-radius:6px; + -webkit-border-radius:6px; + border-radius:6px; + border:1px solid #83c41a; + display:inline-block; + color:#ffffff; + font-family:Arial; + font-size:20px; + font-weight:bold; + padding:6px 24px; + text-decoration:none; + text-shadow:1px 1px 9px #689324; +}.mainbutton:hover { + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #8cb82b), color-stop(1, #9dce2c) ); + background:-moz-linear-gradient( center top, #8cb82b 5%, #9dce2c 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#8cb82b', endColorstr='#9dce2c'); + background-color:#8cb82b; + }.mainbutton:active { + position:relative; + top:1px; + } diff --git a/app/controllers/admin/callforpapers_controller.rb b/app/controllers/admin/callforpapers_controller.rb new file mode 100644 index 00000000..c4534af7 --- /dev/null +++ b/app/controllers/admin/callforpapers_controller.rb @@ -0,0 +1,28 @@ +class Admin::CallforpapersController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def show + @cfp = @conference.call_for_papers + if @cfp.nil? + @cfp = CallForPapers.new + end + end + + def update + @cfp = @conference.call_for_papers + @cfp.update_attributes(params[:call_for_papers]) + redirect_to(admin_conference_cfp_info_path(:id => @conference.short_title), :notice => 'Call for Papers was successfully updated.') + + end + + def create + @cfp = CallForPapers.new(params[:call_for_papers]) + @conference.call_for_papers = @cfp + if @cfp.save + redirect_to(admin_conference_cfp_info_path(:id => @conference.short_title), :notice => 'Call for Papers was successfully updated.') + else + redirect_to(admin_conference_cfp_info_path(:id => @conference.short_title), :error => 'Call for Papers failed.') + end + end +end diff --git a/app/controllers/admin/conference_controller.rb b/app/controllers/admin/conference_controller.rb new file mode 100644 index 00000000..763e9b7d --- /dev/null +++ b/app/controllers/admin/conference_controller.rb @@ -0,0 +1,36 @@ +class Admin::ConferenceController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def index + @conferences = Conference.all + if @conferences.count == 0 + redirect_to new_admin_conference_path + return + end + end + + def new + @conference = Conference.new + end + + def create + @conference = Conference.new(params[:conference]) + if @conference.save + redirect_to(admin_conference_path(:id => @conference.short_title), :notice => 'Conference was successfully created.') + else + render :action => "new" + end + end + + def update + @conference = Conference.find_all_by_short_title(params[:id]).first + @conference.update_attributes(params[:conference]) + flash[:notice] = "Updated Conference" + redirect_to(admin_conference_path(:id => @conference.short_title), :notice => 'Conference was successfully updated.') + end + + def show + @conference = Conference.find_all_by_short_title(params[:id]).first + end +end diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb new file mode 100644 index 00000000..764b752a --- /dev/null +++ b/app/controllers/admin/events_controller.rb @@ -0,0 +1,41 @@ +class Admin::EventsController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def index + @events = @conference.events + end + + def show + @event = @conference.events.find(params[:id]) + @comments = @event.root_comments + @comment_count = @event.comment_threads.count + end + + def edit + @event_types = @conference.event_types + @event = Event.find_by_id(params[:id]) + @person = @event.submitter + @url = edit_admin_conference_event_path(@conference.short_title, @event) + end + + def comment + event = @conference.events.find_by_id(params[:id]) + comment = Comment.build_from(event, current_user.id, params[:comment]) + comment.save! + if !params[:parent].nil? + comment.move_to_child_of(params[:parent]) + end + + redirect_to admin_conference_event_path(:conference_id => @conference.short_title, :id => params[:id]) + end + def create + + end + + def update_state + event = Event.find(params[:id]) + event.send(:"#{params[:transition]}!", :send_mail => params[:send_mail]) + redirect_to(admin_conference_events_path(:conference_id => @conference.short_title), :notice => "Updated state") + end +end diff --git a/app/controllers/admin/eventtype_controller.rb b/app/controllers/admin/eventtype_controller.rb new file mode 100644 index 00000000..64aa3939 --- /dev/null +++ b/app/controllers/admin/eventtype_controller.rb @@ -0,0 +1,17 @@ +class Admin::EventtypeController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def show + render :event_type_list + end + + def update + begin + @conference.update_attributes!(params[:conference]) + redirect_to(admin_conference_eventtype_list_path(:conference_id => @conference.short_title), :notice => 'Event types were successfully updated.') + rescue Exception => e + redirect_to(admin_conference_eventtype_list_path(:conference_id => @conference.short_title), :alert => 'Event types update failed: #{e.message}') + end + end +end \ No newline at end of file diff --git a/app/controllers/admin/people_controller.rb b/app/controllers/admin/people_controller.rb new file mode 100644 index 00000000..d31f4c2d --- /dev/null +++ b/app/controllers/admin/people_controller.rb @@ -0,0 +1,24 @@ +class Admin::PeopleController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def index + @people = Person.all + end + + def create + + end + + def show + @person = Person.find(params[:id]) + end + + def update + + end + + def delete + + end +end diff --git a/app/controllers/admin/registrations_controller.rb b/app/controllers/admin/registrations_controller.rb new file mode 100644 index 00000000..ee92e8c1 --- /dev/null +++ b/app/controllers/admin/registrations_controller.rb @@ -0,0 +1,16 @@ +class Admin::RegistrationsController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def show + session[:return_to] ||= request.referer + @pdf_filename = "#{@conference.title}.pdf" + @registrations = @conference.registrations.all(:joins => :person, + :order => "people.last_name ASC", + :select => "registrations.*, + people.last_name AS last_name, + people.first_name AS first_name, + people.public_name AS public_name, + people.email AS email") + end +end \ No newline at end of file diff --git a/app/controllers/admin/rooms_controller.rb b/app/controllers/admin/rooms_controller.rb new file mode 100644 index 00000000..b736b22f --- /dev/null +++ b/app/controllers/admin/rooms_controller.rb @@ -0,0 +1,18 @@ +class Admin::RoomsController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def show + render :rooms_list + end + + def update + if @conference.update_attributes(params[:conference]) + redirect_to(admin_conference_rooms_list_path(:conference_id => @conference.short_title), :notice => 'Rooms were successfully updated.') + else + redirect_to(admin_conference_rooms_list_path(:conference_id => @conference.short_title), :notice => 'Room update failed.') + end + + end + +end diff --git a/app/controllers/admin/tracks_controller.rb b/app/controllers/admin/tracks_controller.rb new file mode 100644 index 00000000..88798893 --- /dev/null +++ b/app/controllers/admin/tracks_controller.rb @@ -0,0 +1,18 @@ +class Admin::TracksController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def show + render :tracks_list + end + + def update + if @conference.update_attributes(params[:conference]) + redirect_to(admin_conference_tracks_list_path(:conference_id => @conference.short_title), :notice => 'Tracks were successfully updated.') + else + redirect_to(admin_conference_tracks_list_path(:conference_id => @conference.short_title), :notice => 'Tracks update failed.') + end + + end + +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb new file mode 100644 index 00000000..35ab58d4 --- /dev/null +++ b/app/controllers/admin/users_controller.rb @@ -0,0 +1,20 @@ +class Admin::UsersController < ApplicationController + before_filter :verify_admin + layout "admin" + + def index + @users = User.all(:joins => :person, + :order => "people.last_name ASC", + :select => "users.*, + people.last_name AS last_name, + people.first_name AS first_name, + people.public_name AS public_name, + people.email AS email") + end + + def update + user = User.find(params[:id]) + user.update_attributes!(params[:user]) + redirect_to admin_users_path, :notice => "Updated #{user.email}" + end +end diff --git a/app/controllers/admin/venue_controller.rb b/app/controllers/admin/venue_controller.rb new file mode 100644 index 00000000..d8ad1c5d --- /dev/null +++ b/app/controllers/admin/venue_controller.rb @@ -0,0 +1,24 @@ +class Admin::VenueController < ApplicationController + before_filter :verify_organizer + layout "admin" + + def index + end + + def update + @venue = @conference.venue + venue_params = params[:venue] + @venue.name = venue_params[:name] + @venue.address= venue_params[:address] + @venue.website = venue_params[:website] + @venue.description = venue_params[:description] + @venue.save + redirect_to(admin_conference_venue_info_path(:conference_id => @conference.short_title), :notice => 'Venue was successfully updated.') + end + + def show + @venue = @conference.venue + render :venue_info + end + +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 00000000..d45387fa --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,42 @@ +class ApplicationController < ActionController::Base + include ApplicationHelper + + protect_from_forgery + + def verify_user + :authenticate_user! + + if (current_user.nil?) + redirect_to '/accounts/sign_in' + return + end + + @conference = Conference.find_all_by_short_title(params[:conference_id]).first + end + + def organizer_or_admin? + has_role?(current_user, 'admin') || has_role?(current_user, 'organizer') + end + + def verify_organizer + verify_user + + ## Todo simplify this + redirect_to '/' unless has_role?(current_user, 'admin') || has_role?(current_user, 'organizer') + end + + def verify_admin + verify_user + + redirect_to '/' unless has_role?(current_user, 'admin') + end + + def current_ability + @current_ability ||= AdminAbility.new(current_user) + end + + rescue_from CanCan::AccessDenied do |exception| + Rails.logger.debug("Access denied!") + redirect_to '/', :alert => exception.message + end +end diff --git a/app/controllers/conference_registration_controller.rb b/app/controllers/conference_registration_controller.rb new file mode 100644 index 00000000..50b8d3d5 --- /dev/null +++ b/app/controllers/conference_registration_controller.rb @@ -0,0 +1,51 @@ +class ConferenceRegistrationController < ApplicationController + before_filter :verify_user + + def register + # TODO Figure out how to change the route's id from :id to :conference_id + @conference = Conference.find_all_by_short_title(params[:id]).first + @person = current_user.person + @registration = @person.registrations.where(:conference_id => @conference.id).first + @registered = true + if @registration.nil? + @registered = false + @registration = @person.registrations.new(:conference_id => @conference.id) + end + end + + def update + conference = Conference.find_all_by_short_title(params[:id]).first + person = current_user.person + registration = person.registrations.where(:conference_id => conference.id).first + update_registration = true + begin + if registration.nil? + update_registration = false + person.update_attributes(params[:registration][:person_attributes]) + params[:registration].delete :person_attributes + registration = person.registrations.new(params[:registration]) + registration.conference_id = conference.id + registration.save! + else + registration.update_attributes!(params[:registration]) + end + rescue Exception => e + redirect_to(register_conference_path(:id => conference.short_title), :alert => 'Registration failed:' + e.message) + return + end + + redirect_message = "You are now registered." + if update_registration + redirect_message = "Registration updated." + end + redirect_to(register_conference_path(:id => conference.short_title), :notice => redirect_message) + end + + def unregister + conference = Conference.find_all_by_short_title(params[:id]).first + person = current_user.person + registration = person.registrations.where(:conference_id => conference.id).first + registration.destroy + redirect_to :root + end +end diff --git a/app/controllers/event_attachments_controller.rb b/app/controllers/event_attachments_controller.rb new file mode 100644 index 00000000..95248f0c --- /dev/null +++ b/app/controllers/event_attachments_controller.rb @@ -0,0 +1,97 @@ +class EventAttachmentsController < ApplicationController + before_filter :verify_user + skip_before_filter :verify_user, :only => [:show] + + def index + @proposal = current_user.person.events.find(params[:proposal_id]) + @uploads = @proposal.event_attachments + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @uploads.map{|upload| upload.to_jq_upload } } + end + end + + def show + upload = EventAttachment.find(params[:id]) + if upload.public? + send_file upload.attachment.path + return + end + + if current_user.nil? + verify_user + return + end + + if organizer_or_admin? || current_user.person == upload.event.submitter + send_file upload.attachment.path + else + raise ActionController::RoutingError.new('Not Found') + end + end + + def new + @upload = EventAttachment.new + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @upload } + end + end + + def edit + @upload = EventAttachment.find(params[:id]) + end + + def create + params[:event_attachment][:title] = params[:title][0] + params[:event_attachment][:public] = false + params[:event_attachment][:event_id] = params[:proposal_id] + + if params.has_key?(:public) + params[:event_attachment][:public] = true + end + @upload = EventAttachment.new(params[:event_attachment]) + + respond_to do |format| + if @upload.save + format.html { + render :json => [@upload.to_jq_upload].to_json, + :content_type => 'text/html', + :layout => false + } + format.json { render json: [@upload.to_jq_upload].to_json, status: :created, + location: conference_proposal_event_attachment_path(@upload.event.conference.short_title, @upload.event, @upload) } + else + format.html { render action: "new" } + format.json { render json: @upload.errors, status: :unprocessable_entity } + end + end + end + + def update + @upload = EventAttachment.find(params[:proposal_id]) + + respond_to do |format| + if @upload.update_attributes(params[:upload]) + format.html { redirect_to @upload, notice: 'Upload was successfully updated.' } + format.json { head :no_content } + else + format.html { render action: "edit" } + format.json { render json: @upload.errors, status: :unprocessable_entity } + end + end + end + + def destroy + @proposal = current_user.person.events.find(params[:proposal_id]) + @upload = @proposal.event_attachments.find(params[:id]) + @upload.destroy + + respond_to do |format| + format.html { redirect_to uploads_url } + format.json { head :no_content } + end + end +end \ No newline at end of file diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 00000000..089c8d49 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,7 @@ +class HomeController < ApplicationController + def index + @today = Date.current + @conferences = Conference.where("start_date >= ?", @today) + end + +end diff --git a/app/controllers/proposal_controller.rb b/app/controllers/proposal_controller.rb new file mode 100644 index 00000000..15bb60a4 --- /dev/null +++ b/app/controllers/proposal_controller.rb @@ -0,0 +1,97 @@ +class ProposalController < ApplicationController + before_filter :verify_user + + def index + @person = current_user.person + @events = @person.proposals @conference + end + + def destroy + current_user.person.withdraw_proposal(params[:id]) + redirect_to(conference_proposal_index_path(:conference_id => @conference.short_title), :alert => 'Proposal withdrawn.') + end + + def new + @url = conference_proposal_index_path(@conference.short_title) + @event = Event.new + @event_types = @conference.event_types + @person = current_user.person + end + + def edit + @url = conference_proposal_path(@conference.short_title, params[:id]) + @person = current_user.person + @event_types = @conference.event_types + @event = @person.events.find_by_id(params[:id]) + @attachments = @event.event_attachments + + if @event.nil? + redirect_to(conference_proposal_index_path(:conference_id => @conference.short_title), :alert => 'Invalid proposal.') + end + end + + def update + person = current_user.person + session[:return_to] ||= request.referer + submitter = params[:person] + + params[:event].delete :people_attributes + params[:event].delete :person + + if submitter[:public_name] != person.public_name || submitter[:biography] != person.biography + person.update_attributes(submitter) + end + + event = Event.find_by_id(params[:id]) + + if event.update_attributes!(params[:event]) + redirect_to(conference_proposal_index_path(:conference_id => @conference.short_title), :notice => "'#{event.title}' was successfully updated.") + else + redirect_to session[:return_to], :alert => "'#{event.title}' was NOT updated!" + end + end + + def create + person = current_user.person + session[:return_to] ||= request.referer + + event_params = params[:event] + submitter = params[:person] + params[:event].delete :person + + # First, update the submitter's info, if they've changed anything + if submitter[:public_name] != person.public_name || submitter[:biography] != person.biography + person.update_attributes(submitter) + end + + @event = Event.new(event_params) + @event.conference = @conference + @event.event_people.new(:person => person, + :event_role => "submitter") + @event.event_people.new(:person => person, + :event_role => "speaker") + + begin + @event.save! + rescue Exception => e + @url = conference_proposal_index_path(@conference.short_title) + @event_types = @conference.event_types + @person = current_user.person + + flash[:error] = "Could not submit proposal: #{e.message}" + render :action => 'new' + return + end + + registration = person.registrations.where(:conference_id => @conference.id).first + if registration.nil? + redirect_to(register_conference_path(@conference.short_title), :notice => 'Event was successfully submitted. You probably want to register for the conference now!') + else + redirect_to(conference_proposal_index_path(:conference_id => @conference.short_title), :notice => 'Event was successfully submitted.') + end + end + + def show + + end +end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb new file mode 100644 index 00000000..4d97851a --- /dev/null +++ b/app/controllers/registrations_controller.rb @@ -0,0 +1,52 @@ +class RegistrationsController < Devise::RegistrationsController + def update + @user = User.find(current_user.id) + email_changed = false + + if !params[:user][:email].nil? + if @user.email != params[:user][:email] + email_changed = true + else + params[:user].delete :email + end + end + + password_changed = false + if !params[:user][:password].nil? + if !params[:user][:password].empty? + password_changed = true + else + params[:user].delete :password + params[:user].delete :password_confirmation + params[:user].delete :current_password + end + end + + + successfully_updated = if email_changed or password_changed + @user.update_with_password(params[:user]) + else + @user.update_without_password(params[:user]) + end + + if successfully_updated + set_flash_message :notice, :updated + # Sign in the user bypassing validation in case his password changed + sign_in @user, :bypass => true + redirect_to after_update_path_for(@user) + else + render "edit" + end + end + + protected + + def after_update_path_for(resource) + edit_user_registration_path(resource) + end + + def after_sign_up_path_for(resource) + edit_user_registration_path(resource) + end + +end \ No newline at end of file diff --git a/app/helpers/admin/call_for_papers_helper.rb b/app/helpers/admin/call_for_papers_helper.rb new file mode 100644 index 00000000..6e8ba54b --- /dev/null +++ b/app/helpers/admin/call_for_papers_helper.rb @@ -0,0 +1,2 @@ +module Admin::CallForPapersHelper +end diff --git a/app/helpers/admin/conference_helper.rb b/app/helpers/admin/conference_helper.rb new file mode 100644 index 00000000..c17678c0 --- /dev/null +++ b/app/helpers/admin/conference_helper.rb @@ -0,0 +1,2 @@ +module Admin::ConferenceHelper +end diff --git a/app/helpers/admin/people_helper.rb b/app/helpers/admin/people_helper.rb new file mode 100644 index 00000000..a6e7e7fa --- /dev/null +++ b/app/helpers/admin/people_helper.rb @@ -0,0 +1,2 @@ +module Admin::PeopleHelper +end diff --git a/app/helpers/admin/users_helper.rb b/app/helpers/admin/users_helper.rb new file mode 100644 index 00000000..5995c2aa --- /dev/null +++ b/app/helpers/admin/users_helper.rb @@ -0,0 +1,2 @@ +module Admin::UsersHelper +end diff --git a/app/helpers/admin/venue_helper.rb b/app/helpers/admin/venue_helper.rb new file mode 100644 index 00000000..409948c7 --- /dev/null +++ b/app/helpers/admin/venue_helper.rb @@ -0,0 +1,2 @@ +module Admin::VenueHelper +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 00000000..ed736e54 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,43 @@ +module ApplicationHelper + + def add_association_link(association_name, form_builder, div_class, html_options = {}) + link_to_add_association "Add " + association_name.to_s.singularize, form_builder, div_class, html_options.merge(:class => "assoc btn btn-success") + end + + def remove_association_link(association_name, form_builder) + link_to_remove_association("Remove " + association_name.to_s.singularize, form_builder, :class => "assoc btn btn-danger") + tag(:hr) + end + + def dynamic_association(association_name, title, form_builder, options = {}) + render "shared/dynamic_association", :association_name => association_name, :title => title, :f => form_builder, :hint => options[:hint] + end + + def has_role?(current_user, role) + return !!current_user.role?(role.to_s.camelize) + end + + # TODO Output better html + def format_comments(comment, padding = 0) + result = "" + result += "

" + result += "
" + result += "#{comment.user.person.public_name} #{comment.created_at}

" + result += comment.body + result += "
Reply

" + result += "
" + result += "
" + result += "" + result += "" + result += "" + result += "" + result += "
" + result += "
" + #result += edit_admin_conference_event_path(@conference.short_title, @event) + comment.children.each do |child| + result += format_comments(child, 50) + result += "
" + end + + result + end +end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb new file mode 100644 index 00000000..23de56ac --- /dev/null +++ b/app/helpers/home_helper.rb @@ -0,0 +1,2 @@ +module HomeHelper +end diff --git a/app/mailers/.gitkeep b/app/mailers/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/models/.gitkeep b/app/models/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app/models/ability.rb b/app/models/ability.rb new file mode 100644 index 00000000..58929d7a --- /dev/null +++ b/app/models/ability.rb @@ -0,0 +1,28 @@ +class Ability + include CanCan::Ability + + def initialize(user) + # Define abilities for the passed in user here. For example: + # + # user ||= User.new # guest user (not logged in) + # if user.admin? + # can :manage, :all + # else + # can :read, :all + # end + # + # The first argument to `can` is the action you are giving the user permission to do. + # If you pass :manage it will apply to every action. Other common actions here are + # :read, :create, :update and :destroy. + # + # The second argument is the resource the user can perform the action on. If you pass + # :all it will apply to every resource. Otherwise pass a Ruby class of the resource. + # + # The third argument is an optional hash of conditions to further filter the objects. + # For example, here the user can only update published articles. + # + # can :update, Article, :published => true + # + # See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities + end +end diff --git a/app/models/admin_ability.rb b/app/models/admin_ability.rb new file mode 100644 index 00000000..8aff497a --- /dev/null +++ b/app/models/admin_ability.rb @@ -0,0 +1,17 @@ +class AdminAbility + include CanCan::Ability + + def initialize(user) + @user = user || User.new # for guest + @user.get_roles.each { |role| send(role.name.downcase) } + end + + def organizer + can :manage, Event + end + + def admin + organizer + can :manage, :all + end +end diff --git a/app/models/call_for_papers.rb b/app/models/call_for_papers.rb new file mode 100644 index 00000000..f31a8127 --- /dev/null +++ b/app/models/call_for_papers.rb @@ -0,0 +1,7 @@ +class CallForPapers < ActiveRecord::Base + attr_accessible :start_date, :end_date, :hard_deadline, :description + belongs_to :conference + + validates_presence_of :start_date, :end_date, :hard_deadline + +end \ No newline at end of file diff --git a/app/models/comment.rb b/app/models/comment.rb new file mode 100644 index 00000000..8cccba85 --- /dev/null +++ b/app/models/comment.rb @@ -0,0 +1,48 @@ +class Comment < ActiveRecord::Base + acts_as_nested_set :scope => [:commentable_id, :commentable_type] + attr_accessible :commentable, :body, :user_id + validates_presence_of :body + validates_presence_of :user + + # NOTE: install the acts_as_votable plugin if you + # want user to vote on the quality of comments. + #acts_as_votable + + belongs_to :commentable, :polymorphic => true + + # NOTE: Comments belong to a user + belongs_to :user + + # Helper class method that allows you to build a comment + # by passing a commentable object, a user_id, and comment text + # example in readme + def self.build_from(obj, user_id, comment) + new \ + :commentable => obj, + :body => comment, + :user_id => user_id + end + + #helper method to check if a comment has children + def has_children? + self.children.any? + end + + # Helper class method to lookup all comments assigned + # to all commentable types for a given user. + scope :find_comments_by_user, lambda { |user| + where(:user_id => user.id).order('created_at DESC') + } + + # Helper class method to look up all comments for + # commentable class name and commentable id. + scope :find_comments_for_commentable, lambda { |commentable_str, commentable_id| + where(:commentable_type => commentable_str.to_s, :commentable_id => commentable_id).order('created_at DESC') + } + + # Helper class method to look up a commentable object + # given the commentable class name and id + def self.find_commentable(commentable_str, commentable_id) + commentable_str.constantize.find(commentable_id) + end +end \ No newline at end of file diff --git a/app/models/conference.rb b/app/models/conference.rb new file mode 100644 index 00000000..6216cfda --- /dev/null +++ b/app/models/conference.rb @@ -0,0 +1,87 @@ +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, :cfp_open, :registration_open, + :event_types_attributes + + has_one :call_for_papers, :dependent => :destroy + has_many :events, :dependent => :destroy + has_many :event_types, :dependent => :destroy + has_many :tracks, :dependent => :destroy + has_many :rooms, :dependent => :destroy + has_many :registrations, :dependent => :destroy + + belongs_to :venue + + accepts_nested_attributes_for :rooms, :reject_if => proc {|r| r["name"].blank?}, :allow_destroy => true + accepts_nested_attributes_for :tracks, :reject_if => proc {|r| r["name"].blank?}, :allow_destroy => true + accepts_nested_attributes_for :venue + accepts_nested_attributes_for :event_types, :allow_destroy => true + + validates_presence_of :title, + :short_title, + :social_tag + validates_uniqueness_of :short_title + validates_format_of :short_title, :with => /^[a-zA-Z0-9_-]*$/ + before_create :generate_guid + before_create :create_venue + + + def self.current + self.order("created_at DESC").first + end + + def date_range_string + startstr = "Unknown - " + endstr = "Unknown" + if start_date.month == end_date.month && start_date.year == end_date.year + startstr = start_date.strftime("%B %d - ") + endstr = end_date.strftime("%d, %Y") + elsif start_date.month != end_date.month && start_date.year == end_date.year + startstr = start_date.strftime("%B %d - ") + endstr = end_date.strftime("%B %d, %Y") + else + startstr = start_date.strftime("%B %d, %Y - ") + endstr = end_date.strftime("%B %d, %Y") + end + + result = startstr + endstr + result + end + + def user_registered? user + return nil if user.nil? + return nil if user.person.nil? + + if self.registrations.where(:person_id => user.person.id).count == 0 + Rails.logger.debug("Returning false") + false + else + Rails.logger.debug("Returning true") + true + end + end + + def cfp_open? + today = Date.current + cfp = self.call_for_papers + if !cfp.nil? && (cfp.start_date.. cfp.end_date).cover?(today) + return true + end + + return false + end + private + + def create_venue + self.venue_id = Venue.create.id + true + end + + def generate_guid + begin + guid = SecureRandom.urlsafe_base64 + end while Person.where(:guid => guid).exists? + self.guid = guid + end + +end \ No newline at end of file diff --git a/app/models/event.rb b/app/models/event.rb new file mode 100644 index 00000000..fc7fc5dc --- /dev/null +++ b/app/models/event.rb @@ -0,0 +1,101 @@ +class Event < ActiveRecord::Base + include ActiveRecord::Transitions + + attr_accessible :title, :subtitle, :abstract, :description, :event_type_id, :people_attributes, :person, + :proposal_additional_speakers + acts_as_commentable + + has_many :event_people, :dependent => :destroy + has_many :event_attachments, :dependent => :destroy + has_many :people, :through => :event_people + belongs_to :event_type + + belongs_to :track + belongs_to :room + belongs_to :conference + + accepts_nested_attributes_for :event_people + accepts_nested_attributes_for :event_attachments, :allow_destroy => true, :reject_if => :all_blank + accepts_nested_attributes_for :people + before_create :generate_guid + validate :abstract_limit + + state_machine :initial => :new do + state :new + state :review + state :withdrawn + state :accepted + state :unconfirmed + state :confirmed + state :canceled + state :rejected + + event :start_review do + transitions :to => :review, :from => [:new, :rejected] + end + event :withdraw do + transitions :to => :withdrawn, :from => [:new, :review, :unconfirmed] + end + event :accept do + transitions :to => :unconfirmed, :from => [:new, :review], :on_transition => :process_acceptance + end + event :unconfirm do + transitions :to => :review, :from=>[:confirmed] + end + event :confirm do + transitions :to => :confirmed, :from => :unconfirmed + end + event :cancel do + transitions :to => :canceled, :from => [:unconfirmed, :confirmed] + end + event :reject do + transitions :to => :rejected, :from => [:new, :review], :on_transition => :process_rejection + end + end + + def submitter + result = self.event_people.where(:event_role => "submitter").first + if !result.nil? + result.person + else + nil + end + end + + def transition_possible?(transition) + self.class.state_machine.events_for(self.current_state).include?(transition) + end + + def process_acceptance(options) +# if options[:send_mail] + # self.event_people.presenter.each do |event_person| + # event_person.generate_token! + # SelectionNotification.acceptance_notification(event_person).deliver + # end + # end + + end + + def abstract_word_count + if self.abstract.nil? + 0 + else + self.abstract.split.size + end + end + private + + def abstract_limit + if self.abstract.split.size > 250 + errors.add(:abstract, "cannot have more than 250 words") + end + end + + def generate_guid + begin + guid = SecureRandom.urlsafe_base64 + end while self.class.where(:guid => guid).exists? + self.guid = guid + end + +end \ No newline at end of file diff --git a/app/models/event_attachment.rb b/app/models/event_attachment.rb new file mode 100644 index 00000000..9d33fa41 --- /dev/null +++ b/app/models/event_attachment.rb @@ -0,0 +1,28 @@ +class EventAttachment < ActiveRecord::Base + belongs_to :event + scope :public, where(:public => true) + attr_accessible :public, :attachment, :event_id, :title + + has_attached_file :attachment, :path => ":rails_root/storage/:rails_env/attachments/:id/:style/:basename.:extension" + include Rails.application.routes.url_helpers + + def to_jq_upload + { + "name" => read_attribute(:attachment_file_name), + "size" => read_attribute(:attachment_file_size), + "title" => read_attribute(:title), + "public" => read_attribute(:public), + #"url" => attachment.url(:original), + "url" => conference_proposal_event_attachment_path(self.event.conference.short_title, self.event_id, self.id), + "delete_url" => conference_proposal_event_attachment_path(self.event.conference.short_title, self.event_id, self.id), + "delete_type" => "DELETE" + } + + end + #:path => ":rails_root/public/system/:attachment/:id/:style/:filename", + # :url => "/system/:attachment/:id/:style/:filename" + + #has_paper_trail :meta => {:associated_id => :event_id, :associated_type => "Event"} + + +end diff --git a/app/models/event_person.rb b/app/models/event_person.rb new file mode 100644 index 00000000..ba0721a2 --- /dev/null +++ b/app/models/event_person.rb @@ -0,0 +1,8 @@ +class EventPerson < ActiveRecord::Base + attr_accessible :event, :person, :person_id, :event_role + # TODO Do we need these roles? + ROLES = [["Speaker","speaker"], ["Submitter","submitter"], ["Moderator","moderator"]] + + belongs_to :event + belongs_to :person +end \ No newline at end of file diff --git a/app/models/event_type.rb b/app/models/event_type.rb new file mode 100644 index 00000000..49afdbbb --- /dev/null +++ b/app/models/event_type.rb @@ -0,0 +1,5 @@ +class EventType < ActiveRecord::Base + attr_accessible :title, :length + + belongs_to :conference +end diff --git a/app/models/person.rb b/app/models/person.rb new file mode 100644 index 00000000..173f0088 --- /dev/null +++ b/app/models/person.rb @@ -0,0 +1,71 @@ +class Person < ActiveRecord::Base + attr_accessible :email, :first_name, :last_name, :public_name, :biography, :company + + has_many :event_people, :dependent => :destroy + has_many :events, :through => :event_people, :uniq => true + has_many :registrations, :dependent => :destroy + validates :first_name, :presence => true + validates :last_name, :presence => true + validate :biography_limit + + before_create :generate_guid + before_save :set_public_name + + def to_s + if self.public_name.empty? + self.first_name + " " + self.last_name + else + self.public_name + end + end + + def withdraw_proposal id + proposal = self.events.find_by_id(id) + if !proposal.nil? + proposal.withdraw + proposal.save + end + end + + def attending_conference? conference + Registration.where(:conference_id => conference.id, + :person_id => self.id).count + end + + def proposals conference + self.events.where("conference_id = ? AND state != ? AND state != ?", conference.id, "withdrawn", "rejected") + end + + def proposal_count conference + proposals(conference).count + end + + def biography_word_count + if self.biography.nil? + 0 + else + self.biography.split.size + end + end + + private + def biography_limit + if self.biography.split.size > 150 + errors.add(:abstract, "cannot have more than 150 words") + end + end + + def set_public_name + if public_name.empty? + self.public_name = "#{first_name} #{last_name}" + end + end + + def generate_guid + begin + guid = SecureRandom.urlsafe_base64 + end while Person.where(:guid => guid).exists? + self.guid = guid + end + +end \ No newline at end of file diff --git a/app/models/registration.rb b/app/models/registration.rb new file mode 100644 index 00000000..b8886570 --- /dev/null +++ b/app/models/registration.rb @@ -0,0 +1,9 @@ +class Registration < ActiveRecord::Base + belongs_to :person + belongs_to :conference + + attr_accessible :person_id, :conference_id, :attending_social_events, :attending_social_events_with_partner, + :using_affiliated_lodging, :arrival, :departure, :person_attributes + accepts_nested_attributes_for :person + +end \ No newline at end of file diff --git a/app/models/role.rb b/app/models/role.rb new file mode 100644 index 00000000..0fb24257 --- /dev/null +++ b/app/models/role.rb @@ -0,0 +1,4 @@ +class Role < ActiveRecord::Base + attr_accessible :name + has_and_belongs_to_many :users +end \ No newline at end of file diff --git a/app/models/room.rb b/app/models/room.rb new file mode 100644 index 00000000..ba68566e --- /dev/null +++ b/app/models/room.rb @@ -0,0 +1,18 @@ +class Room < ActiveRecord::Base + attr_accessible :name, :size, :public + + belongs_to :conference + has_many :events + + before_create :generate_guid + + private + + def generate_guid + begin + guid = SecureRandom.urlsafe_base64 + end while Person.where(:guid => guid).exists? + self.guid = guid + end + +end \ No newline at end of file diff --git a/app/models/track.rb b/app/models/track.rb new file mode 100644 index 00000000..7c6f685f --- /dev/null +++ b/app/models/track.rb @@ -0,0 +1,17 @@ +class Track < ActiveRecord::Base + attr_accessible :name, :description, :color + + belongs_to :conference + + before_create :generate_guid + + private + + def generate_guid + begin + guid = SecureRandom.urlsafe_base64 + end while Person.where(:guid => guid).exists? + self.guid = guid + end + +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 00000000..9e262977 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,55 @@ +class User < ActiveRecord::Base + + # Include default devise modules. Others available are: + # :token_authenticatable, :confirmable, + # :lockable, :timeoutable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :trackable, :validatable + + has_and_belongs_to_many :roles + has_one :person + + attr_accessible :email, :password, :password_confirmation, :remember_me, :role_id, :role_ids, :person_attributes + accepts_nested_attributes_for :person + accepts_nested_attributes_for :roles + + before_save :setup_role + before_create :create_person + + def role?(role) + Rails.logger.debug("Checking role in user") + return !!self.roles.find_by_name(role.to_s.camelize) + end + + def get_roles + return self.roles + end + + def setup_role + if self.role_ids.empty? + self.role_ids = [1] + end + end + + def popup_details + details = "Sign-in Count
" + details += "#{self.sign_in_count}
" + details += "Current Sign-in
" + details += "#{self.current_sign_in_at}
" + details += "Last Sign-in
" + details += "#{self.last_sign_in_at}
" + details += "Current Sign-in IP
" + details += "#{self.current_sign_in_ip}
" + details += "Last Sign-in IP
" + details += "#{self.last_sign_in_ip}
" + details += "Created at
" + details += "#{self.created_at}
" + end + + private + def create_person + # TODO Search people for existing email address, add to their account + build_person(:email => self.email) + true + end +end diff --git a/app/models/venue.rb b/app/models/venue.rb new file mode 100644 index 00000000..e0251f7c --- /dev/null +++ b/app/models/venue.rb @@ -0,0 +1,14 @@ +class Venue < ActiveRecord::Base + has_many :conferences + before_create :generate_guid + + private + + def generate_guid + begin + guid = SecureRandom.urlsafe_base64 + end while Venue.where(:guid => guid).exists? + self.guid = guid + end + +end diff --git a/app/views/admin/callforpapers/show.html.haml b/app/views/admin/callforpapers/show.html.haml new file mode 100644 index 00000000..5b388a82 --- /dev/null +++ b/app/views/admin/callforpapers/show.html.haml @@ -0,0 +1,11 @@ +.container-fluid + .row-fluid + .span3 + = render 'admin/conference/sidebar', :activated => "Call for Papers" + .span9 + = semantic_form_for(@cfp, :url => admin_conference_cfp_update_path(@conference.short_title),:html => {:multipart => true}) do |f| + = f.input :start_date, :as => :string, :input_html => { :id => "conference-start-datepicker", :readonly => "readonly" } + = f.input :end_date, :as => :string, :input_html => { :id => "conference-end-datepicker", :readonly => "readonly" } + = f.input :hard_deadline, :as => :string, :input_html => { :id => "cfp-hard-datepicker", :readonly => "readonly" } + = f.input :description, :hint => "Welcome text for the Call for Papers" + = f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"} \ No newline at end of file diff --git a/app/views/admin/conference/_sidebar.html.haml b/app/views/admin/conference/_sidebar.html.haml new file mode 100644 index 00000000..cb8b4397 --- /dev/null +++ b/app/views/admin/conference/_sidebar.html.haml @@ -0,0 +1,26 @@ +.well.sidebar-nav + %ul.nav.nav-list + - if activated == "Conference" + %li.active= link_to "Conference", "#" + - else + %li= link_to "Conference", admin_conference_path(@conference.short_title) + - if activated == "Call for Papers" + %li.active= link_to "Call for Papers", "#" + - else + %li= link_to "Call for Papers", admin_conference_cfp_info_path(@conference.short_title) + - if activated == "Venue" + %li.active= link_to "Venue", "#" + - else + %li= link_to "Venue", admin_conference_venue_info_path(@conference.short_title) + - if activated == "Rooms" + %li.active= link_to "Rooms", "#" + - else + %li= link_to "Rooms", admin_conference_rooms_list_path(@conference.short_title) + - if activated == "Tracks" + %li.active= link_to "Tracks", "#" + - else + %li= link_to "Tracks", admin_conference_tracks_list_path(@conference.short_title) + - if activated == "Event Types" + %li.active= link_to "Event Types", "#" + - else + %li= link_to "Event Types", admin_conference_eventtype_list_path(@conference.short_title) \ No newline at end of file diff --git a/app/views/admin/conference/index.html.haml b/app/views/admin/conference/index.html.haml new file mode 100644 index 00000000..737680e7 --- /dev/null +++ b/app/views/admin/conference/index.html.haml @@ -0,0 +1,3 @@ +%ul + - @conferences.each do |conference| + %li= link_to conference.title, admin_conference_path(conference.short_title) diff --git a/app/views/admin/conference/new.html.haml b/app/views/admin/conference/new.html.haml new file mode 100644 index 00000000..76301c7f --- /dev/null +++ b/app/views/admin/conference/new.html.haml @@ -0,0 +1,13 @@ += semantic_form_for(@conference, :url => admin_conference_index_path) do |f| + = f.inputs "Basic Information" do + = f.input :title, :hint => "The name of your conference as it shall appear throughout the site. Example: 'OpenSUSE Conference 2013'" + = f.input :short_title, :hint => "A short and unique handle for your conference, using only lower-case letters, numbers and underscores. This will be used to identify your conference in URLs etc. Example: 'froscon2011'" + = f.input :social_tag, :hint => "The hashtag you'll use on Twitter and Google+. Don't include the '#' sign!'" + = f.input :contact_email, :hint => "Contact email address for your conference. Will be used as reply-to address in emails sent out by the system." + = f.inputs "Scheduling" do + = f.input :timezone, :as => :time_zone, :hint => "Please select in what time zone your conference will take place. This is needed to correctly display times for your events." + = f.input :start_date, :as => :string, :input_html => { :id => "conference-start-datepicker", :readonly => "readonly" } + = f.input :end_date, :as => :string, :input_html => { :id => "conference-end-datepicker", :readonly => "readonly" } + = f.input :html_export_path, :hint => "The path for static HTML exports" + = f.actions do + = f.action :submit, :button_html => {:class => "btn primary"} diff --git a/app/views/admin/conference/show.html.haml b/app/views/admin/conference/show.html.haml new file mode 100644 index 00000000..4d4df114 --- /dev/null +++ b/app/views/admin/conference/show.html.haml @@ -0,0 +1,17 @@ +.container-fluid + .row-fluid + .span3 + = render 'admin/conference/sidebar', :activated => "Conference" + .span9 + = semantic_form_for(@conference, :url => admin_conference_path(@conference.short_title),:html => {:multipart => true}) do |f| + = f.input :title, :hint => "The full name of the conference, such as 'OpenSUSE Conference 2013'" + = f.input :short_title, :hint => "A short title, such as 'osc2013', to be used in URLs" + = f.input :social_tag, :hint => "The hashtag you'll use on Twitter and Google+. Don't include the '#' sign!'" + = f.input :contact_email, :hint => "Contact email address for your conference. Will be used as reply-to address in emails sent out by the system." + = f.input :timezone, :as => :time_zone, :hint => "The conference time zone" + = f.input :start_date, :as => :string, :input_html => { :id => "conference-start-datepicker", :readonly => "readonly" } + = f.input :end_date, :as => :string, :input_html => { :id => "conference-end-datepicker", :readonly => "readonly" } + = f.input :html_export_path, :hint => "The path for static HTML exports" + = f.input :cfp_open, :label => false, :hint => "Allow people to submit papers?" + = f.input :registration_open, :label => false, :hint => "Allow people to register as attendees?" + = f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"} \ No newline at end of file diff --git a/app/views/admin/events/_person_fields.html.haml b/app/views/admin/events/_person_fields.html.haml new file mode 100644 index 00000000..4999c2ab --- /dev/null +++ b/app/views/admin/events/_person_fields.html.haml @@ -0,0 +1,6 @@ +.nested-fields + = f.inputs do + = f.input :email + = f.input :public_name + = remove_association_link :person, f + diff --git a/app/views/admin/events/edit.html.haml b/app/views/admin/events/edit.html.haml new file mode 100644 index 00000000..9372b87b --- /dev/null +++ b/app/views/admin/events/edit.html.haml @@ -0,0 +1,2 @@ +.container + = render 'proposal/form' \ No newline at end of file diff --git a/app/views/admin/events/index.html.haml b/app/views/admin/events/index.html.haml new file mode 100644 index 00000000..902f8d43 --- /dev/null +++ b/app/views/admin/events/index.html.haml @@ -0,0 +1,48 @@ +.row-fluid + .span12 + %table.table.table-striped.table-bordered.table-hover + %thead + %th + %b Title + %th + %b Submitter + %th + %b Type + %th + %b State + - @events.each do |event| + %tr + %td + =link_to event.title, admin_conference_event_path(@conference.short_title, event) + %td + - if !event.submitter.nil? + =link_to event.submitter.public_name, admin_person_path(event.submitter) + - else + Unknown submitter + %td + = event.event_type.title + %td + - if event.state == "withdrawn" + Withdrawn + - else + .dropdown + = link_to "#", :class => "dropdown-toggle" do + = event.state.humanize + + %ul.dropdown-menu + - if event.transition_possible? :accept + %li= link_to "Accept event (no email)", update_state_admin_conference_event_path(@conference.short_title, event, :transition => :accept, :send_mail => false), + :method => :put, :hint => "Accept this event without sending an automated email." + - if event.transition_possible? :reject + %li= link_to "Reject event (no email)", update_state_admin_conference_event_path(@conference.short_title, event, :transition => :reject, :send_mail => false), + :method => :put, :confirm => "Are you sure?", + :hint => "Reject this event without sending an automated email." + - if event.transition_possible? :start_review + %li= link_to "Start review", update_state_admin_conference_event_path(@conference.short_title, event, :transition => :start_review), + :method => :put + - if event.transition_possible? :confirm + %li= link_to "Confirm event", update_state_admin_conference_event_path(@conference.short_title, event, :transition => :confirm), + :method => :put, :hint => "Confirm that the speaker(s) will be present and that the event will actually take place." + - if event.transition_possible? :cancel + %li= link_to "Cancel event", update_state_admin_conference_event_path(@conference.short_title, event, :transition => :cancel), + :method => :put, :hint => "Mark this event as cancelled. Usually this means that the speakers had to cancel their appearance." diff --git a/app/views/admin/events/show.html.haml b/app/views/admin/events/show.html.haml new file mode 100644 index 00000000..bdd42183 --- /dev/null +++ b/app/views/admin/events/show.html.haml @@ -0,0 +1,112 @@ +.row-fluid + .span10 + %h3 + = @event.title + %i= @event.subtitle + + .span2 + = link_to "Edit Submission", edit_admin_conference_event_path(@conference.short_title, @event), + :class => "btn btn-mini btn-primary pull-right", :style => "margin-top:20px;" + +.row-fluid + .span12 + %table.table + %tr + %td + %b State + %td + .dropdown + = link_to "#", :class => "dropdown-toggle" do + = @event.state.humanize + + %ul.dropdown-menu + - if @event.transition_possible? :accept + %li= link_to "Accept event (no email)", update_state_admin_conference_event_path(@conference.short_title, @event, :transition => :accept, :send_mail => false), + :method => :put, :hint => "Accept this event without sending an automated email." + - if @event.transition_possible? :reject + %li= link_to "Reject event (no email)", update_state_admin_conference_event_path(@conference.short_title, @event, :transition => :reject, :send_mail => false), + :method => :put, :confirm => "Are you sure?", + :hint => "Reject this event without sending an automated email." + - if @event.transition_possible? :start_review + %li= link_to "Start review", update_state_admin_conference_event_path(@conference.short_title, @event, :transition => :start_review), + :method => :put + - if @event.transition_possible? :confirm + %li= link_to "Confirm event", update_state_admin_conference_event_path(@conference.short_title, @event, :transition => :confirm), + :method => :put, :hint => "Confirm that the speaker(s) will be present and that the event will actually take place." + - if @event.transition_possible? :cancel + %li= link_to "Cancel event", update_state_admin_conference_event_path(@conference.short_title, @event, :transition => :cancel), + :method => :put, :hint => "Mark this event as cancelled. Usually this means that the speakers had to cancel their appearance." + + %tr + %td + %b Submitter + %td + = link_to @event.submitter.public_name, admin_person_path(@conference.short_title, @event.submitter) + ( + = link_to @event.submitter.email, "mailto: #{@event.submitter.email}" + ) + %tr + %td + %b Biography + %td + = simple_format(@event.submitter.biography) + %tr + %td + %b Submitted on + %td= @event.created_at + %tr + %td + %b Last updated on + %td= @event.updated_at + %tr + %td + %b Abstract + %td= simple_format(@event.abstract) + %tr + %td + %b Description + %td= simple_format(@event.description) + - if @event.event_attachments.size > 0 + %table.table + %thead + %th + %b Filename + %th + %b Title + %th + %b Size + %th + %b Public? + %th + %tbody + - @event.event_attachments.each do |a| + %tr + %td + = link_to a.attachment_file_name, conference_proposal_event_attachment_path(@conference.short_title, @event.id, a.id) + %td + = a.title + %td + = number_to_human_size a.attachment_file_size + %td + - if a.public? + Public + - else + Not Public + %td + = link_to "Delete", conference_proposal_event_attachment_path(@conference.short_title, @event.id, a.id), + :method => :delete, :confirm => "Are you sure you want to delete this attachment? It's permanant!" + +.row-fluid + = link_to "Comments (#{@comment_count})", "#", :id => "event-comment-link" + #comments-div + %hr + %ul.media + %div + .row-fluid + = form_tag(comment_admin_conference_event_path(@conference.short_title, @event.id), :method => :post) do + = text_area_tag(:comment, "") + = submit_tag "Add Comment", :class => "btn btn-primary pull-right" + %br + - @comments.each do |comment| + %div + = raw format_comments(comment) \ No newline at end of file diff --git a/app/views/admin/eventtype/_event_type_fields.html.erb b/app/views/admin/eventtype/_event_type_fields.html.erb new file mode 100644 index 00000000..bca9f61b --- /dev/null +++ b/app/views/admin/eventtype/_event_type_fields.html.erb @@ -0,0 +1,7 @@ +
+ <%= f.inputs do %> + <%= f.input :title %> + <%= f.input :length, :input_html => {:size => 3} %> + <%= remove_association_link :event_type, f %> + <% end %> +
diff --git a/app/views/admin/eventtype/event_type_list.html.haml b/app/views/admin/eventtype/event_type_list.html.haml new file mode 100644 index 00000000..d1715ce5 --- /dev/null +++ b/app/views/admin/eventtype/event_type_list.html.haml @@ -0,0 +1,8 @@ +.container-fluid + .row-fluid + .span3 + = render 'admin/conference/sidebar', :activated => "Event Types" + .span9 + = semantic_form_for(@conference, :url => admin_conference_eventtype_update_path(@conference.short_title)) do |f| + = dynamic_association :event_types, "Event Types", f + = f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"} \ No newline at end of file diff --git a/app/views/admin/people/index.html.haml b/app/views/admin/people/index.html.haml new file mode 100644 index 00000000..af8a64bf --- /dev/null +++ b/app/views/admin/people/index.html.haml @@ -0,0 +1,18 @@ +%table.table.table-striped.table-bordered.table-hover + %thead + %th + %b Last Name + %th + %b First Name + %th + %b Public Name + %th + %b # of Conference Registrations + %th + - @people.each do |person| + %tr + %td= person.last_name + %td= person.first_name + %td= person.public_name + %td= person.registrations.count + %td= link_to "View", admin_person_path(person) \ No newline at end of file diff --git a/app/views/admin/people/show.html.haml b/app/views/admin/people/show.html.haml new file mode 100644 index 00000000..c996aec3 --- /dev/null +++ b/app/views/admin/people/show.html.haml @@ -0,0 +1,49 @@ +%table.table + %tr + %td + %b First Name + %td + = @person.first_name + %tr + %td + %b Last Name + %td + = @person.last_name + %tr + %td + %b Public Name + %td + = @person.public_name + %tr + %td + %b Company + %td + = @person.company + %tr + %td + %b Email + %td + = @person.email + %tr + %td + %b Created At + %td + = @person.created_at + %tr + %td + %b Updated At + %td + = @person.updated_at + %tr + %td + %b Biography + %td + = @person.biography + %tr + %td + %b Registered to attend + %td + - if @person.registrations.count == 0 + None + - else + = @person.registrations.map { |r| r.conference.title }.join ',' \ No newline at end of file diff --git a/app/views/admin/registrations/show.html.haml b/app/views/admin/registrations/show.html.haml new file mode 100644 index 00000000..a3e5d168 --- /dev/null +++ b/app/views/admin/registrations/show.html.haml @@ -0,0 +1,48 @@ +.row-fluid + .span12 + .pull-right{:style=>"margin-top:20px; margin-bottom:20px;"} + = link_to "Export PDF", admin_conference_registrations_path(@conference.short_title, :format => :pdf), :class => "btn btn-success" +.row-fluid + .span12 + %table.table.table-striped.table-bordered.table-hover + %thead + %th + %b Last Name + %th + %b First Name + %th + %b Public Name + %th + %b Email + %th + %b Attending Social Events + %th + %b Attending With Partner + %th + %b Arrival Date + %th + %b Departure Date + %th + %th + - @registrations.each do |registration| + %tr + %td + = registration.last_name + %td + = registration.first_name + %td + = registration.public_name + %td + = registration.email + %td + = registration.attending_social_events + %td + = registration.attending_social_events_with_partner + %td + = registration.arrival + %td + = registration.departure + %td + = link_to "Edit", "#", :class => "btn btn-primary" + %td + = link_to "Delete", "#", :class => "btn btn-danger", :confirm => "Really delete registration for #{registration.public_name}?" \ No newline at end of file diff --git a/app/views/admin/registrations/show.pdf.prawn b/app/views/admin/registrations/show.pdf.prawn new file mode 100644 index 00000000..38a8031a --- /dev/null +++ b/app/views/admin/registrations/show.pdf.prawn @@ -0,0 +1,38 @@ +prawn_document(:force_download=>true, :filename => @pdf_filename) do |pdf| + table_array = [] + header_array = [" ", + "Last Name", + "First Name", + "Public Name", + "Email", + "Attending Social Events", + "Attending With Partner", + "Arrival Date", + "Departure Date"] + table_array << header_array + @registrations.each do |registration| + row = [] + row << "" + row << registration.last_name + row << registration.first_name + row << registration.public_name + row << registration.email + if registration.attending_social_events + row << "X" + else + row << " " + end + + if registration.attending_social_events_with_partner.to_s + row << "X" + else + row << " " + end + row << registration.arrival + row << registration.departure + table_array << row + end + + pdf.text "#{@conference.title} Registrations", :font_size => 25 + pdf.table table_array, :header => true, :cell_style => {:size => 5, :border_width => 1} +end \ No newline at end of file diff --git a/app/views/admin/rooms/_room_fields.html.erb b/app/views/admin/rooms/_room_fields.html.erb new file mode 100644 index 00000000..203176e8 --- /dev/null +++ b/app/views/admin/rooms/_room_fields.html.erb @@ -0,0 +1,8 @@ +
+ <%= f.inputs do %> + <%= f.input :name %> + <%= f.input :size, :input_html => {:size => 5} %> + <%= f.input :public, :as => :boolean %> + <%= remove_association_link :room, f %> + <% end %> +
diff --git a/app/views/admin/rooms/rooms_list.html.haml b/app/views/admin/rooms/rooms_list.html.haml new file mode 100644 index 00000000..76c9b7eb --- /dev/null +++ b/app/views/admin/rooms/rooms_list.html.haml @@ -0,0 +1,8 @@ +.container-fluid + .row-fluid + .span3 + = render 'admin/conference/sidebar', :activated => "Rooms" + .span9 + = semantic_form_for(@conference, :url => admin_conference_rooms_update_path(@conference.short_title)) do |f| + = dynamic_association :rooms, "Rooms", f + = f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"} \ No newline at end of file diff --git a/app/views/admin/tracks/_track_fields.html.erb b/app/views/admin/tracks/_track_fields.html.erb new file mode 100644 index 00000000..b40c76d8 --- /dev/null +++ b/app/views/admin/tracks/_track_fields.html.erb @@ -0,0 +1,8 @@ +
+ <%= f.inputs do %> + <%= f.input :name %> + <%= f.input :color, :input_html => {:size => 6} %> + <%= f.input :description, :input_html => {:rows => 2 } %> + <%= remove_association_link :track, f %> + <% end %> +
diff --git a/app/views/admin/tracks/tracks_list.html.haml b/app/views/admin/tracks/tracks_list.html.haml new file mode 100644 index 00000000..7ada0212 --- /dev/null +++ b/app/views/admin/tracks/tracks_list.html.haml @@ -0,0 +1,8 @@ +.container-fluid + .row-fluid + .span3 + = render 'admin/conference/sidebar', :activated => "Tracks" + .span9 + = semantic_form_for(@conference, :url => admin_conference_tracks_update_path(@conference.short_title)) do |f| + = dynamic_association :tracks, "Tracks", f + = f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"} \ No newline at end of file diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml new file mode 100644 index 00000000..bd0d5f64 --- /dev/null +++ b/app/views/admin/users/index.html.haml @@ -0,0 +1,57 @@ +.container-fluid + .row-fluid + .span12 + %table.table.table-striped.table-bordered.table-hover + %thead + %th + %b Email + %th + %b Last Name + %th + %b First Name + %th + %b Public Name + %th + %b Roles + %th + %th + - @users.each do |user| + %tr + %td + = user.email + %td + = user.last_name + %td + = user.first_name + %td + = user.public_name + %td + = user.roles.map { |role| role.name }.join ',' + %td + .modal.hide.fade{:id => "user-role-selection-#{user.id}", "role" => "dialog", "aria-hidden" => "true"} + .modal-header + %button{"type"=>"button", :class=>"close", "data-dismiss"=>"modal", "aria-hidden"=>"true"} + × + %h3{:id => "role-selector-header-#{user.id}"} + Modifying Roles + .modal-body + - if current_user == user + You cannot modify your own role! + %br + %button{:class=> "btn btn-danger", "data-dismiss"=> "modal", "aria-hidden"=>"true"} + Cancel + + - else + = "Give #{user.public_name} (#{user.email}) the following roles:" + = semantic_form_for(user, :url => admin_user_path(user), :method => :put) do |f| + = f.input :roles, :label => false + %button{:class=> "btn btn-danger", "data-dismiss"=> "modal", "aria-hidden"=>"true"} + Cancel + = f.action :submit, :as => :button, :button_html => {:value => "Save", :class => "btn btn-primary"} + =link_to "Modify Roles", "#", "data-toggle" => "modal", "data-target" => "#user-role-selection-#{user.id}}" + %td + =link_to "Details", "#", :class => "user-details-popover", "data-trigger" => "click", "data-placement" => "bottom", + "data-html" => "true", + "data-content" => user.popup_details, + "data-original-title" => "" + diff --git a/app/views/admin/venue/venue_info.html.haml b/app/views/admin/venue/venue_info.html.haml new file mode 100644 index 00000000..a4431b76 --- /dev/null +++ b/app/views/admin/venue/venue_info.html.haml @@ -0,0 +1,11 @@ +.container-fluid + .row-fluid + .span3 + = render 'admin/conference/sidebar', :activated => "Venue" + .span9 + = semantic_form_for(@venue, :url => admin_conference_venue_update_path(@conference.short_title),:html => {:multipart => true}) do |f| + = f.input :name + = f.input :address + = f.input :website + = f.input :description, :input_html => { :rows => 10, :cols => 20 } + = f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"} \ No newline at end of file diff --git a/app/views/conference_registration/register.html.haml b/app/views/conference_registration/register.html.haml new file mode 100644 index 00000000..3774998b --- /dev/null +++ b/app/views/conference_registration/register.html.haml @@ -0,0 +1,22 @@ +.container-fluid + .row-fluid + .span12 + %h3.pull-left + Registration for + = @conference.title + .row-fluid + .span12 + = semantic_form_for(@registration, :url => register_conference_path(@conference.short_title), :html => { :method => :put }) do |f| + = f.fields_for :person do + = f.inputs :public_name, :for => :person + = f.input :attending_social_events, :label => false + = f.input :attending_social_events_with_partner, :label => false + = f.input :using_affiliated_lodging, :label => false + = f.input :arrival, :as => :string, :input_html => { :id => "conference-start-datepicker", :readonly => "readonly" } + = f.input :departure, :as => :string, :input_html => { :id => "conference-end-datepicker", :readonly => "readonly" } + - if @registered + = f.action :submit, :button_html => { :value => "Update Registration", :class => "btn btn-primary" } + = link_to "Unregister", register_conference_path(@conference.short_title),:method => :delete, :class => "btn btn-danger", + :confirm => "Are you sure you want to unregister?" + - else + = f.action :submit, :button_html => { :value => "Register", :class => "btn btn-primary" } diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 00000000..81e4472d --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb @@ -0,0 +1,12 @@ +

Resend confirmation instructions

+ +<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> + <%= devise_error_messages! %> + +
<%= f.label :email %>
+ <%= f.email_field :email %>
+ +
<%= f.submit "Resend confirmation instructions" %>
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 00000000..a5c4585e --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

Welcome <%= @resource.email %>!

+ +

You can confirm your account email through the link below:

+ +

<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 00000000..ae9e888a --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

Hello <%= @resource.email %>!

+ +

Someone has requested a link to change your password, and you can do this through the link below.

+ +

<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>

+ +

If you didn't request this, please ignore this email.

+

Your password won't change until you access the link above and create a new one.

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 00000000..2263c219 --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @resource.email %>!

+ +

Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

+ +

Click the link below to unlock your account:

+ +

<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>

diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 00000000..fe620ef4 --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,16 @@ +

Change your password

+ +<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> + <%= devise_error_messages! %> + <%= f.hidden_field :reset_password_token %> + +
<%= f.label :password, "New password" %>
+ <%= f.password_field :password %>
+ +
<%= f.label :password_confirmation, "Confirm new password" %>
+ <%= f.password_field :password_confirmation %>
+ +
<%= f.submit "Change my password" %>
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb new file mode 100644 index 00000000..23501646 --- /dev/null +++ b/app/views/devise/passwords/new.html.erb @@ -0,0 +1,12 @@ +

Forgot your password?

+ +<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> + <%= devise_error_messages! %> + +
<%= f.label :email %>
+ <%= f.email_field :email %>
+ +
<%= f.submit "Send me reset password instructions" %>
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/registrations/_edit_account.html.erb b/app/views/devise/registrations/_edit_account.html.erb new file mode 100644 index 00000000..1945f846 --- /dev/null +++ b/app/views/devise/registrations/_edit_account.html.erb @@ -0,0 +1,21 @@ +<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> +
<%= f.label :email %>
+ <%= f.email_field :email %>
+ +
<%= f.label :password %> (leave blank if you don't want to change it)
+ <%= f.password_field :password, :autocomplete => "off" %>
+ +
<%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation %>
+ +
<%= f.label :current_password %> (we need your current password to confirm your changes)
+ <%= f.password_field :current_password %>
+ +
<%= f.submit "Update" %>
+<% end %> + +

Cancel my account

+ +

Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.

+ +<%= link_to "Back", :back %> diff --git a/app/views/devise/registrations/_edit_profile.html.erb b/app/views/devise/registrations/_edit_profile.html.erb new file mode 100644 index 00000000..9ea5b36a --- /dev/null +++ b/app/views/devise/registrations/_edit_profile.html.erb @@ -0,0 +1,20 @@ +<% if false %> +<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> + <%= f.fields_for :person do |p| %> +

<%= p.label :first_name %>

+

<%= p.text_field :first_name %>

+ +

<%= p.label :last_name %>

+

<%= p.text_field :last_name %>

+ <% end %> +
<%= f.submit "Update" %>
+<% end %> + +<% end %> + +<%= semantic_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> + <%= f.fields_for :person do |p| %> + <%= f.inputs :first_name, :last_name, :for => :person %> + <% end %> +
<%= f.submit "Update" %>
+<% end %> diff --git a/app/views/devise/registrations/edit.html.erb-backup b/app/views/devise/registrations/edit.html.erb-backup new file mode 100644 index 00000000..0abfb3fc --- /dev/null +++ b/app/views/devise/registrations/edit.html.erb-backup @@ -0,0 +1,18 @@ +<%= devise_error_messages! %> +<%= semantic_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> + + <%= f.inputs :name => "Profile" do %> + <%= f.fields_for :person do |p| %> + <%= f.inputs :first_name, :last_name, :for => :person %> + <% end %> + <% end %> + + <%= f.inputs :name => "Account" do %> + <%= f.input :email %> + <%= f.input :password, :hint => "(Leave blank if you don't want to change it)", :input_html => {:autocomplete => "off"} %> + <%= f.input :password_confirmation, :input_html => {:autocomplete => "off"} %> + <%= f.input :current_password, :input_html => {:autocomplete => "off"}, :hint => "(we need your current password to confirm your changes)" %> + <% end %> + +
<%= f.submit "Update" %>
+<% end %> diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml new file mode 100644 index 00000000..5a88d768 --- /dev/null +++ b/app/views/devise/registrations/edit.html.haml @@ -0,0 +1,19 @@ +.container + .row-fluid + = semantic_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| + = f.inputs :name => "Profile" do + = f.fields_for :person do |p| + = p.inputs :first_name, :last_name, :public_name + = p.input :company, :label => "Affiliation", :hint => "This could be a company, a user group, or nothing at all." + = p.input :biography, :input_html => {:rows => 5, "onkeyup" => "word_count(this, 'biography-count')"}, :for => :person + You have used + %span#biography-count #{current_user.person.biography_word_count} + words. Biographies are limited to 150 words. + %br + %br + = f.inputs :name => "Account" do + = f.input :email, :required => false + = f.input :password, :hint => "(Leave blank if you don't want to change it)", :input_html => {:autocomplete => "off"} + = f.input :password_confirmation, :input_html => {:autocomplete => "off"} + = f.input :current_password, :input_html => {:autocomplete => "off"}, :hint => "(we need your current password to confirm password or email changes)" + = f.submit "Update" diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 00000000..9703db37 --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,18 @@ +

Sign up

+ +<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> + <%= devise_error_messages! %> + +
<%= f.label :email %>
+ <%= f.email_field :email %>
+ +
<%= f.label :password %>
+ <%= f.password_field :password %>
+ +
<%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation %>
+ +
<%= f.submit "Sign up" %>
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/sessions/new.html.erb-backup b/app/views/devise/sessions/new.html.erb-backup new file mode 100644 index 00000000..30ab9196 --- /dev/null +++ b/app/views/devise/sessions/new.html.erb-backup @@ -0,0 +1,18 @@ +/ +

Sign in

+ +<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> +
<%= f.label :email %>
+ <%= f.email_field :email %>
+ +
<%= f.label :password %>
+ <%= f.password_field :password %>
+ + <% if devise_mapping.rememberable? -%> +
<%= f.check_box :remember_me %> <%= f.label :remember_me %>
+ <% end -%> + +
<%= f.submit "Sign in" %>
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml new file mode 100644 index 00000000..0974e5ed --- /dev/null +++ b/app/views/devise/sessions/new.html.haml @@ -0,0 +1,13 @@ +.container + %h2.center-text + Sign in + .row + .span6.offset3 + = semantic_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| + = f.input :email + = f.input :password + - if devise_mapping.rememberable? + = f.input :remember_me, :as=> :boolean + = f.submit "Sign in" + + = render "devise/shared/links" diff --git a/app/views/devise/shared/_links.erb b/app/views/devise/shared/_links.erb new file mode 100644 index 00000000..eab783a4 --- /dev/null +++ b/app/views/devise/shared/_links.erb @@ -0,0 +1,25 @@ +<%- if controller_name != 'sessions' %> + <%= link_to "Sign in", new_session_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.registerable? && controller_name != 'registrations' %> + <%= link_to "Sign up", new_registration_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.recoverable? && controller_name != 'passwords' %> + <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> + <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> + <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.omniauthable? %> + <%- resource_class.omniauth_providers.each do |provider| %> + <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
+ <% end -%> +<% end -%> \ No newline at end of file diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb new file mode 100644 index 00000000..e55e82ee --- /dev/null +++ b/app/views/devise/unlocks/new.html.erb @@ -0,0 +1,12 @@ +

Resend unlock instructions

+ +<%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %> + <%= devise_error_messages! %> + +
<%= f.label :email %>
+ <%= f.email_field :email %>
+ +
<%= f.submit "Resend unlock instructions" %>
+<% end %> + +<%= render "devise/shared/links" %> diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml new file mode 100644 index 00000000..92a2bfda --- /dev/null +++ b/app/views/home/index.html.haml @@ -0,0 +1,38 @@ +.container-fluid + %h1.center-text + Upcoming Conferences + - @conferences.each do |conference| + - if conference.cfp_open? + .well + %h2.center-text + = conference.title + %h4.center-text + = conference.date_range_string + .center-text + %i + %a= link_to conference.venue.name, conference.venue.website + , + = conference.venue.address + .row-fluid{:style => "padding-top: 30px;"} + .span4 + - if conference.user_registered?(current_user) + = link_to "Modify Registration", register_conference_path(conference.short_title), :class => "mainbutton center-text" + %br + %i.icon-thumbs-up You're attending! + - else + = link_to "Register", register_conference_path(conference.short_title), :class => "mainbutton center-text" + %br + - if !current_user.nil? + %i.icon-thumbs-down + You haven't registered yet! + .span4.offset4.pull-right + - if !current_user.nil? && current_user.person.proposal_count(conference) > 0 + = link_to "View My Proposals", conference_proposal_index_path(conference.short_title), :class => "mainbutton center-text" + %br + - if !current_user.nil? + %i.icon-star + You've submitted + %b= current_user.person.proposal_count conference + proposals! + - else + = link_to "Submit Proposal", conference_proposal_index_path(conference.short_title), :class => "mainbutton center-text" \ No newline at end of file diff --git a/app/views/layouts/_admin_navigation.html.haml b/app/views/layouts/_admin_navigation.html.haml new file mode 100644 index 00000000..418443f3 --- /dev/null +++ b/app/views/layouts/_admin_navigation.html.haml @@ -0,0 +1,44 @@ +- if @conference.nil? || @conference.short_title.nil? + = link_to "OSEM", admin_conference_index_path, :class => 'brand' +- else + = link_to @conference.title, admin_conference_path(@conference.short_title), :class => 'brand' +- if @conference && !@conference.short_title.nil? + %ul.nav + - if current_page?(admin_conference_registrations_path(@conference.short_title)) + %li.active + = link_to "Registrations", "#" + - else + %li + = link_to "Registrations", admin_conference_registrations_path(@conference.short_title) + - if current_page?(admin_conference_events_path(@conference.short_title)) + %li.active + = link_to "Events", "#" + - else + %li + = link_to "Events", admin_conference_events_path(@conference.short_title) +%ul.nav.secondary-nav.pull-right + - if !@conference.nil? + %li.dropdown + = link_to "#", :class => "dropdown-toggle" do + Export + + %ul.dropdown-menu + %li= link_to "JSON", "#" + %li= link_to "HTML", "#" + %li.dropdown + - unless Conference.count == 1 && !has_role?(current_user, "admin") + = link_to "#", :class => "dropdown-toggle" do + Conferences + + %ul.dropdown-menu + - conferences = Conference.order("created_at DESC") + - unless conferences.nil? + - conferences.each do |c| + - unless @conference == c + %li= link_to c.short_title, admin_conference_path(c.short_title) + - if has_role?(current_user, "admin") + %li= link_to "New Conference", new_admin_conference_path + - if has_role?(current_user, "admin") + %li= link_to "Users", admin_users_path + %li= link_to "People", admin_people_path + %li= link_to "Log out", destroy_user_session_path, :method => :delete \ No newline at end of file diff --git a/app/views/layouts/_messages.html.haml b/app/views/layouts/_messages.html.haml new file mode 100644 index 00000000..fd8e69a8 --- /dev/null +++ b/app/views/layouts/_messages.html.haml @@ -0,0 +1,5 @@ +- flash.each do |name, msg| + - if msg.is_a?(String) + %div{:class => "alert alert-#{name == :notice ? "success" : "error"}"} + %a.close{"data-dismiss" => "alert"} × + = content_tag :div, msg, :id => "flash_#{name}" \ No newline at end of file diff --git a/app/views/layouts/_navigation.html.haml b/app/views/layouts/_navigation.html.haml new file mode 100644 index 00000000..2beda77d --- /dev/null +++ b/app/views/layouts/_navigation.html.haml @@ -0,0 +1,11 @@ += link_to "OSEM", root_path, :class => 'brand' +%ul.nav.pull-right + - if user_signed_in? + %li + = link_to('Logout', destroy_user_session_path, :method=>'delete') + - else + %li + = link_to('Login', new_user_session_path) + - if user_signed_in? + %li + = link_to('Edit account', edit_user_registration_path) diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml new file mode 100644 index 00000000..a36217bb --- /dev/null +++ b/app/views/layouts/admin.html.haml @@ -0,0 +1,27 @@ +!!! +%html + %head + %meta{:charset => "utf-8"} + %meta{:name => "viewport", :content => "width=device-width, initial-scale=1, maximum-scale=1"} + %title= content_for?(:title) ? yield(:title) : "Admin" + %meta{:content => "", :name => "description"} + %meta{:content => "", :name => "author"} + = stylesheet_link_tag "application", :media => "all" + = javascript_include_tag "application" + = csrf_meta_tags + = yield(:head) + %body + .navbar.navbar-fixed-top + .navbar-inner + .container-fluid + = render 'layouts/admin_navigation' + .container + .content + #wrap + #inner-content + .row-fluid + .span12 + = render 'layouts/messages' + .row-fluid + .span12 + = yield \ No newline at end of file diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml new file mode 100644 index 00000000..a68290bc --- /dev/null +++ b/app/views/layouts/application.html.haml @@ -0,0 +1,35 @@ +!!! +%html + %head + %meta{:charset => "utf-8"} + %meta{:name => "viewport", :content => "width=device-width, initial-scale=1, maximum-scale=1"} + %title= content_for?(:title) ? yield(:title) : "OSEM" + %meta{:content => "", :name => "description"} + %meta{:content => "", :name => "author"} + = stylesheet_link_tag "application", :media => "all" + = javascript_include_tag "application" + = csrf_meta_tags + = yield(:head) + %body + .navbar.navbar-fixed-top + .navbar-inner + .container-fluid + = render 'layouts/navigation' + .container + .content + #wrap + #inner-content + .row-fluid + .span12 + = render 'layouts/messages' + .row-fluid + .span12 + = yield + #push + #footer + .container + %p.muted.credit.center-text{:style => "font-size: 10px;"} + OSEM is (C) 2013 + = link_to "Matt Barringer", "http://incoherent.de" + and released under the GPL. You can get the source code from + = link_to "Github.", "https://github.com/mbarringer/osem" \ No newline at end of file diff --git a/app/views/proposal/_form.html.haml b/app/views/proposal/_form.html.haml new file mode 100644 index 00000000..adb5677a --- /dev/null +++ b/app/views/proposal/_form.html.haml @@ -0,0 +1,142 @@ +.tabbable + %ul.nav.nav-tabs + %li.active + = link_to "Proposal", "#proposal-content", "data-toggle"=>"tab" + %li + = link_to "Attachments", "#attachment-content", "data-toggle"=>"tab" + .tab-content + #proposal-content.tab-pane.active + .span12 + = semantic_form_for(@event, :url => @url) do |f| + %section#basic + = f.inputs :name => "Basic Information" do + = f.input :title, :required => true + = f.input :subtitle + = f.input :abstract, :input_html => {:rows => 5, "onkeyup" => "word_count(this, 'abstract-count')"}, :required => true + You have used + %span#abstract-count #{@event.abstract_word_count} + words. Abstracts are limited to 250 words. + %br + %br + = f.input :description, :input_html => {:rows => 5}, :hint => "This will only be shown to organizers, not to attendees." + %section#details + = f.inputs :name => "Event Details" do + = f.input :event_type_id,:as => :select, :collection => @event_types, :include_blank => false + %section#information + = f.inputs :name => "Your Information" do + = semantic_fields_for @person do |p| + = p.input :public_name, :required => true + = p.input :company, :label => "Affiliation", :hint => "This could be a company, a user group, or nothing at all." + = p.input :biography, :required => true, :input_html => {:rows => 5, "onkeyup" => "word_count(this, 'biography-count')"} + You have used + %span#biography-count #{@person.biography_word_count} + words. Biographies are limited to 150 words. + %br + %br + + %section#speakers + = f.inputs :name => "Additional Speakers" do + Will there be any additional speakers? If so, please enter their full names, email address, and a short + biography for each. + = f.input :proposal_additional_speakers, :input_html => {:rows => 5}, :label => false + = f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"} + #attachment-content.tab-pane + .span12 + = form_for EventAttachment.new, :url => conference_proposal_event_attachment_index_path(@conference.short_title, @event), + :html => { :multipart => true, :id => "fileupload" } do |f| + %table.table.table-striped + %thead + %th + %th + %b Filename + %th + %b Title + %th + %b Size + %th + %b Public + %th + %tbody.files + .row-fluid.fileupload-buttonbar + .btn.btn-success.fileinput-button + %i.icon-plus.icon-white + Add files... + = f.file_field :attachment + = f.hidden_field :event_id, :value => @event.id + +:javascript + $(document).ready( function() { + $('#fileupload').fileupload(); + $('#fileupload').bind('fileuploadsubmit', function (e, data) { + var inputs = data.context.find(':input'); + if (inputs.filter('[required][value=""]').first().focus().length) { + return false; + } + data.formData = inputs.serializeArray(); + }); + + } ); + +:plain + + + diff --git a/app/views/proposal/edit.html.haml b/app/views/proposal/edit.html.haml new file mode 100644 index 00000000..42ae2e85 --- /dev/null +++ b/app/views/proposal/edit.html.haml @@ -0,0 +1,2 @@ +.container + = render 'form' \ No newline at end of file diff --git a/app/views/proposal/index.html.haml b/app/views/proposal/index.html.haml new file mode 100644 index 00000000..9296f0ef --- /dev/null +++ b/app/views/proposal/index.html.haml @@ -0,0 +1,18 @@ +.container-fluid + .row-fluid + .span13 + %h2.pull-left + My Proposals + .pull-right{:style=>"margin-top:20px;"} + = link_to "New Proposal", new_conference_proposal_path(@conference.short_title), :class => "btn btn-primary" + - @events.each do |event| + .row-fluid + .span12 + .well + .row-fluid + .span10 + = event.title + .pull-right + = link_to "Edit", edit_conference_proposal_path(@conference.short_title, event.id), :class => "btn btn-mini btn-primary" + = link_to "Withdraw", conference_proposal_path(@conference.short_title, event.id), :method => :delete, + :confirm => "Are you sure you want to withdraw this proposal?", :class => "btn btn-mini btn-danger" diff --git a/app/views/proposal/new.html.haml b/app/views/proposal/new.html.haml new file mode 100644 index 00000000..42ae2e85 --- /dev/null +++ b/app/views/proposal/new.html.haml @@ -0,0 +1,2 @@ +.container + = render 'form' \ No newline at end of file diff --git a/app/views/shared/_dynamic_association.html.haml b/app/views/shared/_dynamic_association.html.haml new file mode 100644 index 00000000..1484100a --- /dev/null +++ b/app/views/shared/_dynamic_association.html.haml @@ -0,0 +1,10 @@ += f.inputs title do + - if hint + %p.inline-hint= hint + %div{:class => association_name.to_s} + = f.semantic_fields_for association_name do |association_form| + = render association_name.to_s.singularize + "_fields", :f => association_form + .links + = add_association_link association_name, f, association_name + %br + %br diff --git a/config.ru b/config.ru new file mode 100644 index 00000000..988dac89 --- /dev/null +++ b/config.ru @@ -0,0 +1,4 @@ +# This file is used by Rack-based servers to start the application. + +require ::File.expand_path('../config/environment', __FILE__) +run Osem::Application diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 00000000..76448fbb --- /dev/null +++ b/config/application.rb @@ -0,0 +1,63 @@ +require File.expand_path('../boot', __FILE__) + +require 'rails/all' +include ActionView::Helpers::NumberHelper + +if defined?(Bundler) + # If you precompile assets before deploying to production, use this line + Bundler.require(*Rails.groups(:assets => %w(development test))) + # If you want your assets lazily compiled in production, use this line + # Bundler.require(:default, :assets, Rails.env) +end + +module Osem + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Custom directories with classes and modules you want to be autoloadable. + # config.autoload_paths += %W(#{config.root}/extras) + + # Only load the plugins named here, in the order given (default is alphabetical). + # :all can be used as a placeholder for all plugins not explicitly named. + # config.plugins = [ :exception_notification, :ssl_requirement, :all ] + + # Activate observers that should always be running. + # config.active_record.observers = :cacher, :garbage_collector, :forum_observer + + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. + # config.time_zone = 'Central Time (US & Canada)' + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] + # config.i18n.default_locale = :de + + # Configure the default encoding used in templates for Ruby 1.9. + config.encoding = "utf-8" + + # Configure sensitive parameters which will be filtered from the log file. + config.filter_parameters += [:password] + + # Enable escaping HTML in JSON. + config.active_support.escape_html_entities_in_json = true + + # Use SQL instead of Active Record's schema dumper when creating the database. + # This is necessary if your schema can't be completely dumped by the schema dumper, + # like if you have constraints or database-specific column types + # config.active_record.schema_format = :sql + + # Enforce whitelist mode for mass assignment. + # This will create an empty whitelist of attributes available for mass-assignment for all models + # in your app. As such, your models will need to explicitly whitelist or blacklist accessible + # parameters by using an attr_accessible or attr_protected declaration. + config.active_record.whitelist_attributes = true + + # Enable the asset pipeline + config.assets.enabled = true + + # Version of your assets, change this if you want to expire all your assets + config.assets.version = '1.0' + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 00000000..4489e586 --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,6 @@ +require 'rubygems' + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) + +require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 00000000..51a4dd45 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 + +production: + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 + timeout: 5000 diff --git a/config/deploy.rb b/config/deploy.rb new file mode 100644 index 00000000..f3f702f9 --- /dev/null +++ b/config/deploy.rb @@ -0,0 +1,16 @@ +namespace :paperclip do + + desc "Create a storage folder for Paperclip attachment in shared path" + task :create_storage do + run "mkdir -p #{shared_path}/storage" + end + + desc "Link the Paperclip storage folder into the current release" + task :link_storage do + run "ln -nfs #{shared_path}/storage #{release_path}/storage" + end + +end + +before "deploy:setup", 'paperclip:create_storage' +after "deploy:update_code", "paperclip:link_storage" diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 00000000..d5b37271 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the rails application +require File.expand_path('../application', __FILE__) + +# Initialize the rails application +Osem::Application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 00000000..113e6099 --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,39 @@ +Osem::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Log error messages when you accidentally call methods on nil. + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Don't care if the mailer can't send + config.action_mailer.raise_delivery_errors = false + + # Print deprecation notices to the Rails logger + config.active_support.deprecation = :log + + # Only use best-standards-support built into browsers + config.action_dispatch.best_standards_support = :builtin + + # Raise exception on mass assignment protection for Active Record models + config.active_record.mass_assignment_sanitizer = :strict + + # Log the query plan for queries taking more than this (works + # with SQLite, MySQL, and PostgreSQL) + config.active_record.auto_explain_threshold_in_seconds = 0.5 + + # Do not compress assets + config.assets.compress = false + config.assets.compile = false + + # Expands the lines which load the assets + config.assets.debug = true + config.action_mailer.default_url_options = { :host => 'localhost:3000' } +end diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 00000000..e5a60292 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,67 @@ +Osem::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # Code is not reloaded between requests + config.cache_classes = true + + # Full error reports are disabled and caching is turned on + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Disable Rails's static asset server (Apache or nginx will already do this) + config.serve_static_assets = false + + # Compress JavaScripts and CSS + config.assets.compress = true + + # Don't fallback to assets pipeline if a precompiled asset is missed + config.assets.compile = false + + # Generate digests for assets URLs + config.assets.digest = true + + # Defaults to nil and saved in location specified by config.assets.prefix + # config.assets.manifest = YOUR_PATH + + # Specifies the header that your server uses for sending files + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # See everything in the log (default is :info) + # config.log_level = :debug + + # Prepend all log lines with the following tags + # config.log_tags = [ :subdomain, :uuid ] + + # Use a different logger for distributed setups + # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + + # Use a different cache store in production + # config.cache_store = :mem_cache_store + + # Enable serving of images, stylesheets, and JavaScripts from an asset server + # config.action_controller.asset_host = "http://assets.example.com" + + # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) + # config.assets.precompile += %w( search.js ) + + # Disable delivery errors, bad email addresses will be ignored + # config.action_mailer.raise_delivery_errors = false + + # Enable threaded mode + # config.threadsafe! + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation can not be found) + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners + config.active_support.deprecation = :notify + + # Log the query plan for queries taking more than this (works + # with SQLite, MySQL, and PostgreSQL) + # config.active_record.auto_explain_threshold_in_seconds = 0.5 +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 00000000..29febc63 --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,37 @@ +Osem::Application.configure do + # Settings specified here will take precedence over those in config/application.rb + + # The test environment is used exclusively to run your application's + # test suite. You never need to work with it otherwise. Remember that + # your test database is "scratch space" for the test suite and is wiped + # and recreated between test runs. Don't rely on the data there! + config.cache_classes = true + + # Configure static asset server for tests with Cache-Control for performance + config.serve_static_assets = true + config.static_cache_control = "public, max-age=3600" + + # Log error messages when you accidentally call methods on nil + config.whiny_nils = true + + # Show full error reports and disable caching + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # Raise exceptions instead of rendering exception templates + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment + config.action_controller.allow_forgery_protection = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Raise exception on mass assignment protection for Active Record models + config.active_record.mass_assignment_sanitizer = :strict + + # Print deprecation notices to the stderr + config.active_support.deprecation = :stderr +end diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 00000000..59385cdf --- /dev/null +++ b/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 00000000..d8114c2b --- /dev/null +++ b/config/initializers/devise.rb @@ -0,0 +1,232 @@ +# Use this hook to configure devise mailer, warden hooks and so forth. +# Many of these configuration options can be set straight in your model. +Devise.setup do |config| + # ==> Mailer Configuration + # Configure the e-mail address which will be shown in Devise::Mailer, + # note that it will be overwritten if you use your own mailer class with default "from" parameter. + config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" + + # Configure the class responsible to send e-mails. + # config.mailer = "Devise::Mailer" + + # ==> ORM configuration + # Load and configure the ORM. Supports :active_record (default) and + # :mongoid (bson_ext recommended) by default. Other ORMs may be + # available as additional gems. + require 'devise/orm/active_record' + + # ==> Configuration for any authentication mechanism + # Configure which keys are used when authenticating a user. The default is + # just :email. You can configure it to use [:username, :subdomain], so for + # authenticating a user, both parameters are required. Remember that those + # parameters are used only when authenticating and not when retrieving from + # session. If you need permissions, you should implement that in a before filter. + # You can also supply a hash where the value is a boolean determining whether + # or not authentication should be aborted when the value is not present. + # config.authentication_keys = [ :email ] + + # Configure parameters from the request object used for authentication. Each entry + # given should be a request method and it will automatically be passed to the + # find_for_authentication method and considered in your model lookup. For instance, + # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. + # The same considerations mentioned for authentication_keys also apply to request_keys. + # config.request_keys = [] + + # Configure which authentication keys should be case-insensitive. + # These keys will be downcased upon creating or modifying a user and when used + # to authenticate or find a user. Default is :email. + config.case_insensitive_keys = [ :email ] + + # Configure which authentication keys should have whitespace stripped. + # These keys will have whitespace before and after removed upon creating or + # modifying a user and when used to authenticate or find a user. Default is :email. + config.strip_whitespace_keys = [ :email ] + + # Tell if authentication through request.params is enabled. True by default. + # It can be set to an array that will enable params authentication only for the + # given strategies, for example, `config.params_authenticatable = [:database]` will + # enable it only for database (email + password) authentication. + # config.params_authenticatable = true + + # Tell if authentication through HTTP Basic Auth is enabled. False by default. + # It can be set to an array that will enable http authentication only for the + # given strategies, for example, `config.http_authenticatable = [:token]` will + # enable it only for token authentication. + # config.http_authenticatable = false + + # If http headers should be returned for AJAX requests. True by default. + # config.http_authenticatable_on_xhr = true + + # The realm used in Http Basic Authentication. "Application" by default. + # config.http_authentication_realm = "Application" + + # It will change confirmation, password recovery and other workflows + # to behave the same regardless if the e-mail provided was right or wrong. + # Does not affect registerable. + # config.paranoid = true + + # By default Devise will store the user in session. You can skip storage for + # :http_auth and :token_auth by adding those symbols to the array below. + # Notice that if you are skipping storage for all authentication paths, you + # may want to disable generating routes to Devise's sessions controller by + # passing :skip => :sessions to `devise_for` in your config/routes.rb + config.skip_session_storage = [:http_auth] + + # ==> Configuration for :database_authenticatable + # For bcrypt, this is the cost for hashing the password and defaults to 10. If + # using other encryptors, it sets how many times you want the password re-encrypted. + # + # Limiting the stretches to just one in testing will increase the performance of + # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use + # a value less than 10 in other environments. + config.stretches = Rails.env.test? ? 1 : 10 + + # Setup a pepper to generate the encrypted password. + # config.pepper = "9a89de1df41b08b48af904bbbc5b8d713addd9d14e2758f12dd14105acd8627fececa02f33f89c0e9764e74262d8874c4a52534b788f5aa4b1716522ae6888b1" + + # ==> Configuration for :confirmable + # A period that the user is allowed to access the website even without + # confirming his account. For instance, if set to 2.days, the user will be + # able to access the website for two days without confirming his account, + # access will be blocked just in the third day. Default is 0.days, meaning + # the user cannot access the website without confirming his account. + # config.allow_unconfirmed_access_for = 2.days + + # If true, requires any email changes to be confirmed (exactly the same way as + # initial account confirmation) to be applied. Requires additional unconfirmed_email + # db field (see migrations). Until confirmed new email is stored in + # unconfirmed email column, and copied to email column on successful confirmation. + config.reconfirmable = true + + # Defines which key will be used when confirming an account + # config.confirmation_keys = [ :email ] + + # ==> Configuration for :rememberable + # The time the user will be remembered without asking for credentials again. + # config.remember_for = 2.weeks + + # If true, extends the user's remember period when remembered via cookie. + # config.extend_remember_period = false + + # Options to be passed to the created cookie. For instance, you can set + # :secure => true in order to force SSL only cookies. + # config.rememberable_options = {} + + # ==> Configuration for :validatable + # Range for password length. Default is 6..128. + # config.password_length = 6..128 + + # Email regex used to validate email formats. It simply asserts that + # an one (and only one) @ exists in the given string. This is mainly + # to give user feedback and not to assert the e-mail validity. + # config.email_regexp = /\A[^@]+@[^@]+\z/ + + # ==> Configuration for :timeoutable + # The time you want to timeout the user session without activity. After this + # time the user will be asked for credentials again. Default is 30 minutes. + # config.timeout_in = 30.minutes + + # If true, expires auth token on session timeout. + # config.expire_auth_token_on_timeout = false + + # ==> Configuration for :lockable + # Defines which strategy will be used to lock an account. + # :failed_attempts = Locks an account after a number of failed attempts to sign in. + # :none = No lock strategy. You should handle locking by yourself. + # config.lock_strategy = :failed_attempts + + # Defines which key will be used when locking and unlocking an account + # config.unlock_keys = [ :email ] + + # Defines which strategy will be used to unlock an account. + # :email = Sends an unlock link to the user email + # :time = Re-enables login after a certain amount of time (see :unlock_in below) + # :both = Enables both strategies + # :none = No unlock strategy. You should handle unlocking by yourself. + # config.unlock_strategy = :both + + # Number of authentication tries before locking an account if lock_strategy + # is failed attempts. + # config.maximum_attempts = 20 + + # Time interval to unlock the account if :time is enabled as unlock_strategy. + # config.unlock_in = 1.hour + + # ==> Configuration for :recoverable + # + # Defines which key will be used when recovering the password for an account + # config.reset_password_keys = [ :email ] + + # Time interval you can reset your password with a reset password key. + # Don't put a too small interval or your users won't have the time to + # change their passwords. + config.reset_password_within = 6.hours + + # ==> Configuration for :encryptable + # Allow you to use another encryption algorithm besides bcrypt (default). You can use + # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, + # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) + # and :restful_authentication_sha1 (then you should set stretches to 10, and copy + # REST_AUTH_SITE_KEY to pepper) + # config.encryptor = :sha512 + + # ==> Configuration for :token_authenticatable + # Defines name of the authentication token params key + # config.token_authentication_key = :auth_token + + # ==> Scopes configuration + # Turn scoped views on. Before rendering "sessions/new", it will first check for + # "users/sessions/new". It's turned off by default because it's slower if you + # are using only default views. + # config.scoped_views = false + + # Configure the default scope given to Warden. By default it's the first + # devise role declared in your routes (usually :user). + # config.default_scope = :user + + # Set this configuration to false if you want /users/sign_out to sign out + # only the current scope. By default, Devise signs out all scopes. + # config.sign_out_all_scopes = true + + # ==> Navigation configuration + # Lists the formats that should be treated as navigational. Formats like + # :html, should redirect to the sign in page when the user does not have + # access, but formats like :xml or :json, should return 401. + # + # If you have any extra navigational formats, like :iphone or :mobile, you + # should add them to the navigational formats lists. + # + # The "*/*" below is required to match Internet Explorer requests. + # config.navigational_formats = ["*/*", :html] + + # The default HTTP method used to sign out a resource. Default is :delete. + config.sign_out_via = :delete + + # ==> OmniAuth + # Add a new OmniAuth provider. Check the wiki for more information on setting + # up on your models and hooks. + # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' + + # ==> Warden configuration + # If you want to use other strategies, that are not supported by Devise, or + # change the failure app, you can configure them inside the config.warden block. + # + # config.warden do |manager| + # manager.intercept_401 = false + # manager.default_strategies(:scope => :user).unshift :some_external_strategy + # end + + # ==> Mountable engine configurations + # When using Devise inside an engine, let's call it `MyEngine`, and this engine + # is mountable, there are some extra configurations to be taken into account. + # The following options are available, assuming the engine is mounted as: + # + # mount MyEngine, at: "/my_engine" + # + # The router that invoked `devise_for`, in the example above, would be: + # config.router_name = :my_engine + # + # When using omniauth, Devise cannot automatically set Omniauth path, + # so you need to do it manually. For the users scope, it would be: + # config.omniauth_path_prefix = "/my_engine/users/auth" +end \ No newline at end of file diff --git a/config/initializers/formtastic.rb b/config/initializers/formtastic.rb new file mode 100644 index 00000000..69eaae95 --- /dev/null +++ b/config/initializers/formtastic.rb @@ -0,0 +1,79 @@ +# encoding: utf-8 + +# Set the default text field size when input is a string. Default is nil. +# Formtastic::FormBuilder.default_text_field_size = 50 + +# Set the default text area height when input is a text. Default is 20. +# Formtastic::FormBuilder.default_text_area_height = 5 + +# Set the default text area width when input is a text. Default is nil. +# Formtastic::FormBuilder.default_text_area_width = 50 + +# Should all fields be considered "required" by default? +# Defaults to true. +# Formtastic::FormBuilder.all_fields_required_by_default = true + +# Should select fields have a blank option/prompt by default? +# Defaults to true. +Formtastic::FormBuilder.include_blank_for_select_by_default = false + +# Set the string that will be appended to the labels/fieldsets which are required +# It accepts string or procs and the default is a localized version of +# '*'. In other words, if you configure formtastic.required +# in your locale, it will replace the abbr title properly. But if you don't want to use +# abbr tag, you can simply give a string as below +Formtastic::FormBuilder.required_string = "(required)" + +# Set the string that will be appended to the labels/fieldsets which are optional +# Defaults to an empty string ("") and also accepts procs (see required_string above) +# Formtastic::FormBuilder.optional_string = "(optional)" + +# Set the way inline errors will be displayed. +# Defaults to :sentence, valid options are :sentence, :list, :first and :none +# Formtastic::FormBuilder.inline_errors = :sentence +# Formtastic uses the following classes as default for hints, inline_errors and error list + +# If you override the class here, please ensure to override it in your stylesheets as well +# Formtastic::FormBuilder.default_hint_class = "inline-hints" +# Formtastic::FormBuilder.default_inline_error_class = "inline-errors" +# Formtastic::FormBuilder.default_error_list_class = "errors" + +# Set the method to call on label text to transform or format it for human-friendly +# reading when formtastic is used without object. Defaults to :humanize. +# Formtastic::FormBuilder.label_str_method = :humanize + +# Set the array of methods to try calling on parent objects in :select and :radio inputs +# for the text inside each @