Merge branch 'master' into responsive_charts

This commit is contained in:
Stella Rouzi 2017-07-15 13:46:40 +03:00 • committed by GitHub
commit 56c8328b2f
273 changed files with 7772 additions and 3171 deletions

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
Dockerfile
docker-compose.*

2
.gitignore vendored
View file

@ -36,3 +36,5 @@ pickle-email-*.html
.env.development .env.development
.env.test .env.test
.env.local .env.local
docker-compose.env
docker-compose.yml

1
.haml-lint.yml Normal file
View file

@ -0,0 +1 @@
inherits_from: .haml-lint_todo.yml

436
.haml-lint_todo.yml Normal file
View file

@ -0,0 +1,436 @@
# This configuration was generated by
# `haml-lint --auto-gen-config`
# on 2017-06-09 04:02:20 +0530 using Haml-Lint version 0.24.0.
# The point is for the user to remove these configuration records
# one by one as the lints are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of Haml-Lint, may require this file to be generated again.
linters:
# Offense count: 945
LineLength:
exclude:
- "app/views/admin/campaigns/_form.html.haml"
- "app/views/admin/campaigns/index.html.haml"
- "app/views/admin/cfps/_form.html.haml"
- "app/views/admin/cfps/show.html.haml"
- "app/views/admin/comments/_all_comments.html.haml"
- "app/views/admin/comments/_posted_comments.html.haml"
- "app/views/admin/comments/_unread_comments.html.haml"
- "app/views/admin/commercials/index.html.haml"
- "app/views/admin/conferences/_campaigns.html.haml"
- "app/views/admin/conferences/_doughnut_chart.html.haml"
- "app/views/admin/conferences/_line_chart.html.haml"
- "app/views/admin/conferences/_recent_registrations.html.haml"
- "app/views/admin/conferences/_recent_submissions.html.haml"
- "app/views/admin/conferences/_recent_users.html.haml"
- "app/views/admin/conferences/_targets.html.haml"
- "app/views/admin/conferences/_todo_list.html.haml"
- "app/views/admin/conferences/_top_submitter.html.haml"
- "app/views/admin/conferences/edit.html.haml"
- "app/views/admin/conferences/index.html.haml"
- "app/views/admin/conferences/new.html.haml"
- "app/views/admin/conferences/show.html.haml"
- "app/views/admin/contacts/edit.html.haml"
- "app/views/admin/difficulty_levels/_form.html.haml"
- "app/views/admin/difficulty_levels/index.html.haml"
- "app/views/admin/emails/_help.html.haml"
- "app/views/admin/emails/index.html.haml"
- "app/views/admin/event_types/_form.html.haml"
- "app/views/admin/event_types/index.html.haml"
- "app/views/admin/events/_all_events.csv.haml"
- "app/views/admin/events/_all_with_comments.csv.haml"
- "app/views/admin/events/_change_state_dropdown.html.haml"
- "app/views/admin/events/_confirmed_events.csv.haml"
- "app/views/admin/events/_nested_comments.html.haml"
- "app/views/admin/events/_proposal.html.haml"
- "app/views/admin/events/_voting.html.haml"
- "app/views/admin/events/_voting_index.html.haml"
- "app/views/admin/events/index.html.haml"
- "app/views/admin/events/registrations.html.haml"
- "app/views/admin/events/show.html.haml"
- "app/views/admin/lodgings/_form.html.haml"
- "app/views/admin/lodgings/index.html.haml"
- "app/views/admin/organizations/_form.html.haml"
- "app/views/admin/organizations/index.html.haml"
- "app/views/admin/programs/_form.html.haml"
- "app/views/admin/programs/show.html.haml"
- "app/views/admin/questions/_form.html.haml"
- "app/views/admin/questions/_questions.html.haml"
- "app/views/admin/questions/edit.html.haml"
- "app/views/admin/questions/index.html.haml"
- "app/views/admin/questions/show.html.haml"
- "app/views/admin/registration_periods/_form.html.haml"
- "app/views/admin/registration_periods/show.html.haml"
- "app/views/admin/registrations/edit.html.haml"
- "app/views/admin/registrations/index.html.haml"
- "app/views/admin/reports/_all_events.html.haml"
- "app/views/admin/reports/_events_with_requirements.html.haml"
- "app/views/admin/reports/_events_without_commercials.html.haml"
- "app/views/admin/reports/_missing_speakers.html.haml"
- "app/views/admin/resources/_form.html.haml"
- "app/views/admin/resources/index.html.haml"
- "app/views/admin/resources/show.html.haml"
- "app/views/admin/roles/_form.html.haml"
- "app/views/admin/roles/_users.html.haml"
- "app/views/admin/roles/index.html.haml"
- "app/views/admin/roles/show.html.haml"
- "app/views/admin/rooms/_form.html.haml"
- "app/views/admin/rooms/index.html.haml"
- "app/views/admin/schedules/_day_tab.html.haml"
- "app/views/admin/schedules/_event.html.haml"
- "app/views/admin/schedules/index.html.haml"
- "app/views/admin/schedules/show.html.haml"
- "app/views/admin/splashpages/_form.html.haml"
- "app/views/admin/splashpages/show.html.haml"
- "app/views/admin/sponsors/_form.html.haml"
- "app/views/admin/sponsors/index.html.haml"
- "app/views/admin/sponsorship_levels/_form.html.haml"
- "app/views/admin/sponsorship_levels/index.html.haml"
- "app/views/admin/targets/_form.html.haml"
- "app/views/admin/targets/index.html.haml"
- "app/views/admin/tickets/_form.html.haml"
- "app/views/admin/tickets/index.html.haml"
- "app/views/admin/tickets/show.html.haml"
- "app/views/admin/tracks/_form.html.haml"
- "app/views/admin/tracks/index.html.haml"
- "app/views/admin/tracks/show.html.haml"
- "app/views/admin/users/_event_registrations.html.haml"
- "app/views/admin/users/_form.html.haml"
- "app/views/admin/users/_submissions.html.haml"
- "app/views/admin/users/index.html.haml"
- "app/views/admin/users/show.html.haml"
- "app/views/admin/venues/_form.html.haml"
- "app/views/admin/venues/show.html.haml"
- "app/views/admin/versions/_object_desc_and_link.html.haml"
- "app/views/admin/versions/index.html.haml"
- "app/views/admin/volunteers/index.html.haml"
- "app/views/commercials/edit.html.haml"
- "app/views/commercials/new.html.haml"
- "app/views/conference_registrations/_form.html.haml"
- "app/views/conference_registrations/_questions.html.haml"
- "app/views/conference_registrations/_registration_info.html.haml"
- "app/views/conference_registrations/_volunteer.html.haml"
- "app/views/conference_registrations/show.html.haml"
- "app/views/conferences/_call_for_paper.html.haml"
- "app/views/conferences/_conference_details.html.haml"
- "app/views/conferences/_gallery.html.haml"
- "app/views/conferences/_lodging.html.haml"
- "app/views/conferences/_program.html.haml"
- "app/views/conferences/_registration.html.haml"
- "app/views/conferences/_schedule_splashpage.html.haml"
- "app/views/conferences/_sponsors.html.haml"
- "app/views/conferences/_tickets.html.haml"
- "app/views/conferences/_venue.html.haml"
- "app/views/conferences/_venue_map.html.haml"
- "app/views/conferences/index.html.haml"
- "app/views/conferences/show.html.haml"
- "app/views/devise/confirmations/new.html.haml"
- "app/views/devise/ichain_sessions/new.html.haml"
- "app/views/devise/ichain_sessions/new_test.html.haml"
- "app/views/devise/passwords/edit.html.haml"
- "app/views/devise/passwords/new.html.haml"
- "app/views/devise/registrations/_volunteeruser.html.haml"
- "app/views/devise/registrations/edit.html.haml"
- "app/views/devise/registrations/new.html.haml"
- "app/views/devise/sessions/new.html.haml"
- "app/views/devise/shared/_help.html.haml"
- "app/views/devise/shared/_links.html.haml"
- "app/views/devise/shared/_openid_links.html.haml"
- "app/views/devise/shared/_sign_in_form_embedded.html.haml"
- "app/views/devise/shared/_sign_up_form_embedded.html.haml"
- "app/views/layouts/_admin_sidebar.html.haml"
- "app/views/layouts/_admin_sidebar_index.html.haml"
- "app/views/layouts/_messages.html.haml"
- "app/views/layouts/_navigation.html.haml"
- "app/views/layouts/application.html.haml"
- "app/views/organizations/index.html.haml"
- "app/views/payments/_payment.html.haml"
- "app/views/proposals/_form.html.haml"
- "app/views/proposals/_proposal_form.html.haml"
- "app/views/proposals/_tooltip.html.haml"
- "app/views/proposals/index.html.haml"
- "app/views/proposals/new.html.haml"
- "app/views/proposals/registrations.html.haml"
- "app/views/proposals/show.html.haml"
- "app/views/schedules/_carousel.html.haml"
- "app/views/schedules/_event.html.haml"
- "app/views/schedules/_schedule.html.haml"
- "app/views/schedules/_schedule_item.html.haml"
- "app/views/schedules/_schedule_tabs.html.haml"
- "app/views/schedules/events.html.haml"
- "app/views/schedules/show.html.haml"
- "app/views/schedules/show.xml.haml"
- "app/views/shared/_changelog_actions.haml"
- "app/views/shared/_dynamic_association.html.haml"
- "app/views/shared/_media_item.html.haml"
- "app/views/shared/_media_items.html.haml"
- "app/views/shared/_object_changes.html.haml"
- "app/views/tickets/_ticket.html.haml"
- "app/views/tickets/index.html.haml"
- "app/views/users/edit.html.haml"
- "app/views/users/show.html.haml"
- "app/views/admin/cfps/index.html.haml"
- "app/views/tracks/_form.html.haml"
- "app/views/tracks/index.html.haml"
- "app/views/tracks/show.html.haml"
# Offense count: 223
InstanceVariables:
exclude:
- "app/views/admin/campaigns/_form.html.haml"
- "app/views/admin/cfps/_booths_cfp.html.haml"
- "app/views/admin/cfps/_form.html.haml"
- "app/views/admin/conferences/_todo_list.html.haml"
- "app/views/admin/difficulty_levels/_form.html.haml"
- "app/views/admin/event_types/_form.html.haml"
- "app/views/admin/events/_change_state_dropdown.html.haml"
- "app/views/admin/events/_form.html.haml"
- "app/views/admin/events/_proposal.html.haml"
- "app/views/admin/events/_voting.html.haml"
- "app/views/admin/events/_voting_index.html.haml"
- "app/views/admin/lodgings/_form.html.haml"
- "app/views/admin/organizations/_form.html.haml"
- "app/views/admin/questions/_questions.html.haml"
- "app/views/admin/registration_periods/_form.html.haml"
- "app/views/admin/reports/_all_events.html.haml"
- "app/views/admin/reports/_events_with_requirements.html.haml"
- "app/views/admin/reports/_events_without_commercials.html.haml"
- "app/views/admin/reports/_missing_speakers.html.haml"
- "app/views/admin/roles/_form.html.haml"
- "app/views/admin/roles/_users.html.haml"
- "app/views/admin/rooms/_form.html.haml"
- "app/views/admin/schedules/_day_tab.html.haml"
- "app/views/admin/schedules/_event.html.haml"
- "app/views/admin/splashpages/_form.html.haml"
- "app/views/admin/sponsors/_form.html.haml"
- "app/views/admin/sponsorship_levels/_form.html.haml"
- "app/views/admin/tickets/_form.html.haml"
- "app/views/admin/tracks/_form.html.haml"
- "app/views/admin/users/_form.html.haml"
- "app/views/admin/users/_submissions.html.haml"
- "app/views/admin/venues/_form.html.haml"
- "app/views/conference_registrations/_form.html.haml"
- "app/views/conference_registrations/_registration_info.html.haml"
- "app/views/conference_registrations/_volunteer.html.haml"
- "app/views/conferences/_call_for_paper.html.haml"
- "app/views/conferences/_lodging.html.haml"
- "app/views/conferences/_program.html.haml"
- "app/views/conferences/_registration.html.haml"
- "app/views/conferences/_schedule_splashpage.html.haml"
- "app/views/conferences/_sponsors.html.haml"
- "app/views/conferences/_tickets.html.haml"
- "app/views/conferences/_venue.html.haml"
- "app/views/conferences/_venue_map.html.haml"
- "app/views/layouts/_admin_sidebar.html.haml"
- "app/views/layouts/_user_menu.html.haml"
- "app/views/payments/_payment.html.haml"
- "app/views/proposals/_encouragement_text.html.haml"
- "app/views/proposals/_form.html.haml"
- "app/views/proposals/_proposal_form.html.haml"
- "app/views/schedules/_carousel.html.haml"
- "app/views/schedules/_event.html.haml"
- "app/views/schedules/_schedule.html.haml"
- "app/views/schedules/_schedule_item.html.haml"
- "app/views/schedules/_schedule_tabs.html.haml"
- "app/views/admin/cfps/_events_cfp.html.haml"
- "app/views/tracks/_form.html.haml"
# Offense count: 32
IdNames:
exclude:
- "app/views/admin/cfps/show.html.haml"
- "app/views/admin/comments/index.html.haml"
- "app/views/admin/conferences/index.html.haml"
- "app/views/admin/conferences/show.html.haml"
- "app/views/admin/difficulty_levels/index.html.haml"
- "app/views/admin/event_types/index.html.haml"
- "app/views/admin/programs/show.html.haml"
- "app/views/admin/questions/show.html.haml"
- "app/views/admin/roles/show.html.haml"
- "app/views/admin/schedules/index.html.haml"
- "app/views/admin/sponsorship_levels/index.html.haml"
- "app/views/admin/users/_event_registrations.html.haml"
- "app/views/admin/users/show.html.haml"
- "app/views/users/edit.html.haml"
- "app/views/admin/cfps/_events_cfp.html.haml"
# Offense count: 4
UnnecessaryInterpolation:
exclude:
- "app/views/admin/conferences/_doughnut_chart.html.haml"
- "app/views/admin/conferences/_recent_submissions.html.haml"
- "app/views/proposals/_proposal_form.html.haml"
- "app/views/proposals/new.html.haml"
# Offense count: 48
UnnecessaryStringOutput:
exclude:
- "app/views/admin/conferences/_targets.html.haml"
- "app/views/admin/event_types/index.html.haml"
- "app/views/admin/events/show.html.haml"
- "app/views/admin/users/_submissions.html.haml"
- "app/views/admin/venues/show.html.haml"
- "app/views/admin/versions/_object_desc_and_link.html.haml"
- "app/views/admin/volunteers/show.html.haml"
- "app/views/conference_registrations/show.html.haml"
- "app/views/conferences/_call_for_paper.html.haml"
- "app/views/conferences/_conference_details.html.haml"
- "app/views/conferences/_program.html.haml"
- "app/views/conferences/_venue.html.haml"
- "app/views/conferences/show.html.haml"
- "app/views/devise/registrations/edit.html.haml"
- "app/views/proposals/_encouragement_text.html.haml"
- "app/views/proposals/_form.html.haml"
- "app/views/users/edit.html.haml"
- "app/views/users/show.html.haml"
# Offense count: 268
SpaceInsideHashAttributes:
exclude:
- "app/views/admin/conferences/_todo_list.html.haml"
- "app/views/admin/questions/_form.html.haml"
- "app/views/admin/questions/index.html.haml"
- "app/views/admin/registrations/index.html.haml"
- "app/views/admin/reports/_all_events.html.haml"
- "app/views/admin/reports/index.html.haml"
- "app/views/admin/schedules/_day_tab.html.haml"
- "app/views/admin/schedules/_event.html.haml"
- "app/views/admin/schedules/index.html.haml"
- "app/views/admin/schedules/show.html.haml"
- "app/views/admin/sponsorship_levels/index.html.haml"
- "app/views/admin/targets/index.html.haml"
- "app/views/admin/tickets/show.html.haml"
- "app/views/admin/tracks/index.html.haml"
- "app/views/admin/users/show.html.haml"
- "app/views/admin/venues/_form.html.haml"
- "app/views/admin/venues/show.html.haml"
- "app/views/conference_registrations/_form.html.haml"
- "app/views/conference_registrations/_ticket.html.haml"
- "app/views/conference_registrations/_volunteer.html.haml"
- "app/views/conferences/_venue.html.haml"
- "app/views/conferences/_venue_map.html.haml"
- "app/views/conferences/index.html.haml"
- "app/views/devise/shared/_help.html.haml"
- "app/views/devise/shared/_openid_links.html.haml"
- "app/views/devise/shared/_sign_in_form_embedded.html.haml"
- "app/views/layouts/_admin_sidebar.html.haml"
- "app/views/layouts/_admin_sidebar_index.html.haml"
- "app/views/layouts/_messages.html.haml"
- "app/views/layouts/_navigation.html.haml"
- "app/views/layouts/application.html.haml"
- "app/views/payments/_payment.html.haml"
- "app/views/proposals/_form.html.haml"
- "app/views/proposals/_tooltip.html.haml"
- "app/views/proposals/index.html.haml"
- "app/views/proposals/new.html.haml"
- "app/views/proposals/show.html.haml"
- "app/views/schedules/_carousel.html.haml"
- "app/views/schedules/_schedule_item.html.haml"
- "app/views/shared/_changelog_actions.haml"
- "app/views/shared/_dynamic_association.html.haml"
- "app/views/shared/_media_item.html.haml"
- "app/views/shared/_media_items.html.haml"
- "app/views/shared/_object_changes.html.haml"
- "app/views/tickets/_ticket.html.haml"
- "app/views/tickets/index.html.haml"
- "app/views/tracks/index.html.haml"
- "app/views/tracks/show.html.haml"
# Offense count: 23
ClassesBeforeIds:
exclude:
- "app/views/admin/emails/index.html.haml"
- "app/views/admin/events/show.html.haml"
- "app/views/admin/reports/index.html.haml"
- "app/views/admin/users/show.html.haml"
- "app/views/admin/venues/_form.html.haml"
- "app/views/conferences/index.html.haml"
- "app/views/devise/shared/_help.html.haml"
- "app/views/devise/shared/_sign_in_form_embedded.html.haml"
- "app/views/layouts/_navigation.html.haml"
- "app/views/proposals/_form.html.haml"
# Offense count: 55
SpaceBeforeScript:
exclude:
- "app/views/admin/events/_form.html.haml"
- "app/views/admin/events/index.html.haml"
- "app/views/admin/registrations/index.html.haml"
- "app/views/admin/splashpages/show.html.haml"
- "app/views/admin/sponsors/_form.html.haml"
- "app/views/admin/targets/_form.html.haml"
- "app/views/admin/tracks/_form.html.haml"
- "app/views/admin/tracks/show.html.haml"
- "app/views/admin/venues/show.html.haml"
- "app/views/admin/versions/index.html.haml"
- "app/views/conference_registrations/_form.html.haml"
- "app/views/conference_registrations/_ticket.html.haml"
- "app/views/conference_registrations/show.html.haml"
- "app/views/conferences/_lodging.html.haml"
- "app/views/conferences/_schedule_splashpage.html.haml"
- "app/views/conferences/_sponsors.html.haml"
- "app/views/conferences/_tickets.html.haml"
- "app/views/conferences/_venue.html.haml"
- "app/views/conferences/index.html.haml"
- "app/views/layouts/_admin.html.haml"
- "app/views/layouts/_navigation.html.haml"
- "app/views/layouts/_user_menu.html.haml"
- "app/views/layouts/application.html.haml"
- "app/views/proposals/_encouragement_text.html.haml"
- "app/views/proposals/new.html.haml"
- "app/views/proposals/registrations.html.haml"
- "app/views/proposals/show.html.haml"
- "app/views/schedules/events.html.haml"
- "app/views/schedules/show.xml.haml"
- "app/views/tickets/index.html.haml"
# Offense count: 14
ConsecutiveSilentScripts:
exclude:
- "app/views/admin/events/index.html.haml"
- "app/views/admin/schedules/_day_tab.html.haml"
- "app/views/admin/schedules/_event.html.haml"
- "app/views/admin/versions/_object_desc_and_link.html.haml"
- "app/views/schedules/_carousel.html.haml"
# Offense count: 29
TrailingWhitespace:
exclude:
- "app/views/admin/organizations/_form.html.haml"
- "app/views/admin/users/index.html.haml"
- "app/views/admin/users/show.html.haml"
- "app/views/admin/volunteers/index.html.haml"
- "app/views/admin/volunteers/show.html.haml"
- "app/views/conference_registrations/_volunteer.html.haml"
- "app/views/devise/passwords/new.html.haml"
- "app/views/payments/new.html.haml"
- "app/views/tickets/index.html.haml"
# Offense count: 2
FinalNewline:
exclude:
- "app/views/admin/registrations/index.csv.haml"
- "app/views/layouts/_admin.html.haml"
# Offense count: 1
ImplicitDiv:
exclude:
- "app/views/admin/registrations/index.html.haml"
# Offense count: 9
MultilinePipe:
exclude:
- "app/views/admin/schedules/_day_tab.html.haml"
- "app/views/admin/schedules/_event.html.haml"
- "app/views/schedules/_carousel.html.haml"
- "app/views/schedules/_event.html.haml"
- "app/views/schedules/_schedule_item.html.haml"
# Offense count: 7
ClassAttributeWithStaticValue:
exclude:
- "app/views/conferences/_gallery.html.haml"
- "app/views/layouts/_navigation.html.haml"
- "app/views/schedules/events.html.haml"

View file

@ -1,131 +1,7 @@
Rails: # Inherit style from another configuration
Enabled: true
inherit_from: .rubocop_todo.yml inherit_from: .rubocop_todo.yml
Style/HashSyntax: # Apply rule to all cops
Enabled: true
EnforcedStyle: ruby19
# Things deprecated in current ruby API
Lint/DeprecatedClassMethods:
Enabled: true
# Do not compare with nil. Use .nil? instead
Style/NilComparison:
Enabled: true
# Use one empty line between method definitions
Style/EmptyLineBetweenDefs:
Enabled: true
# There should be only one empty line in designated place
Style/EmptyLines:
Enabled: true
# Keep a blank line before and after private.
Style/EmptyLinesAroundAccessModifier:
Enabled: true
# Use hash literal {} instead of Hash.new
Style/EmptyLiteral:
Enabled: true
# Prefer `each` over `for i`
Style/For:
Enabled: true
# Wrap your assignment in condition if you mean it, otherwise it is most likely equality check
Lint/AssignmentInCondition:
Enabled: true
# Align blocks of code properly
Lint/BlockAlignment:
Enabled: true
# Do not use literal in conditions. We have it enabled for now
Lint/LiteralInCondition:
Enabled: false
# Prefer `Kernel#loop -> break` over `begin -> while`
Lint/Loop:
Enabled: true
# do not put space before arguments when they are in parentheses
Lint/ParenthesesAsGroupedExpression:
Enabled: true
# Do not rescue Exceptions class itself
Lint/RescueException:
Enabled: true
# do not shadow local variables in blocks, choose other name
Lint/ShadowingOuterLocalVariable:
Enabled: true
# use _ or variable_name to explicitly mark variable as unused
Lint/UnusedBlockArgument:
Enabled: true
# use _ or _argument_name to explicitly mark argument as unused
Lint/UnusedMethodArgument:
Enabled: true
# avoid useless assignment
Lint/UselessAssignment:
Enabled: true
# Do not use variables in void context
Lint/Void:
Enabled: true
# Align the elements of a hash literal if they span more than one line
Style/AlignHash:
Enabled: true
# Align the parameters of a method call if they span more than one line
Style/AlignParameters:
Enabled: true
# Use && instead of and, use || instead of or
Style/AndOr:
Enabled: true
# avoid deep blocks nesting
Metrics/BlockNesting:
Max: 4
Metrics/ClassLength:
Max: 300
Exclude:
- 'app/models/conference.rb'
# avoid redundunt curly braces when it is obvious that hash is used
Style/BracesAroundHashParameters:
Enabled: true
# Checks that operators have space around them, except for ** which should not have surrounding space.
Style/SpaceAroundOperators:
Enabled: true
Style/CaseEquality:
Enabled: true
Style/ClassAndModuleChildren:
Enabled: true
Style/StringLiterals:
Enabled: true
Style/TrailingBlankLines:
Enabled: true
Style/TrailingWhitespace:
Enabled: true
# Checks for uses of double negation (!!) to convert something to a boolean value.
Style/DoubleNegation:
Enabled: true
AllCops: AllCops:
Include: Include:
- '**/Rakefile' - '**/Rakefile'
@ -136,3 +12,22 @@ AllCops:
- 'bundle/**/*' - 'bundle/**/*'
- 'config/**/*' - 'config/**/*'
- 'bin/*' - 'bin/*'
#################### Metrics ###############################
# Avoid deep blocks nesting
Metrics/BlockNesting:
Max: 4
# Avoid writing classes that are more than 300 lines
Metrics/ClassLength:
Max: 300
Exclude:
- 'app/models/conference.rb'
Metrics/BlockLength:
Exclude:
- 'spec/models/conference_spec.rb'
- 'spec/features/ability_spec.rb'
- 'spec/models/ability_spec.rb'

File diff suppressed because it is too large Load diff

View file

@ -24,5 +24,9 @@ before_script:
- mysql -u root -e 'create database osem_test;' - mysql -u root -e 'create database osem_test;'
- RAILS_ENV=test bundle exec rake db:migrate --trace - RAILS_ENV=test bundle exec rake db:migrate --trace
script: script:
- 'bundle exec rubocop -Dc .rubocop.yml' - "./travis_script.sh $TEST_SUITE"
- 'bundle exec rspec --color --format documentation' env:
- TEST_SUITE=rspec
- TEST_SUITE=linters
matrix:
fast_finish: true

View file

@ -131,7 +131,7 @@ We are using [rubocop](https://github.com/bbatsov/rubocop) as a style checker. I
vagrant exec bundle exec rubocop vagrant exec bundle exec rubocop
``` ```
You can read through current enabled rules in `.rubocop.yml` file. Explanations of the defined [rules](http://rubydoc.info/github/bbatsov/rubocop/master/frames) can be found in modules [Cop::Lint](http://rubydoc.info/github/bbatsov/rubocop/master/Rubocop/Cop/Lint) and [Cop::Style](http://rubydoc.info/github/bbatsov/rubocop/master/Rubocop/Cop/Style). You can read through current enabled rules in `.rubocop.yml` file. Explanations of the defined [rules](http://rubydoc.info/github/bbatsov/rubocop/master/frames) can be found in modules [Cop::Lint](http://rubydoc.info/github/bbatsov/rubocop/master/Rubocop/Cop/Lint) and [Cop::Style](http://rubydoc.info/github/bbatsov/rubocop/master/Rubocop/Cop/Style) and [Cop:Rails](https://rubocop.readthedocs.io/en/latest/cops_rails/).
Additionally you can read through the [ruby style-guide](https://github.com/bbatsov/ruby-style-guide) to better understand core principles. Additionally you can read through the [ruby style-guide](https://github.com/bbatsov/ruby-style-guide) to better understand core principles.
### Test Suite ### Test Suite
@ -169,6 +169,18 @@ config.ichain_force_test_username = "testuser"
# set email of 'testuser' # set email of 'testuser'
config.ichain_force_test_attributes = {:email => "testuser@example.com"} config.ichain_force_test_attributes = {:email => "testuser@example.com"}
``` ```
### Using OpenID in developement
OSEM supports [OpenID](https://openid.net/) logins via [OmniAuth](https://github.com/omniauth/omniauth) and related provider specific gems. OmniAuth provides the ablity to define per-provider mock accounts for testing. The supported providers are Facebook, Google, openSUSE and GitHub. If you want to use the OSEM provided mock accounts you need to set the appropriate `OSEM_PROVIDER_KEY` and `OSEM_PROVIDER_SECRET` environment variables to a non empty string in the `.env` file.
e.g.
```
OSEM_GITHUB_KEY='sample'
OSEM_GITHUB_SECRET='sample'
```
If you don't already have a `.env` file you can use the `dotenv.example` as a template.
## Labels for issues and PRs ## Labels for issues and PRs
...and what they mean! ...and what they mean!
@ -204,6 +216,8 @@ config.ichain_force_test_attributes = {:email => "testuser@example.com"}
* Our code needs to be re-written; to avoid code duplication, or make the code more readable, or do things in a simpler way! * Our code needs to be re-written; to avoid code duplication, or make the code more readable, or do things in a simpler way!
14. **Research** 14. **Research**
* Ideas to explore; and think if there is anything we want to include in our app. * Ideas to explore; and think if there is anything we want to include in our app.
15. **GSoC**
* To group all the issues and PRs related to Google Summer of Code together.
## Code of Conduct ## Code of Conduct
OSEM is part of the openSUSE project. We follow all the [openSUSE Guiding Principles!](http://en.opensuse.org/openSUSE:Guiding_principles) If you think someone doesn't do that, please let us know at maintainers@osem.io OSEM is part of the openSUSE project. We follow all the [openSUSE Guiding Principles!](http://en.opensuse.org/openSUSE:Guiding_principles) If you think someone doesn't do that, please let us know at maintainers@osem.io

45
Dockerfile Normal file
View file

@ -0,0 +1,45 @@
FROM ruby:2.3
MAINTAINER TheAssassin <theassassin@users.noreply.github.com>
# required for compiling assets
RUN apt-get update && \
apt-get install -y nodejs nodejs-legacy mariadb-client imagemagick
# used to run the container without root permissions
RUN adduser --home /osem/ --system --group --disabled-login --disabled-password osem
# required to detect when the database is up and running in init.sh
RUN cd /usr/bin && \
wget https://github.com/jwilder/dockerize/releases/download/v0.3.0/dockerize-linux-amd64-v0.3.0.tar.gz -O dockerize.tar.gz && \
echo "36e8319cdf9d2b07340f456ec61cfa0f495ec6c130b02ad9c116fd55a5c43fa1 dockerize.tar.gz" | sha256sum -c && \
tar -xf dockerize.tar.gz && \
rm dockerize.tar.gz
# explicitly add Gemfile and install dependencies using bundler to make use of
# Docker's caching
WORKDIR /osem/
COPY Gemfile /osem/
COPY Gemfile.lock /osem/
RUN bundle install --without test development
# add OSEM files and prepare them for use inside a Docker container
COPY . /osem/
RUN chown osem.osem /osem/ -R && \
mv /osem/config/database.yml.docker /osem/config/database.yml
# data directory is used to cache the secret key in a file
ENV DATA_DIR /data
RUN install -d -m 0700 -o osem $DATA_DIR
VOLUME ["$DATA_DIR"]
USER osem
EXPOSE 9292
COPY docker/init.sh /init.sh
# a user could override this if they wanted to serve the static files directly
# from a webserver
ENV RAILS_SERVE_STATIC_FILES 1
CMD ["bash", "/init.sh"]

14
Gemfile
View file

@ -18,10 +18,9 @@ gem 'rails_12factor', group: :production
gem 'responders', '~> 2.0' gem 'responders', '~> 2.0'
# as the database for Active Record # as the database for Active Record
# choose only one
gem 'mysql2' gem 'mysql2'
# gem 'pg'
# for observing records
gem 'rails-observers'
# for tracking data changes # for tracking data changes
gem 'paper_trail' gem 'paper_trail'
@ -126,8 +125,11 @@ gem 'country_select'
# as PDF generator # as PDF generator
gem 'prawn_rails' gem 'prawn_rails'
gem 'rqrcode'
gem 'prawn-qrcode', '~> 0.2.2.1'
# to render XLS spreadsheets # to render XLS spreadsheets
gem 'axlsx', git: 'https://github.com/randym/axlsx.git'
gem 'axlsx_rails' gem 'axlsx_rails'
# as error catcher # as error catcher
@ -193,13 +195,17 @@ gem 'stripe'
# Provides Sprockets implementation for Rails Asset Pipeline # Provides Sprockets implementation for Rails Asset Pipeline
gem 'sprockets-rails' gem 'sprockets-rails'
# for multiple speakers select on proposal/event forms
gem 'selectize-rails'
# Use guard and spring for testing in development # Use guard and spring for testing in development
group :development do group :development do
# to launch specs when files are modified # to launch specs when files are modified
gem 'guard-rspec', '~> 4.2.8' gem 'guard-rspec', '~> 4.2.8'
gem 'spring-commands-rspec' gem 'spring-commands-rspec'
gem 'haml_lint', '~> 0.24.0'
# for static code analisys # for static code analisys
gem 'rubocop', require: false gem 'rubocop', '~> 0.49.0', require: false
# as database # as database
gem 'sqlite3' gem 'sqlite3'
# to open mails # to open mails

View file

@ -1,3 +1,13 @@
GIT
remote: https://github.com/randym/axlsx.git
revision: c8ac844572b25fda358cc01d2104720c4c42f450
specs:
axlsx (2.1.0.pre)
htmlentities (~> 4.3.4)
mimemagic (~> 0.3)
nokogiri (>= 1.6.6)
rubyzip (>= 1.2.1)
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
remote: https://rails-assets.org/ remote: https://rails-assets.org/
@ -58,17 +68,13 @@ GEM
user_agent_parser user_agent_parser
uuidtools uuidtools
arel (6.0.3) arel (6.0.3)
ast (2.2.0) ast (2.3.0)
autoprefixer-rails (5.1.9) autoprefixer-rails (5.1.9)
execjs execjs
json json
awesome_nested_set (3.0.0.rc.5) awesome_nested_set (3.0.0.rc.5)
activerecord (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5)
aws_cf_signer (0.1.3) aws_cf_signer (0.1.3)
axlsx (2.0.1)
htmlentities (~> 4.3.1)
nokogiri (>= 1.4.1)
rubyzip (~> 1.2.1)
axlsx_rails (0.2.0) axlsx_rails (0.2.0)
axlsx (>= 2.0.1) axlsx (>= 2.0.1)
rails (>= 3.1) rails (>= 3.1)
@ -83,9 +89,7 @@ GEM
momentjs-rails (>= 2.8.1) momentjs-rails (>= 2.8.1)
browser (0.6.0) browser (0.6.0)
builder (3.2.2) builder (3.2.2)
byebug (3.1.2) byebug (9.0.6)
columnize (~> 0.8)
debugger-linecache (~> 1.2)
cancancan (1.13.1) cancancan (1.13.1)
capybara (2.6.2) capybara (2.6.2)
addressable addressable
@ -111,6 +115,7 @@ GEM
chart-js-rails (0.0.6) chart-js-rails (0.0.6)
railties (> 3.1) railties (> 3.1)
chronic (0.10.2) chronic (0.10.2)
chunky_png (1.3.8)
cliver (0.3.2) cliver (0.3.2)
cloudinary (1.1.6) cloudinary (1.1.6)
aws_cf_signer aws_cf_signer
@ -124,7 +129,6 @@ GEM
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.10.0) coffee-script-source (1.10.0)
columnize (0.8.9)
countable-rails (0.0.1) countable-rails (0.0.1)
railties (>= 3.1) railties (>= 3.1)
countries (1.2.5) countries (1.2.5)
@ -146,7 +150,6 @@ GEM
dante (0.2.0) dante (0.2.0)
database_cleaner (1.3.0) database_cleaner (1.3.0)
debug_inspector (0.0.2) debug_inspector (0.0.2)
debugger-linecache (1.2.0)
delayed_job (4.1.1) delayed_job (4.1.1)
activesupport (>= 3.0, < 5.0) activesupport (>= 3.0, < 5.0)
delayed_job_active_record (4.1.0) delayed_job_active_record (4.1.0)
@ -209,6 +212,12 @@ GEM
activesupport (>= 4.0.1) activesupport (>= 4.0.1)
haml (>= 3.1, < 5.0) haml (>= 3.1, < 5.0)
railties (>= 4.0.1) railties (>= 4.0.1)
haml_lint (0.24.0)
haml (>= 4.0, < 5.1)
rainbow
rake (>= 10, < 13)
rubocop (>= 0.47.0)
sysexits (~> 1.1)
hashie (2.1.1) hashie (2.1.1)
hike (1.2.3) hike (1.2.3)
hoptoad_notifier (2.4.11) hoptoad_notifier (2.4.11)
@ -230,7 +239,7 @@ GEM
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
jquery-ui-rails (4.2.1) jquery-ui-rails (4.2.1)
railties (>= 3.2.16) railties (>= 3.2.16)
json (1.8.3) json (1.8.6)
json-schema (2.5.0) json-schema (2.5.0)
addressable (~> 2.3) addressable (~> 2.3)
jwt (1.0.0) jwt (1.0.0)
@ -256,12 +265,13 @@ GEM
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
method_source (0.8.2) method_source (0.8.2)
mime-types (2.99.1) mime-types (2.99.1)
mimemagic (0.3.2)
mina (0.3.8) mina (0.3.8)
open4 (~> 1.3.4) open4 (~> 1.3.4)
rake rake
mini_magick (4.5.1) mini_magick (4.5.1)
mini_portile2 (2.1.0) mini_portile2 (2.2.0)
minitest (5.9.0) minitest (5.10.1)
momentjs-rails (2.8.1) momentjs-rails (2.8.1)
railties (>= 3.1) railties (>= 3.1)
monetize (1.4.0) monetize (1.4.0)
@ -280,9 +290,8 @@ GEM
mysql2 (0.4.2) mysql2 (0.4.2)
netrc (0.11.0) netrc (0.11.0)
nio4r (1.2.1) nio4r (1.2.1)
nokogiri (1.6.8) nokogiri (1.8.0)
mini_portile2 (~> 2.1.0) mini_portile2 (~> 2.2.0)
pkg-config (~> 1.1.7)
oauth2 (0.9.4) oauth2 (0.9.4)
faraday (>= 0.8, < 0.10) faraday (>= 0.8, < 0.10)
jwt (~> 1.0) jwt (~> 1.0)
@ -310,11 +319,12 @@ GEM
rack-openid (~> 1.3.1) rack-openid (~> 1.3.1)
open4 (1.3.4) open4 (1.3.4)
orm_adapter (0.5.0) orm_adapter (0.5.0)
paper_trail (5.0.1) paper_trail (5.2.1)
activerecord (>= 3.0, < 6.0) activerecord (>= 3.0, < 6.0)
activesupport (>= 3.0, < 6.0) activesupport (>= 3.0, < 6.0)
request_store (~> 1.1) request_store (~> 1.1)
parser (2.3.0.3) parallel (1.11.2)
parser (2.4.0.0)
ast (~> 2.2) ast (~> 2.2)
pdf-core (0.2.5) pdf-core (0.2.5)
phantomjs (2.1.1.0) phantomjs (2.1.1.0)
@ -322,7 +332,6 @@ GEM
actionpack actionpack
activesupport activesupport
rails (>= 3.0.0) rails (>= 3.0.0)
pkg-config (1.1.7)
poltergeist (1.9.0) poltergeist (1.9.0)
capybara (~> 2.1) capybara (~> 2.1)
cliver (~> 0.3.1) cliver (~> 0.3.1)
@ -332,6 +341,9 @@ GEM
prawn (1.0.0) prawn (1.0.0)
pdf-core (~> 0.2.2) pdf-core (~> 0.2.2)
ttfunk (~> 1.1.1) ttfunk (~> 1.1.1)
prawn-qrcode (0.2.2.1)
prawn (>= 0.11.1)
rqrcode (>= 0.4.1)
prawn_rails (0.0.11) prawn_rails (0.0.11)
prawn (>= 0.11.1) prawn (>= 0.11.1)
railties (>= 3.0.0) railties (>= 3.0.0)
@ -375,17 +387,15 @@ GEM
rails-assets-waypoints (4.0.0) rails-assets-waypoints (4.0.0)
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.7) rails-dom-testing (1.0.8)
activesupport (>= 4.2.0.beta, < 5.0) activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0) nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.3)
loofah (~> 2.0) loofah (~> 2.0)
rails-i18n (4.0.8) rails-i18n (4.0.8)
i18n (~> 0.7) i18n (~> 0.7)
railties (~> 4.0) railties (~> 4.0)
rails-observers (0.1.2)
activemodel (~> 4.0)
rails_12factor (0.0.3) rails_12factor (0.0.3)
rails_serve_static_assets rails_serve_static_assets
rails_stdout_logging rails_stdout_logging
@ -396,7 +406,8 @@ GEM
activesupport (= 4.2.7.1) activesupport (= 4.2.7.1)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.1.0) rainbow (2.2.2)
rake
rake (10.5.0) rake (10.5.0)
rb-fsevent (0.9.4) rb-fsevent (0.9.4)
rb-inotify (0.9.4) rb-inotify (0.9.4)
@ -418,6 +429,8 @@ GEM
mime-types (>= 1.16, < 3.0) mime-types (>= 1.16, < 3.0)
netrc (~> 0.7) netrc (~> 0.7)
rolify (5.1.0) rolify (5.1.0)
rqrcode (0.10.1)
chunky_png (~> 1.0)
rspec (3.0.0) rspec (3.0.0)
rspec-core (~> 3.0.0) rspec-core (~> 3.0.0)
rspec-expectations (~> 3.0.0) rspec-expectations (~> 3.0.0)
@ -442,15 +455,16 @@ GEM
rspec-mocks (~> 3.0.0) rspec-mocks (~> 3.0.0)
rspec-support (~> 3.0.0) rspec-support (~> 3.0.0)
rspec-support (3.0.2) rspec-support (3.0.2)
rubocop (0.37.0) rubocop (0.49.1)
parser (>= 2.3.0.2, < 3.0) parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 0.3) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-oembed (0.8.14) ruby-oembed (0.8.14)
ruby-openid (2.5.0) ruby-openid (2.5.0)
ruby-progressbar (1.7.5) ruby-progressbar (1.8.1)
rubyzip (1.2.1) rubyzip (1.2.1)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sass (3.2.19) sass (3.2.19)
@ -459,7 +473,8 @@ GEM
sass (~> 3.2.2) sass (~> 3.2.2)
sprockets (~> 2.8, < 2.12) sprockets (~> 2.8, < 2.12)
sprockets-rails (~> 2.0) sprockets-rails (~> 2.0)
shoulda-matchers (2.6.1) selectize-rails (0.12.4)
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
simplecov (0.11.2) simplecov (0.11.2)
docile (~> 1.1.0) docile (~> 1.1.0)
@ -489,10 +504,11 @@ GEM
dante (>= 0.2.0) dante (>= 0.2.0)
multi_json (>= 1.0.0) multi_json (>= 1.0.0)
stripe (>= 1.31.0, <= 1.43) stripe (>= 1.31.0, <= 1.43)
sysexits (1.2.0)
term-ansicolor (1.3.2) term-ansicolor (1.3.2)
tins (~> 1.0) tins (~> 1.0)
thor (0.19.1) thor (0.19.1)
thread_safe (0.3.5) thread_safe (0.3.6)
tilt (1.4.1) tilt (1.4.1)
timecop (0.7.1) timecop (0.7.1)
timers (1.1.0) timers (1.1.0)
@ -501,14 +517,14 @@ GEM
ttfunk (1.1.1) ttfunk (1.1.1)
turbolinks (2.5.3) turbolinks (2.5.3)
coffee-rails coffee-rails
tzinfo (1.2.2) tzinfo (1.2.3)
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (3.0.0) uglifier (3.0.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.2) unf_ext (0.0.7.2)
unicode-display_width (0.3.1) unicode-display_width (1.3.0)
unicode_utils (1.4.0) unicode_utils (1.4.0)
unobtrusive_flash (3.1.0) unobtrusive_flash (3.1.0)
railties railties
@ -545,6 +561,7 @@ DEPENDENCIES
ahoy_matey ahoy_matey
autoprefixer-rails autoprefixer-rails
awesome_nested_set (~> 3.0.0.rc.5) awesome_nested_set (~> 3.0.0.rc.5)
axlsx!
axlsx_rails axlsx_rails
bootstrap-sass (~> 3.3.4.1) bootstrap-sass (~> 3.3.4.1)
bootstrap-switch-rails (~> 3.0.0) bootstrap-switch-rails (~> 3.0.0)
@ -574,6 +591,7 @@ DEPENDENCIES
gravtastic gravtastic
guard-rspec (~> 4.2.8) guard-rspec (~> 4.2.8)
haml-rails haml-rails
haml_lint (~> 0.24.0)
hoptoad_notifier (~> 2.3) hoptoad_notifier (~> 2.3)
iso-639 iso-639
jquery-datatables-rails (~> 2.2.1) jquery-datatables-rails (~> 2.2.1)
@ -596,6 +614,7 @@ DEPENDENCIES
phantomjs phantomjs
piwik_analytics (~> 1.0.1) piwik_analytics (~> 1.0.1)
poltergeist poltergeist
prawn-qrcode (~> 0.2.2.1)
prawn_rails prawn_rails
rails (~> 4.2) rails (~> 4.2)
rails-assets-bootstrap-markdown! rails-assets-bootstrap-markdown!
@ -610,17 +629,18 @@ DEPENDENCIES
rails-assets-trianglify! rails-assets-trianglify!
rails-assets-waypoints! rails-assets-waypoints!
rails-i18n (~> 4.0.0) rails-i18n (~> 4.0.0)
rails-observers
rails_12factor rails_12factor
rdoc-generator-fivefish rdoc-generator-fivefish
redcarpet redcarpet
responders (~> 2.0) responders (~> 2.0)
rolify rolify
rqrcode
rspec-activemodel-mocks rspec-activemodel-mocks
rspec-rails rspec-rails
rubocop rubocop (~> 0.49.0)
ruby-oembed ruby-oembed
sass-rails (>= 4.0.2) sass-rails (>= 4.0.2)
selectize-rails
shoulda-matchers shoulda-matchers
spring-commands-rspec spring-commands-rspec
sprockets-rails sprockets-rails
@ -637,4 +657,4 @@ DEPENDENCIES
whenever whenever
BUNDLED WITH BUNDLED WITH
1.14.3 1.15.1

View file

@ -11,7 +11,9 @@ OSEM is an [semantic versioned](http://semver.org/) app. That means given a vers
## Download ## Download
You can find the latest OSEM releases on our [release page](https://github.com/openSUSE/osem/releases/latest) ([older release here](https://github.com/openSUSE/osem/releases)) You can find the latest OSEM releases on our [release page](https://github.com/openSUSE/osem/releases/latest) ([older release here](https://github.com/openSUSE/osem/releases))
## Deploy ## Deploy
OSEM is a *Ruby on Rails* application. We recommend to run OSEM in production with [mod_passenger](https://www.phusionpassenger.com/download/#open_source) OSEM is a *Ruby on Rails* application. We recommend to run OSEM in production with [mod_passenger](https://www.phusionpassenger.com/download/#open_source)
and the [apache web-server](https://www.apache.org/). There are tons of guides on how to deploy rails apps on various and the [apache web-server](https://www.apache.org/). There are tons of guides on how to deploy rails apps on various
base operating systems. [Check Google](https://encrypted.google.com/search?hl=en&q=ruby%20on%20rails%20apache%20passenger) ;-) base operating systems. [Check Google](https://encrypted.google.com/search?hl=en&q=ruby%20on%20rails%20apache%20passenger) ;-)
@ -24,6 +26,55 @@ If you have an heroku account you can also
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy"> <img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy">
</a> </a>
### Deploy with Docker
You can deploy OSEM using [Docker](https://docker.com/) and [Docker-Compose](https://docs.docker.com/compose/overview/).
*This is just a short guide and does not explain how to use Docker and/or Docker-Compose. You need some experience with these tools to be able to deploy OSEM with Docker properly.*
First of all, copy `docker-compose.yml.example` to `docker-compose.yml` and `docker-compose.env.example` to `docker-compose.env`. You should immediately change
`docker-compose.env`'s permissions to `0600` to make sure all the passphrases in it are kept secret.
(Tip: the easiest and most secure way to do it is to do it with a single command, for example `install -m 0600 docker-compose.env.example docker-compose.env`).
There are two configurations to deploy OSEM with Docker: *evaluation mode* and *production mode*.
#### Evaluation mode
If you want to evaluate OSEM to see if it fits your needs, the default configuration in `docker-compose.env` will work perfectly fine for you.
For convenience reasons, `docker-compose.yml` already contains a [MailHog](https://github.com/mailhog/MailHog) service configuration. MailHog
is going to catch every email sent by OSEM and displays them on a special web service. Thus, it eliminitates the need to set up an SMTP server just to try out OSEM.
Just point your browser to http://localhost:8025 to get access to registration confirmation links etc.
Run `docker-compose up --build` to start the services. On first run, it will take a few minutes to initialize the database. Thus, wait a few minutes before you open up
http://localhost:9292 in your browser.
#### Production mode
To deploy OSEM for production, you have to make a few changes to `docker-compose.yml`. First, remove (or comment) the `mailhog` service, as it is only useful for evaluation and
cannot be used for production.
You can change the forwarded port from port `9292` to any other value if this port is already in use or you just want to use another one.
Next, you have to modify `docker-compose.env`. This file works as a Docker-like replacement for the regular Rails `.env` files described below.
You can configure any of the configuration values shown in the **Configure** section below in it. The most essential variables are already configured to standard
values in `docker-compose.env` which you most likely want to change.
First, you need to modify the email related settings. You need a working SMTP server for OSEM to send out registration confirmation mails etc.
For security reasons, the following variables have to be changed, too:
- `MYSQL_PASSWORD`
- `MYSQL_ROOT_PASSWORD`
- `SECRET_KEY_BASE`
These variables need to be set to the correct values at first, as they are used to initialize everything. Modification of these variables after installation and initialization is more
complicated and out of this document's scope.
As with any other Docker-Compose configuration, run `docker-compose up --build` (or `docker-compose up --build -d` to run in background) to start the services. During the first
start, the database has to be initialized which can take several minutes. The web service is by default exposed on localhost only as it is intended to be served by a reverse proxy (for SSL
termination, caching etc.).
You should not directly expose the web server port unless you have a good reason to do so.
## Configure ## Configure
There are a couple of environment variables you can set to configure OSEM. Check out the *dotenv.example* file. There are a couple of environment variables you can set to configure OSEM. Check out the *dotenv.example* file.
@ -43,7 +94,8 @@ There are a couple of environment variables you can set to configure OSEM. Check
| OSEM_FACEBOOK_SECRET | *string* | OMNIAUTH Developer Secret for Facebook | OSEM_FACEBOOK_SECRET | *string* | OMNIAUTH Developer Secret for Facebook
| OSEM_GITHUB_KEY | *string* | OMNIAUTH Developer Key for GitHub | OSEM_GITHUB_KEY | *string* | OMNIAUTH Developer Key for GitHub
| OSEM_GITHUB_SECRET | *string* | OMNIAUTH Developer Secret for GitHub | OSEM_GITHUB_SECRET | *string* | OMNIAUTH Developer Secret for GitHub
| OSEM_SCHEDULE_CELL_SIZE | *integer* | Schedule timeslot size to use (in minutes), should be greater than zero, should be divisor of 60 | OSEM_SUSE_KEY | *string* | OMNIAUTH Developer Key for openSUSE
| OSEM_SUSE_SECRET | *string* | OMNIAUTH Developer Secret for openSUSE
| OSEM_SMTP_ADDRESS | smtp.opensuse.org | The smtp server to use | OSEM_SMTP_ADDRESS | smtp.opensuse.org | The smtp server to use
| OSEM_SMTP_PORT | *int* | The port on the smtp server | OSEM_SMTP_PORT | *int* | The port on the smtp server
| OSEM_SMTP_USERNAME | *string* | The user for the smtp server | OSEM_SMTP_USERNAME | *string* | The user for the smtp server
@ -70,7 +122,7 @@ $ bundle exec rake logo:reprocess
``` ```
### openID ### openID
In order to use [openID](http://openid.net/) logins for your OSEM installation you need to register your application with the providers ([Google](https://code.google.com/apis/console#:access), [GitHub](https://github.com/settings/applications/new) or [Facebook](https://developers.facebook.com/)) and enter their API keys, changing the existing sample values, in `config/secrets.yml` file, or in your environment variables found in *.env.production* file. In order to use [openID](http://openid.net/) logins for your OSEM installation you need to register your application with the providers ([Google](https://code.google.com/apis/console#:access), [GitHub](https://github.com/settings/applications/new) or [Facebook](https://developers.facebook.com/)) and enter their API keys in the environment variables found in your *.env* file(s).
## Recurring Jobs ## Recurring Jobs
Open a separate terminal and go into the directory where the rails app is present, and type the following to start the delayed_jobs worker for sending email notifications. Open a separate terminal and go into the directory where the rails app is present, and type the following to start the delayed_jobs worker for sending email notifications.

2
Vagrantfile vendored
View file

@ -12,7 +12,7 @@ Vagrant.configure(2) do |config|
# Every Vagrant development environment requires a box. You can search for # Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search. # boxes at https://atlas.hashicorp.com/search.
config.vm.box = "opensuse/openSUSE-42.1-x86_64" config.vm.box = "opensuse/openSUSE-42.2-x86_64"
# Disable automatic box update checking. If you disable this, then # Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs # boxes will only be checked for updates when the user runs

View file

@ -54,10 +54,6 @@
"description": "The user for the smtp server", "description": "The user for the smtp server",
"required": false "required": false
}, },
"OSEM_SCHEDULE_CELL_SIZE": {
"description": "Schedule timeslot size in minutes",
"required": false
},
"RACK_ENV": { "RACK_ENV": {
"required": false "required": false
}, },

View file

@ -46,6 +46,7 @@
//= require unobtrusive_flash //= require unobtrusive_flash
//= require unobtrusive_flash_bootstrap //= require unobtrusive_flash_bootstrap
//= require countable //= require countable
//= require selectize
$(document).ready(function() { $(document).ready(function() {
$('a[disabled=disabled]').click(function(event){ $('a[disabled=disabled]').click(function(event){

View file

@ -16,4 +16,6 @@
*= require bootstrap3-switch *= require bootstrap3-switch
*= require osem-payments *= require osem-payments
*= require osem-navbar *= require osem-navbar
*= require selectize
*= require selectize.bootstrap3
*/ */

View file

@ -106,7 +106,11 @@ background-image: -webkit-gradient(
} }
.speakerinfo { .speakerinfo {
margin-top: 20px; margin-top: 40px;
}
.speakerbio {
margin-top: 10px;
} }
.schedule-title, .schedule-subtitle, .schedule-speaker, .schedule-track { .schedule-title, .schedule-subtitle, .schedule-speaker, .schedule-track {

View file

@ -2,15 +2,22 @@ module Admin
class BaseController < ApplicationController class BaseController < ApplicationController
before_filter :verify_user_admin before_filter :verify_user_admin
private
def current_ability
@current_ability ||= AdminAbility.new(current_user)
end
def verify_user_admin def verify_user_admin
if (current_user.nil?) if (current_user.nil?)
redirect_to sign_in_path redirect_to sign_in_path
return false return false
end end
unless (current_user.has_role? :organizer, :any) || (current_user.has_role? :cfp, :any) || unless (current_user.has_role? :organizer, :any) || (current_user.has_role? :cfp, :any) ||
(current_user.has_role? :info_desk, :any) || (current_user.has_role? :info_desk, :any) || (current_user.has_role? :organization_admin, :any) ||
(current_user.has_role? :volunteers_coordinator, :any) || current_user.is_admin (current_user.has_role? :volunteers_coordinator, :any) ||
raise CanCan::AccessDenied.new('You are not authorized to access this area!') (current_user.has_role? :track_organizer, :any) || current_user.is_admin
raise CanCan::AccessDenied.new('You are not authorized to access this page.')
end end
end end
end end

View file

@ -15,7 +15,7 @@ module Admin
redirect_to admin_conference_campaigns_path(conference_id: @conference.short_title), redirect_to admin_conference_campaigns_path(conference_id: @conference.short_title),
notice: 'Campaign successfully created.' notice: 'Campaign successfully created.'
else else
flash[:error] = 'Campaign creation failed. ' + @campaign.errors.full_messages.to_sentence flash.now[:error] = 'Campaign creation failed. ' + @campaign.errors.full_messages.to_sentence
render action: 'new' render action: 'new'
end end
end end
@ -29,7 +29,7 @@ module Admin
redirect_to admin_conference_campaigns_path(conference_id: @conference.short_title), redirect_to admin_conference_campaigns_path(conference_id: @conference.short_title),
notice: "Campaign '#{@campaign.name}' successfully updated." notice: "Campaign '#{@campaign.name}' successfully updated."
else else
flash[:error] = "Campaign update failed. #{@campaign.errors.full_messages.to_sentence}" flash.now[:error] = "Campaign update failed. #{@campaign.errors.full_messages.to_sentence}"
render action: 'edit' render action: 'edit'
end end
end end

View file

@ -2,51 +2,52 @@ module Admin
class CfpsController < Admin::BaseController class CfpsController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource :program, through: :conference, singleton: true load_and_authorize_resource :program, through: :conference, singleton: true
load_and_authorize_resource through: :program, singleton: true load_and_authorize_resource through: :program
def index; end
def show; end def show; end
def new def new
@cfp = @program.build_cfp @cfp = @program.cfps.new
end end
def edit; end def edit; end
def create def create
@cfp = @program.build_cfp(cfp_params) @cfp = @program.cfps.new(cfp_params)
send_mail_on_cfp_dates_updates = @cfp.notify_on_cfp_date_update? send_mail_on_cfp_dates_updates = @cfp.notify_on_cfp_date_update?
if @cfp.save if @cfp.save
ConferenceCfpUpdateMailJob.perform_later(@conference) if send_mail_on_cfp_dates_updates ConferenceCfpUpdateMailJob.perform_later(@conference) if send_mail_on_cfp_dates_updates
redirect_to admin_conference_program_cfp_path, redirect_to admin_conference_program_cfps_path,
notice: 'Call for papers successfully created.' notice: 'Call for papers successfully created.'
else else
flash[:error] = "Creating the call for papers failed. #{@cfp.errors.full_messages.join('. ')}." flash.now[:error] = "Creating the call for papers failed. #{@cfp.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
def update def update
@cfp = @program.cfp
@cfp.assign_attributes(cfp_params) @cfp.assign_attributes(cfp_params)
send_mail_on_cfp_dates_updates = @cfp.notify_on_cfp_date_update? send_mail_on_cfp_dates_updates = @cfp.notify_on_cfp_date_update?
if @cfp.update_attributes(cfp_params) if @cfp.update_attributes(cfp_params)
ConferenceCfpUpdateMailJob.perform_later(@conference) if send_mail_on_cfp_dates_updates ConferenceCfpUpdateMailJob.perform_later(@conference) if send_mail_on_cfp_dates_updates
redirect_to admin_conference_program_cfp_path(@conference.short_title), redirect_to admin_conference_program_cfps_path(@conference.short_title),
notice: 'Call for papers successfully updated.' notice: 'Call for papers successfully updated.'
else else
flash[:error] = "Updating call for papers failed. #{@cfp.errors.to_a.join('. ')}." flash.now[:error] = "Updating call for papers failed. #{@cfp.errors.to_a.join('. ')}."
render :new render :new
end end
end end
def destroy def destroy
if @cfp.destroy if @cfp.destroy
redirect_to admin_conference_program_cfp_path, notice: 'Call for Papers was successfully deleted.' redirect_to admin_conference_program_cfps_path, notice: 'Call for Papers was successfully deleted.'
else else
redirect_to admin_conference_program_cfp_path, error: 'An error prohibited this Call for Papers from being destroyed: '\ redirect_to admin_conference_program_cfps_path, error: 'An error prohibited this Call for Papers from being destroyed: '\
"#{@cfp.errors.full_messages.join('. ')}." "#{@cfp.errors.full_messages.join('. ')}."
end end
end end
@ -54,7 +55,7 @@ module Admin
private private
def cfp_params def cfp_params
params.require(:cfp).permit(:start_date, :end_date) params.require(:cfp).permit(:start_date, :end_date, :cfp_type)
end end
end end
end end

View file

@ -7,7 +7,6 @@ module Admin
@commercials = @conference.commercials @commercials = @conference.commercials
@commercial = @conference.commercials.build @commercial = @conference.commercials.build
authorize! :create, @conference.commercials.new
end end
def create def create

View file

@ -21,8 +21,8 @@ module Admin
@new_submissions = Event.where('created_at > ?', current_user.last_sign_in_at).count @new_submissions = Event.where('created_at > ?', current_user.last_sign_in_at).count
@active_conferences = Conference.get_active_conferences_for_dashboard # pending or the last two @active_conferences = Conference.get_active_conferences_for_dashboard # pending or the last two
@deactive_conferences = Conference. @deactive_conferences = Conference
get_conferences_without_active_for_dashboard(@active_conferences) # conferences without active .get_conferences_without_active_for_dashboard(@active_conferences) # conferences without active
@conferences = @active_conferences + @deactive_conferences @conferences = @active_conferences + @deactive_conferences
@recent_users = User.limit(5).order(created_at: :desc) @recent_users = User.limit(5).order(created_at: :desc)
@ -37,6 +37,9 @@ module Admin
@registrations = {} @registrations = {}
@registration_weeks = [0] @registration_weeks = [0]
@tickets = {}
@ticket_weeks = [0]
@conferences.each do |c| @conferences.each do |c|
# Event submissions over time chart # Event submissions over time chart
@submissions[c.short_title] = c.get_submissions_per_week @submissions[c.short_title] = c.get_submissions_per_week
@ -45,6 +48,10 @@ module Admin
# Conference registrations over time chart # Conference registrations over time chart
@registrations[c.short_title] = c.get_registrations_per_week @registrations[c.short_title] = c.get_registrations_per_week
@registration_weeks.push(@registrations[c.short_title].length) @registration_weeks.push(@registrations[c.short_title].length)
# Tickets sold over time chart
@tickets[c.short_title] = c.get_tickets_sold_per_week
@ticket_weeks.push(@tickets[c.short_title].length)
end end
@cfp_weeks = @cfp_weeks.max @cfp_weeks = @cfp_weeks.max
@ -55,6 +62,10 @@ module Admin
@registrations = normalize_array_length(@registrations, @registration_weeks) @registrations = normalize_array_length(@registrations, @registration_weeks)
@registration_weeks = @registration_weeks > 0 ? (1..@registration_weeks).to_a : 1 @registration_weeks = @registration_weeks > 0 ? (1..@registration_weeks).to_a : 1
@ticket_weeks = @ticket_weeks.max
@tickets = normalize_array_length(@tickets, @ticket_weeks)
@ticket_weeks = @ticket_weeks > 0 ? (1..@ticket_weeks).to_a : 1
@event_distribution = Conference.event_distribution @event_distribution = Conference.event_distribution
@user_distribution = Conference.user_distribution @user_distribution = Conference.user_distribution
end end
@ -65,7 +76,7 @@ module Admin
def create def create
@conference = Conference.new(conference_params) @conference = Conference.new(conference_params)
@conference.organization = Organization.find_or_create_by(name: 'organization')
if @conference.save if @conference.save
# user that creates the conference becomes organizer of that conference # user that creates the conference becomes organizer of that conference
current_user.add_role :organizer, @conference current_user.add_role :organizer, @conference
@ -73,7 +84,7 @@ module Admin
redirect_to admin_conference_path(id: @conference.short_title), redirect_to admin_conference_path(id: @conference.short_title),
notice: 'Conference was successfully created.' notice: 'Conference was successfully created.'
else else
flash[:error] = 'Could not create conference. ' + @conference.errors.full_messages.to_sentence flash.now[:error] = 'Could not create conference. ' + @conference.errors.full_messages.to_sentence
render action: 'new' render action: 'new'
end end
end end
@ -82,7 +93,6 @@ module Admin
short_title = @conference.short_title short_title = @conference.short_title
@conference.assign_attributes(conference_params) @conference.assign_attributes(conference_params)
send_mail_on_conf_update = @conference.notify_on_dates_changed? send_mail_on_conf_update = @conference.notify_on_dates_changed?
delete_event_schedules if @conference.start_hour_changed? || @conference.end_hour_changed?
if @conference.update_attributes(conference_params) if @conference.update_attributes(conference_params)
ConferenceDateUpdateMailJob.perform_later(@conference) if send_mail_on_conf_update ConferenceDateUpdateMailJob.perform_later(@conference) if send_mail_on_conf_update
@ -106,8 +116,8 @@ module Admin
@new_reg = @conference.registrations.where('created_at > ?', current_user.last_sign_in_at).count @new_reg = @conference.registrations.where('created_at > ?', current_user.last_sign_in_at).count
@total_submissions = @program.events.count @total_submissions = @program.events.count
@new_submissions = @program.events. @new_submissions = @program.events
where('created_at > ?', current_user.last_sign_in_at).count .where('created_at > ?', current_user.last_sign_in_at).count
@program_length = @conference.current_program_hours @program_length = @conference.current_program_hours
@new_program_length = @conference.new_program_hours(current_user.last_sign_in_at) @new_program_length = @conference.new_program_hours(current_user.last_sign_in_at)
@ -134,13 +144,27 @@ module Admin
@submissions_data = @submissions_data.except('Weeks') @submissions_data = @submissions_data.except('Weeks')
end end
@tickets_data = {}
@tickets_data = @conference.get_tickets_data
@ticket_weeks = 0
if @tickets_data['Weeks']
@ticket_weeks = @tickets_data['Weeks']
@tickets_data = @tickets_data.except('Weeks')
end
# Set line color using a hash function
@tickets = []
@tickets_data.keys.each do |title|
@tickets.append(short_title: title, color: "\##{Digest::MD5.hexdigest(title)[0..5]}")
end
# Doughnut charts # Doughnut charts
@event_type_distribution = @conference.event_type_distribution @event_type_distribution = @conference.event_type_distribution
@event_type_distribution_confirmed = @conference.event_type_distribution(:confirmed) @event_type_distribution_confirmed = @conference.event_type_distribution(:confirmed)
@difficulty_levels_distribution = @conference.difficulty_levels_distribution @difficulty_levels_distribution = @conference.difficulty_levels_distribution
@difficulty_levels_distribution_confirmed = @conference. @difficulty_levels_distribution_confirmed = @conference
difficulty_levels_distribution(:confirmed) .difficulty_levels_distribution(:confirmed)
@tracks_distribution = @conference.tracks_distribution @tracks_distribution = @conference.tracks_distribution
@tracks_distribution_confirmed = @conference.tracks_distribution(:confirmed) @tracks_distribution_confirmed = @conference.tracks_distribution(:confirmed)
@ -187,16 +211,7 @@ module Admin
:vpositions_attributes, :use_volunteers, :color, :vpositions_attributes, :use_volunteers, :color,
:sponsorship_levels_attributes, :sponsors_attributes, :sponsorship_levels_attributes, :sponsors_attributes,
:targets, :targets_attributes, :targets, :targets_attributes,
:campaigns, :campaigns_attributes, :registration_limit) :campaigns, :campaigns_attributes, :registration_limit, :organization_id, :ticket_layout)
end
def delete_event_schedules
event_schedules = EventSchedule.select do |e|
e.start_time.strftime('%H').to_i < @conference.start_hour ||
e.end_time.strftime('%H').to_i > @conference.end_hour ||
(e.end_time.strftime('%H').to_i == @conference.end_hour && e.end_time.strftime('%M').to_i > 0)
end
event_schedules.each(&:destroy)
end end
end end
end end

View file

@ -20,7 +20,7 @@ module Admin
redirect_to admin_conference_program_difficulty_levels_path(conference_id: @conference.short_title), redirect_to admin_conference_program_difficulty_levels_path(conference_id: @conference.short_title),
notice: 'Difficulty level successfully created.' notice: 'Difficulty level successfully created.'
else else
flash[:error] = "Creating difficulty level failed: #{@difficulty_level.errors.full_messages.join('. ')}." flash.now[:error] = "Creating difficulty level failed: #{@difficulty_level.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -30,7 +30,7 @@ module Admin
redirect_to admin_conference_program_difficulty_levels_path(conference_id: @conference.short_title), redirect_to admin_conference_program_difficulty_levels_path(conference_id: @conference.short_title),
notice: 'Difficulty level successfully updated.' notice: 'Difficulty level successfully updated.'
else else
flash[:error] = "Update difficulty level failed: #{@difficulty_level.errors.full_messages.join('. ')}." flash.now[:error] = "Update difficulty level failed: #{@difficulty_level.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -18,7 +18,7 @@ module Admin
redirect_to admin_conference_program_event_types_path(conference_id: @conference.short_title), redirect_to admin_conference_program_event_types_path(conference_id: @conference.short_title),
notice: 'Event type successfully created.' notice: 'Event type successfully created.'
else else
flash[:error] = "Creating event type failed: #{@event_type.errors.full_messages.join('. ')}." flash.now[:error] = "Creating event type failed: #{@event_type.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -28,7 +28,7 @@ module Admin
redirect_to admin_conference_program_event_types_path(conference_id: @conference.short_title), redirect_to admin_conference_program_event_types_path(conference_id: @conference.short_title),
notice: 'Event type successfully updated.' notice: 'Event type successfully updated.'
else else
flash[:error] = "Update event type failed: #{@event_type.errors.full_messages.join('. ')}." flash.now[:error] = "Update event type failed: #{@event_type.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -5,7 +5,7 @@ module Admin
load_and_authorize_resource :event, through: :program load_and_authorize_resource :event, through: :program
load_and_authorize_resource :events_registration, only: :toggle_attendance load_and_authorize_resource :events_registration, only: :toggle_attendance
before_action :get_event, except: [:index, :create] before_action :get_event, except: [:index, :create, :new]
# FIXME: The timezome should only be applied on output, otherwise # FIXME: The timezome should only be applied on output, otherwise
# you get lost in timezone conversions... # you get lost in timezone conversions...
@ -50,8 +50,8 @@ module Admin
@ratings = @event.votes.includes(:user) @ratings = @event.votes.includes(:user)
@difficulty_levels = @program.difficulty_levels @difficulty_levels = @program.difficulty_levels
@versions = @event.versions | @versions = @event.versions |
PaperTrail::Version.where(item_type: 'Commercial').where_object(commercialable_id: @event.id, commercialable_type: 'Event') | PaperTrail::Version.where(item_type: 'Commercial').where('object LIKE ?', "%commercialable_id: #{@event.id}\ncommercialable_type: Event%") |
PaperTrail::Version.where(item_type: 'Commercial').where_object_changes(commercialable_id: @event.id, commercialable_type: 'Event') | PaperTrail::Version.where(item_type: 'Commercial').where('object_changes LIKE ?', "%commercialable_id:\n- \n- #{@event.id}\ncommercialable_type:\n- \n- Event%") |
PaperTrail::Version.where(item_type: 'Vote').where('object_changes LIKE ?', "%\nevent_id:\n- \n- #{@event.id}\n%") | PaperTrail::Version.where(item_type: 'Vote').where('object_changes LIKE ?', "%\nevent_id:\n- \n- #{@event.id}\n%") |
PaperTrail::Version.where(item_type: 'Vote').where('object LIKE ?', "%\nevent_id: #{@event.id}\n%") PaperTrail::Version.where(item_type: 'Vote').where('object LIKE ?', "%\nevent_id: #{@event.id}\n%")
end end
@ -79,6 +79,7 @@ module Admin
end end
def update def update
@languages = @program.languages_list
if @event.update_attributes(event_params) if @event.update_attributes(event_params)
if request.xhr? if request.xhr?
@ -89,12 +90,29 @@ module Admin
end end
else else
@url = admin_conference_program_event_path(@conference.short_title, @event) @url = admin_conference_program_event_path(@conference.short_title, @event)
flash[:error] = 'Update not successful. ' + @event.errors.full_messages.to_sentence flash.now[:error] = 'Update not successful. ' + @event.errors.full_messages.to_sentence
render :edit render :edit
end end
end end
def create; end def create
@url = admin_conference_program_events_path(@conference.short_title, @event)
@languages = @program.languages_list
@event.submitter = current_user
if @event.save
ahoy.track 'Event submission', title: 'New submission'
redirect_to admin_conference_program_events_path(@conference.short_title), notice: 'Event was successfully submitted.'
else
flash[:error] = "Could not submit proposal: #{@event.errors.full_messages.join(', ')}"
render action: 'new'
end
end
def new
@url = admin_conference_program_events_path(@conference.short_title, @event)
@languages = @program.languages_list
end
def accept def accept
send_mail = @event.program.conference.email_settings.send_on_accepted send_mail = @event.program.conference.email_settings.send_on_accepted
@ -161,7 +179,8 @@ module Admin
# Set only in admin/events controller # Set only in admin/events controller
:track_id, :state, :language, :is_highlight, :max_attendees, :track_id, :state, :language, :is_highlight, :max_attendees,
# Not used anymore? # Not used anymore?
:proposal_additional_speakers, :user, :users_attributes) :proposal_additional_speakers, :user, :users_attributes,
speaker_ids: [])
end end
def comment_params def comment_params

View file

@ -16,7 +16,7 @@ module Admin
redirect_to admin_conference_lodgings_path(conference_id: @conference.short_title), redirect_to admin_conference_lodgings_path(conference_id: @conference.short_title),
notice: 'Lodging successfully created.' notice: 'Lodging successfully created.'
else else
flash[:error] = "Creating Lodging failed: #{@lodging.errors.full_messages.join('. ')}." flash.now[:error] = "Creating Lodging failed: #{@lodging.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -28,7 +28,7 @@ module Admin
redirect_to admin_conference_lodgings_path(conference_id: @conference.short_title), redirect_to admin_conference_lodgings_path(conference_id: @conference.short_title),
notice: 'Lodging successfully updated.' notice: 'Lodging successfully updated.'
else else
flash[:error] = "Update Lodging failed: #{@lodging.errors.full_messages.join('. ')}." flash.now[:error] = "Update Lodging failed: #{@lodging.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -0,0 +1,52 @@
module Admin
class OrganizationsController < Admin::BaseController
load_and_authorize_resource :organization
def index
@organizations = Organization.all
end
def create
@organization = Organization.new(organization_params)
if @organization.save
redirect_to admin_organizations_path,
notice: 'Organization successfully created'
else
redirect_to new_admin_organization_path,
error: @organization.errors.full_messages.join(', ')
end
end
def new
@organization = Organization.new
end
def edit; end
def update
if @organization.update_attributes(organization_params)
redirect_to admin_organizations_path,
notice: 'Organization successfully updated'
else
redirect_to edit_admin_organization_path(@organization),
error: @organization.errors.full_messages.join(', ')
end
end
def destroy
if @organization.destroy
redirect_to admin_organizations_path,
notice: 'Organization successfully destroyed'
else
redirect_to admin_organizations_path,
error: 'Organization cannot be destroyed'
end
end
private
def organization_params
params.require(:organization).permit(:name, :description, :picture)
end
end
end

View file

@ -0,0 +1,14 @@
module Admin
class PhysicalTicketController < Admin::BaseController
before_action :authenticate_user!
load_resource :conference, find_by: :short_title
load_and_authorize_resource
authorize_resource :conference_registrations, class: Registration
def index
@physical_tickets = @conference.physical_tickets
@tickets_sold_distribution = @conference.tickets_sold_distribution
@tickets_turnover_distribution = @conference.tickets_turnover_distribution
end
end
end

View file

@ -12,20 +12,22 @@ module Admin
@program = @conference.program @program = @conference.program
@program.assign_attributes(program_params) @program.assign_attributes(program_params)
send_mail_on_schedule_public = @program.notify_on_schedule_public? send_mail_on_schedule_public = @program.notify_on_schedule_public?
event_schedules_count_was = @program.event_schedules.count
if @program.update_attributes(program_params) if @program.update_attributes(program_params)
ConferenceScheduleUpdateMailJob.perform_later(@conference) if send_mail_on_schedule_public ConferenceScheduleUpdateMailJob.perform_later(@conference) if send_mail_on_schedule_public
respond_to do |format| respond_to do |format|
format.html do format.html do
redirect_to admin_conference_program_path(@conference.short_title), notice = 'The program was successfully updated.'
notice: 'The program was successfully updated.' notice += ' You changed schedule interval and some events were unscheduled.' if @program.event_schedules.count != event_schedules_count_was
redirect_to admin_conference_program_path(@conference.short_title), notice: notice
end end
format.js { render json: {} } format.js { render json: {} }
end end
else else
respond_to do |format| respond_to do |format|
format.html do format.html do
flash[:error] = "Updating program failed. #{@program.errors.to_a.join('. ')}." flash.now[:error] = "Updating program failed. #{@program.errors.to_a.join('. ')}."
render :new render :new
end end
format.js { render json: { errors: "The selected schedule couldn't been updated #{@program.errors.to_a.join('. ')}" }, status: 422 } format.js { render json: { errors: "The selected schedule couldn't been updated #{@program.errors.to_a.join('. ')}" }, status: 422 }
@ -36,7 +38,7 @@ module Admin
private private
def program_params def program_params
params.require(:program).permit(:rating, :schedule_public, :schedule_fluid, :languages, :blind_voting, :voting_start_date, :voting_end_date, :selected_schedule_id) params.require(:program).permit(:rating, :schedule_public, :schedule_interval, :schedule_fluid, :languages, :blind_voting, :voting_start_date, :voting_end_date, :selected_schedule_id)
end end
end end
end end

View file

@ -1,5 +1,5 @@
module Admin module Admin
class RegistrationPeriodsController < ApplicationController class RegistrationPeriodsController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource through: :conference, singleton: true load_and_authorize_resource through: :conference, singleton: true
@ -16,7 +16,7 @@ module Admin
redirect_to admin_conference_registration_period_path(@conference.short_title), redirect_to admin_conference_registration_period_path(@conference.short_title),
notice: 'Registration Period successfully updated.' notice: 'Registration Period successfully updated.'
else else
flash[:error] = "An error prohibited the Registration Period from being saved: #{@registration_period.errors.full_messages.join('. ')}." flash.now[:error] = "An error prohibited the Registration Period from being saved: #{@registration_period.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -36,7 +36,7 @@ module Admin
redirect_to admin_conference_registration_period_path(@conference.short_title), redirect_to admin_conference_registration_period_path(@conference.short_title),
notice: 'Registration Period successfully updated.' notice: 'Registration Period successfully updated.'
else else
flash[:error] = 'An error prohibited the Registration Period from being saved: ' \ flash.now[:error] = 'An error prohibited the Registration Period from being saved: ' \
"#{@registration_period.errors.full_messages.join('. ')}." "#{@registration_period.errors.full_messages.join('. ')}."
render :edit render :edit
end end

View file

@ -24,7 +24,7 @@ module Admin
redirect_to admin_conference_registrations_path(@conference.short_title), redirect_to admin_conference_registrations_path(@conference.short_title),
notice: "Successfully updated registration for #{@registration.user.email}!" notice: "Successfully updated registration for #{@registration.user.email}!"
else else
flash[:error] = "An error prohibited the Registration for #{@registration.user.email}: "\ flash.now[:error] = "An error prohibited the Registration for #{@registration.user.email}: "\
"#{@registration.errors.full_messages.join('. ')}." "#{@registration.errors.full_messages.join('. ')}."
render :edit render :edit
end end

View file

@ -10,10 +10,10 @@ module Admin
@events_with_requirements = @events.where.not(description: ['', nil]) @events_with_requirements = @events.where.not(description: ['', nil])
attended_registrants_ids = @conference.registrations.where(attended: true).pluck(:user_id) attended_registrants_ids = @conference.registrations.where(attended: true).pluck(:user_id)
@missing_event_speakers = EventUser.joins(:event). @missing_event_speakers = EventUser.joins(:event)
where('event_role = ? and program_id = ?', 'submitter', @program.id). .where('event_role = ? and program_id = ?', 'submitter', @program.id)
where.not(user_id: attended_registrants_ids). .where.not(user_id: attended_registrants_ids)
includes(:user, :event) .includes(:user, :event)
end end
end end
end end

View file

@ -1,5 +1,5 @@
module Admin module Admin
class ResourcesController < ApplicationController class ResourcesController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource :resource, only: [:show, :edit, :update, :destroy] load_and_authorize_resource :resource, only: [:show, :edit, :update, :destroy]
@ -17,7 +17,7 @@ module Admin
redirect_to admin_conference_resources_path(conference_id: @conference.short_title), redirect_to admin_conference_resources_path(conference_id: @conference.short_title),
notice: 'Resource successfully created.' notice: 'Resource successfully created.'
else else
flash[:error] = "Creating resource failed: #{@resource.errors.full_messages.join('. ')}." flash.now[:error] = "Creating resource failed: #{@resource.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -27,7 +27,7 @@ module Admin
redirect_to admin_conference_resources_path(conference_id: @conference.short_title), redirect_to admin_conference_resources_path(conference_id: @conference.short_title),
notice: 'Resource successfully updated.' notice: 'Resource successfully updated.'
else else
flash[:error] = "Resource update failed: #{@resource.errors.full_messages.join('. ')}." flash.now[:error] = "Resource update failed: #{@resource.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -8,14 +8,26 @@ module Admin
def index def index
@roles = Role.where(resource: @conference) @roles = Role.where(resource: @conference)
tracks = @conference.program.tracks.where.not(submitter: nil)
@roles += Role.where(resource: tracks)
authorize! :index, @role authorize! :index, @role
end end
def show def show
@url = if @track
toggle_user_track_admin_conference_role_path(@conference.short_title, @role.name, @track)
else
toggle_user_admin_conference_role_path(@conference.short_title, @role.name)
end
@users = @role.users @users = @role.users
end end
def edit def edit
@url = if @track
track_admin_conference_role_path(@conference.short_title, @role.name, @track)
else
admin_conference_role_path(@conference.short_title, @role.name)
end
@users = @role.users @users = @role.users
end end
@ -23,11 +35,17 @@ module Admin
role_name = @role.name role_name = @role.name
if @role.update_attributes(role_params) if @role.update_attributes(role_params)
redirect_to admin_conference_role_path(@conference.short_title, @role.name), url = if @track
track_admin_conference_role_path(@conference.short_title, @role.name, @track)
else
admin_conference_role_path(@conference.short_title, @role.name)
end
redirect_to url,
notice: 'Successfully updated role ' + @role.name notice: 'Successfully updated role ' + @role.name
else else
@role.name = role_name @role.name = role_name
flash[:error] = 'Could not update role! ' + @role.errors.full_messages.to_sentence flash.now[:error] = 'Could not update role! ' + @role.errors.full_messages.to_sentence
render :edit render :edit
end end
end end
@ -36,8 +54,14 @@ module Admin
user = User.find_by(email: user_params[:email]) user = User.find_by(email: user_params[:email])
state = user_params[:state] state = user_params[:state]
url = if @track
track_admin_conference_role_path(@conference.short_title, @role.name, @track)
else
admin_conference_role_path(@conference.short_title, @role.name)
end
unless user unless user
redirect_to admin_conference_role_path(@conference.short_title, @role.name), redirect_to url,
error: 'Could not find user. Please provide a valid email!' error: 'Could not find user. Please provide a valid email!'
return return
end end
@ -49,17 +73,23 @@ module Admin
return return
end end
if @role.resource_type == 'Conference'
role_resource = @conference
elsif @role.resource_type == 'Track'
role_resource = @track
end
# Remove user # Remove user
if state == 'false' if state == 'false'
if user.remove_role @role.name, @conference if user.remove_role @role.name, role_resource
flash[:notice] = "Successfully removed role #{@role.name} from user #{user.email}" flash[:notice] = "Successfully removed role #{@role.name} from user #{user.email}"
else else
flash[:error] = "Could not remove role #{@role.name} from user #{user.email}" flash[:error] = "Could not remove role #{@role.name} from user #{user.email}"
end end
elsif user.has_role? @role.name, @conference elsif user.has_role? @role.name, role_resource
flash[:error] = "User #{user.email} already has the role #{@role.name}" flash[:error] = "User #{user.email} already has the role #{@role.name}"
# Add user # Add user
elsif user.add_role @role.name, @conference elsif user.add_role @role.name, role_resource
flash[:notice] = "Successfully added role #{@role.name} to user #{user.email}" flash[:notice] = "Successfully added role #{@role.name} to user #{user.email}"
else else
flash[:error] = "Coud not add role #{@role.name} to #{user.email}" flash[:error] = "Coud not add role #{@role.name} to #{user.email}"
@ -67,7 +97,7 @@ module Admin
respond_to do |format| respond_to do |format|
format.js format.js
format.html { redirect_to admin_conference_role_path(@conference.short_title, @role.name) } format.html { redirect_to url }
end end
end end
@ -77,8 +107,13 @@ module Admin
# Set 'organizer' as default role, when there is no other selection # Set 'organizer' as default role, when there is no other selection
@selection = params[:id] ? params[:id].parameterize.underscore : 'organizer' @selection = params[:id] ? params[:id].parameterize.underscore : 'organizer'
if @selection == 'track_organizer'
@track = @conference.program.tracks.find_by(short_name: params[:track_name])
@role = Role.find_by(name: @selection, resource: @track)
else
@role = Role.find_by(name: @selection, resource: @conference) @role = Role.find_by(name: @selection, resource: @conference)
end end
end
def role_params def role_params
params.require(:role).permit(:name, :description, user_ids: []) params.require(:role).permit(:name, :description, user_ids: [])

View file

@ -18,7 +18,7 @@ module Admin
redirect_to admin_conference_venue_rooms_path(conference_id: @conference.short_title), redirect_to admin_conference_venue_rooms_path(conference_id: @conference.short_title),
notice: 'Room successfully created.' notice: 'Room successfully created.'
else else
flash[:error] = "Creating Room failed: #{@room.errors.full_messages.join('. ')}." flash.now[:error] = "Creating Room failed: #{@room.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -28,7 +28,7 @@ module Admin
redirect_to admin_conference_venue_rooms_path(conference_id: @conference.short_title), redirect_to admin_conference_venue_rooms_path(conference_id: @conference.short_title),
notice: 'Room successfully updated.' notice: 'Room successfully updated.'
else else
flash[:error] = "Update Room failed: #{@room.errors.full_messages.join('. ')}." flash.now[:error] = "Update Room failed: #{@room.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -20,7 +20,7 @@ module Admin
redirect_to admin_conference_sponsors_path(conference_id: @conference.short_title), redirect_to admin_conference_sponsors_path(conference_id: @conference.short_title),
notice: 'Sponsor successfully created.' notice: 'Sponsor successfully created.'
else else
flash[:error] = "Creating sponsor failed: #{@sponsor.errors.full_messages.join('. ')}." flash.now[:error] = "Creating sponsor failed: #{@sponsor.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -31,7 +31,7 @@ module Admin
conference_id: @conference.short_title), conference_id: @conference.short_title),
notice: 'Sponsor successfully updated.' notice: 'Sponsor successfully updated.'
else else
flash[:error] = "Update sponsor failed: #{@sponsor.errors.full_messages.join('. ')}." flash.now[:error] = "Update sponsor failed: #{@sponsor.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -19,7 +19,7 @@ module Admin
redirect_to admin_conference_sponsorship_levels_path(conference_id: @conference.short_title), redirect_to admin_conference_sponsorship_levels_path(conference_id: @conference.short_title),
notice: 'Sponsorship level successfully created.' notice: 'Sponsorship level successfully created.'
else else
flash[:error] = "Creating Sponsorship Level failed: #{@sponsorship_level.errors.full_messages.join('. ')}." flash.now[:error] = "Creating Sponsorship Level failed: #{@sponsorship_level.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -30,7 +30,7 @@ module Admin
conference_id: @conference.short_title), conference_id: @conference.short_title),
notice: 'Sponsorship level successfully updated.' notice: 'Sponsorship level successfully updated.'
else else
flash[:error] = "Update Sponsorship level failed: #{@sponsorship_level.errors.full_messages.join('. ')}." flash.now[:error] = "Update Sponsorship level failed: #{@sponsorship_level.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -15,7 +15,7 @@ module Admin
redirect_to admin_conference_targets_path(conference_id: @conference.short_title), redirect_to admin_conference_targets_path(conference_id: @conference.short_title),
notice: 'Target successfully created.' notice: 'Target successfully created.'
else else
flash[:error] = "Creating target failed: #{@target.errors.full_messages.join('. ')}." flash.now[:error] = "Creating target failed: #{@target.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -27,7 +27,7 @@ module Admin
redirect_to admin_conference_targets_path(conference_id: @conference.short_title), redirect_to admin_conference_targets_path(conference_id: @conference.short_title),
notice: 'Target successfully updated.' notice: 'Target successfully updated.'
else else
flash[:error] = "Target update failed: #{@target.errors.full_messages.join('. ')}." flash.now[:error] = "Target update failed: #{@target.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -5,6 +5,8 @@ module Admin
def index def index
authorize! :update, Ticket.new(conference_id: @conference.id) authorize! :update, Ticket.new(conference_id: @conference.id)
@tickets_sold_distribution = @conference.tickets_sold_distribution
@tickets_turnover_distribution = @conference.tickets_turnover_distribution
end end
def new def new
@ -17,7 +19,7 @@ module Admin
redirect_to admin_conference_tickets_path(conference_id: @conference.short_title), redirect_to admin_conference_tickets_path(conference_id: @conference.short_title),
notice: 'Ticket successfully created.' notice: 'Ticket successfully created.'
else else
flash[:error] = "Creating Ticket failed: #{@ticket.errors.full_messages.join('. ')}." flash.now[:error] = "Creating Ticket failed: #{@ticket.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -29,7 +31,7 @@ module Admin
redirect_to admin_conference_tickets_path(conference_id: @conference.short_title), redirect_to admin_conference_tickets_path(conference_id: @conference.short_title),
notice: 'Ticket successfully updated.' notice: 'Ticket successfully updated.'
else else
flash[:error] = "Ticket update failed: #{@ticket.errors.full_messages.join('. ')}." flash.now[:error] = "Ticket update failed: #{@ticket.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -2,7 +2,7 @@ module Admin
class TracksController < Admin::BaseController class TracksController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource :program, through: :conference, singleton: true load_and_authorize_resource :program, through: :conference, singleton: true
load_and_authorize_resource through: :program load_and_authorize_resource through: :program, find_by: :short_name
def index; end def index; end
@ -23,7 +23,7 @@ module Admin
redirect_to admin_conference_program_tracks_path(conference_id: @conference.short_title), redirect_to admin_conference_program_tracks_path(conference_id: @conference.short_title),
notice: 'Track successfully created.' notice: 'Track successfully created.'
else else
flash[:error] = "Creating Track failed: #{@track.errors.full_messages.join('. ')}." flash.now[:error] = "Creating Track failed: #{@track.errors.full_messages.join('. ')}."
render :new render :new
end end
end end
@ -35,7 +35,7 @@ module Admin
redirect_to admin_conference_program_tracks_path(conference_id: @conference.short_title), redirect_to admin_conference_program_tracks_path(conference_id: @conference.short_title),
notice: 'Track successfully updated.' notice: 'Track successfully updated.'
else else
flash[:error] = "Track update failed: #{@track.errors.full_messages.join('. ')}." flash.now[:error] = "Track update failed: #{@track.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end
@ -50,10 +50,19 @@ module Admin
end end
end end
def toggle_cfp_inclusion
@track.cfp_active = !@track.cfp_active
if @track.save
head :ok
else
head :unprocessable_entity
end
end
private private
def track_params def track_params
params.require(:track).permit(:name, :description, :color) params.require(:track).permit(:name, :description, :color, :short_name, :cfp_active)
end end
end end
end end

View file

@ -6,6 +6,17 @@ module Admin
@user = User.new @user = User.new
end end
def create
@user = User.new(user_params)
@user.skip_confirmation!
if @user.save
redirect_to admin_users_path, notice: 'User successfully created.'
else
flash.now[:error] = "Creating User failed: #{@user.errors.full_messages.join('. ')}."
render :new
end
end
def index def index
@users = User.all @users = User.all
end end
@ -50,8 +61,9 @@ module Admin
private private
def user_params def user_params
params.require(:user).permit(:email, :name, :email_public, :biography, :nickname, :affiliation, :is_admin, :username, :login, :is_disabled, params.require(:user).permit(:email, :name, :email_public, :biography, :nickname, :affiliation, :is_admin,
:tshirt, :mobile, :volunteer_experience, :languages, :to_confirm, role_ids: []) :username, :login, :is_disabled, :tshirt, :mobile, :volunteer_experience,
:languages, :to_confirm, :password, role_ids: [])
end end
end end
end end

View file

@ -27,7 +27,7 @@ module Admin
redirect_to admin_conference_venue_path(conference_id: @conference.short_title), redirect_to admin_conference_venue_path(conference_id: @conference.short_title),
notice: 'Venue was successfully updated.' notice: 'Venue was successfully updated.'
else else
flash[:error] = "Update venue failed: #{@venue.errors.full_messages.join('. ')}." flash.now[:error] = "Update venue failed: #{@venue.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -1,9 +1,10 @@
module Admin module Admin
class VolunteersController < Admin::BaseController class VolunteersController < Admin::BaseController
include VolunteersHelper
load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :conference, find_by: :short_title
def index def index
if can_manage_volunteers(@conference) if can_manage_volunteers?(@conference)
render :index render :index
else else
authorize! :index, :volunteer authorize! :index, :volunteer
@ -11,11 +12,11 @@ module Admin
end end
def show def show
if can_manage_volunteers(@conference) if can_manage_volunteers?(@conference)
if @conference.use_vpositions @volunteers = if @conference.use_vpositions
@volunteers = @conference.registrations.joins(:vchoices).uniq @conference.registrations.joins(:vchoices).uniq
else else
@volunteers = @conference.registrations.where(volunteer: true) @conference.registrations.where(volunteer: true)
end end
else else
authorize! :index, :volunteer authorize! :index, :volunteer

View file

@ -38,11 +38,11 @@ class ConferenceRegistrationsController < ApplicationController
def create def create
@registration = @conference.registrations.new(registration_params) @registration = @conference.registrations.new(registration_params)
if current_user.nil? @user = if current_user.nil?
# @user variable needs to be set so that _sign_up_form_embedded works properly # @user variable needs to be set so that _sign_up_form_embedded works properly
@user = @registration.build_user(user_params) @registration.build_user(user_params)
else else
@user = current_user current_user
end end
@registration.user = @user @registration.user = @user
@ -65,7 +65,7 @@ class ConferenceRegistrationsController < ApplicationController
notice: 'You are now registered and will be receiving E-Mail notifications.' notice: 'You are now registered and will be receiving E-Mail notifications.'
end end
else else
flash[:error] = "Could not create your registration for #{@conference.title}: "\ flash.now[:error] = "Could not create your registration for #{@conference.title}: "\
"#{@registration.errors.full_messages.join('. ')}." "#{@registration.errors.full_messages.join('. ')}."
render :new render :new
end end
@ -76,7 +76,7 @@ class ConferenceRegistrationsController < ApplicationController
redirect_to conference_conference_registration_path(@conference.short_title), redirect_to conference_conference_registration_path(@conference.short_title),
notice: 'Registration was successfully updated.' notice: 'Registration was successfully updated.'
else else
flash[:error] = "Could not update your registration for #{@conference.title}: "\ flash.now[:error] = "Could not update your registration for #{@conference.title}: "\
"#{@registration.errors.full_messages.join('. ')}." "#{@registration.errors.full_messages.join('. ')}."
render :edit render :edit
end end
@ -108,8 +108,8 @@ class ConferenceRegistrationsController < ApplicationController
end end
def registration_params def registration_params
params.require(:registration). params.require(:registration)
permit( .permit(
:conference_id, :arrival, :departure, :conference_id, :arrival, :departure,
:volunteer, :volunteer,
vchoice_ids: [], qanswer_ids: [], vchoice_ids: [], qanswer_ids: [],

View file

@ -0,0 +1,9 @@
class OpenidsController < ApplicationController
load_and_authorize_resource :user
load_and_authorize_resource through: :user
def destroy
@openid.destroy
redirect_to :back
end
end

View file

@ -0,0 +1,7 @@
class OrganizationsController < ApplicationController
load_and_authorize_resource :organization
def index
@organizations = Organization.all
end
end

View file

@ -18,12 +18,12 @@ class PaymentsController < ApplicationController
if @payment.purchase && @payment.save if @payment.purchase && @payment.save
update_purchased_ticket_purchases update_purchased_ticket_purchases
redirect_to conference_conference_registration_path(@conference.short_title), redirect_to conference_physical_ticket_index_path,
notice: 'Thanks! Your ticket is booked successfully.' notice: 'Thanks! Your ticket is booked successfully.'
else else
@total_amount_to_pay = Ticket.total_price(@conference, current_user, paid: false) @total_amount_to_pay = Ticket.total_price(@conference, current_user, paid: false)
@unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference) @unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference)
flash[:error] = @payment.errors.full_messages.to_sentence + ' Please try again with correct credentials.' flash.now[:error] = @payment.errors.full_messages.to_sentence + ' Please try again with correct credentials.'
render :new render :new
end end
end end
@ -38,6 +38,8 @@ class PaymentsController < ApplicationController
end end
def update_purchased_ticket_purchases def update_purchased_ticket_purchases
current_user.ticket_purchases.by_conference(@conference).unpaid.update_all(paid: true, payment_id: @payment.id) current_user.ticket_purchases.by_conference(@conference).unpaid.each do |ticket_purchase|
ticket_purchase.pay(@payment)
end
end end
end end

View file

@ -0,0 +1,17 @@
class PhysicalTicketController < ApplicationController
before_action :authenticate_user!
load_resource :conference, find_by: :short_title
load_and_authorize_resource
authorize_resource :conference_registrations, class: Registration
def index
@physical_tickets = current_user.physical_tickets.by_conference(@conference)
@unpaid_ticket_purchases = current_user.ticket_purchases.by_conference(@conference).unpaid
end
def show
@file_name = "ticket_for_#{@conference.short_title}"
@user = @physical_ticket.user
@ticket_layout = @conference.ticket_layout.to_sym
end
end

View file

@ -13,9 +13,8 @@ class ProposalsController < ApplicationController
end end
def show def show
# FIXME: We should show more than the first speaker
@speaker = @event.speakers.first || @event.submitter
@event_schedule = @event.event_schedules.find_by(schedule_id: @program.selected_schedule_id) @event_schedule = @event.event_schedules.find_by(schedule_id: @program.selected_schedule_id)
@speakers_ordered = @event.speakers_ordered
end end
def new def new
@ -40,7 +39,7 @@ class ProposalsController < ApplicationController
if @user.save if @user.save
sign_in(@user) sign_in(@user)
else else
flash[:error] = "Could not save user: #{@user.errors.full_messages.join(', ')}" flash.now[:error] = "Could not save user: #{@user.errors.full_messages.join(', ')}"
render action: 'new' render action: 'new'
return return
end end
@ -48,16 +47,13 @@ class ProposalsController < ApplicationController
# User which creates the proposal is both `submitter` and `speaker` of proposal # User which creates the proposal is both `submitter` and `speaker` of proposal
# by default. # by default.
# TODO: Allow submitter to add speakers to proposals @event.speakers = [current_user]
@event.event_users.new(user: current_user, @event.submitter = current_user
event_role: 'submitter')
@event.event_users.new(user: current_user,
event_role: 'speaker')
if @event.save if @event.save
ahoy.track 'Event submission', title: 'New submission' ahoy.track 'Event submission', title: 'New submission'
redirect_to conference_program_proposals_path(@conference.short_title), notice: 'Proposal was successfully submitted.' redirect_to conference_program_proposals_path(@conference.short_title), notice: 'Proposal was successfully submitted.'
else else
flash[:error] = "Could not submit proposal: #{@event.errors.full_messages.join(', ')}" flash.now[:error] = "Could not submit proposal: #{@event.errors.full_messages.join(', ')}"
render action: 'new' render action: 'new'
end end
end end
@ -69,7 +65,7 @@ class ProposalsController < ApplicationController
redirect_to conference_program_proposals_path(conference_id: @conference.short_title), redirect_to conference_program_proposals_path(conference_id: @conference.short_title),
notice: 'Proposal was successfully updated.' notice: 'Proposal was successfully updated.'
else else
flash[:error] = "Could not update proposal: #{@event.errors.full_messages.join(', ')}" flash.now[:error] = "Could not update proposal: #{@event.errors.full_messages.join(', ')}"
render action: 'edit' render action: 'edit'
end end
end end
@ -147,7 +143,9 @@ class ProposalsController < ApplicationController
def event_params def event_params
params.require(:event).permit(:event_type_id, :track_id, :difficulty_level_id, params.require(:event).permit(:event_type_id, :track_id, :difficulty_level_id,
:title, :subtitle, :abstract, :description, :title, :subtitle, :abstract, :description,
:require_registration, :max_attendees, :language) :require_registration, :max_attendees, :language,
speaker_ids: []
)
end end
def user_params def user_params

View file

@ -23,12 +23,12 @@ class RegistrationsController < Devise::RegistrationsController
def configure_permitted_parameters def configure_permitted_parameters
devise_parameter_sanitizer.permit(:account_update) do |u| devise_parameter_sanitizer.permit(:account_update) do |u|
u. u
permit(:email, :password, :password_confirmation, :current_password, :username, :email_public) .permit(:email, :password, :password_confirmation, :current_password, :username, :email_public)
end end
devise_parameter_sanitizer.permit(:sign_up) do |u| devise_parameter_sanitizer.permit(:sign_up) do |u|
u. u
permit(:email, :password, :password_confirmation, :name, :username) .permit(:email, :password, :password_confirmation, :name, :username)
end end
end end
end end

View file

@ -1,7 +1,8 @@
class SchedulesController < ApplicationController class SchedulesController < ApplicationController
load_and_authorize_resource
protect_from_forgery with: :null_session protect_from_forgery with: :null_session
before_action :respond_to_options before_action :respond_to_options
load_and_authorize_resource :conference, find_by: :short_title load_resource :conference, find_by: :short_title
load_resource :program, through: :conference, singleton: true, except: :index load_resource :program, through: :conference, singleton: true, except: :index
def show def show
@ -13,7 +14,7 @@ class SchedulesController < ApplicationController
@events_xml = schedules.map(&:event).group_by{ |event| event.time.to_date } if schedules @events_xml = schedules.map(&:event).group_by{ |event| event.time.to_date } if schedules
@dates = @conference.start_date..@conference.end_date @dates = @conference.start_date..@conference.end_date
@step_minutes = EventType::LENGTH_STEP.minutes @step_minutes = @program.schedule_interval.minutes
@conf_start = @conference.start_hour @conf_start = @conference.start_hour
@conf_period = @conference.end_hour - @conf_start @conf_period = @conference.end_hour - @conf_start

View file

@ -11,7 +11,7 @@ class TicketPurchasesController < ApplicationController
redirect_to new_conference_payment_path, redirect_to new_conference_payment_path,
notice: 'Please pay here to get tickets.' notice: 'Please pay here to get tickets.'
elsif current_user.ticket_purchases.by_conference(@conference).paid.any? elsif current_user.ticket_purchases.by_conference(@conference).paid.any?
redirect_to conference_conference_registration_path(@conference.short_title), redirect_to conference_physical_ticket_index_path,
notice: 'You have free tickets for the conference.' notice: 'You have free tickets for the conference.'
else else
redirect_to conference_tickets_path(@conference.short_title), redirect_to conference_tickets_path(@conference.short_title),
@ -23,6 +23,10 @@ class TicketPurchasesController < ApplicationController
end end
end end
def index
@unpaid_ticket_purchases = current_user.ticket_purchases.by_conference(@conference).unpaid
end
private private
def ticket_purchase_params def ticket_purchase_params

View file

@ -0,0 +1,47 @@
class TracksController < ApplicationController
load_resource :conference, find_by: :short_title
load_resource :program, through: :conference, singleton: true
load_and_authorize_resource through: :program, find_by: :short_name
def index
@tracks = current_user.tracks.where(program: @program)
end
def show; end
def new
@track = @program.tracks.new(color: @conference.next_color_for_collection(:tracks))
end
def edit; end
def create
@track = @program.tracks.new(track_params)
@track.submitter = current_user
@track.state = 'new'
@track.cfp_active = false
if @track.save
redirect_to conference_program_tracks_path(conference_id: @conference.short_title),
notice: 'Track request successfully created.'
else
flash.now[:error] = "Creating Track request failed: #{@track.errors.full_messages.join('. ')}."
render :new
end
end
def update
if @track.update_attributes(track_params)
redirect_to conference_program_tracks_path(conference_id: @conference.short_title),
notice: 'Track request successfully updated.'
else
flash.now[:error] = "Track request update failed: #{@track.errors.full_messages.join('. ')}."
render :edit
end
end
private
def track_params
params.require(:track).permit(:name, :description, :color, :short_name)
end
end

View file

@ -15,7 +15,7 @@ class UsersController < ApplicationController
if @user.update(user_params) if @user.update(user_params)
redirect_to @user, notice: 'User was successfully updated.' redirect_to @user, notice: 'User was successfully updated.'
else else
flash[:error] = "An error prohibited your Profile from being saved: #{@user.errors.full_messages.join('. ')}." flash.now[:error] = "An error prohibited your Profile from being saved: #{@user.errors.full_messages.join('. ')}."
render :edit render :edit
end end
end end

View file

@ -0,0 +1,7 @@
module Admin
module VolunteersHelper
def can_manage_volunteers?(conference)
current_user.has_role?(:organizer, conference) || current_user.has_role?(:volunteers_coordinator, conference)
end
end
end

View file

@ -1,5 +1,4 @@
module ApplicationHelper module ApplicationHelper
##
# Returns a string build from the start and end date of the given conference. # Returns a string build from the start and end date of the given conference.
# #
# If the conference is only one day long # If the conference is only one day long
@ -34,43 +33,9 @@ module ApplicationHelper
result result
end end
## # Returns time with conference timezone
# Checks if the voting has already started, or if it has already ended def time_with_timezone(time)
# time.strftime('%F %R') + ' ' + @conference.timezone.to_s
def voting_open_or_close(program)
return if program.voting_period?
if program.voting_start_date > Time.current
return 'Voting period has not started yet!'
else # voting_end_date > Date.today because voting_start_date < voting_end_date
return 'Voting period is over!'
end
end
##
# Gets an EventType object, and returns its length in timestamp format (HH:MM)
# ====Gets
# * +Integer+ -> 30
# ====Returns
# * +String+ -> "00:30"
def length_timestamp(length)
[length / 60, length % 60].map { |t| t.to_s.rjust(2, '0') }.join(':')
end
##
# Gets a datetime object
# ====Returns
# * +String+ -> formated datetime object
def format_datetime(obj)
return unless obj
obj.strftime('%Y-%m-%d %H:%M')
end
##
# ====Returns
# * +String+ -> number of registrations / max allowed registrations
def registered_text(event)
return "Registered: #{event.registrations.count}/#{event.max_attendees}" if event.max_attendees
"Registered: #{event.registrations.count}"
end end
# Set resource_name for devise so that we can call the devise help links (views/devise/shared/_links) from anywhere (eg sign_up form in proposals#new) # Set resource_name for devise so that we can call the devise help links (views/devise/shared/_links) from anywhere (eg sign_up form in proposals#new)
@ -78,141 +43,6 @@ module ApplicationHelper
:user :user
end end
# Set devise_mapping for devise so that we can call the devise help links (views/devise/shared/_links) from anywhere (eg sign_up form in proposals#new)
def devise_mapping
@devise_mapping ||= Devise.mappings[:user]
end
def event_status_icon(event)
case event.state
when 'new'
'fa-eye'
when 'unconfirmed'
'fa-check text-muted'
when 'confirmed'
'fa-check text-success'
when 'rejected', 'withdrawn', 'canceled'
'fa-ban'
end
end
def event_progress_color(progress)
progress = progress.to_i
if progress == 100
'progress-bar-success'
elsif progress >= 85
'progress-bar-info'
elsif progress >= 71
'progress-bar-warning'
else
'progress-bar-danger'
end
end
def target_progress_color(progress)
progress = progress.to_i
result =
case
when progress >= 90 then 'green'
when progress < 90 && progress >= 80 then 'orange'
else 'red'
end
result
end
def days_left_color(days_left)
days_left = days_left.to_i
if days_left > 30
result = 'green'
elsif days_left < 30 && days_left > 10
result = 'orange'
else
result = 'red'
end
result
end
def bootstrap_class_for(flash_type)
case flash_type
when 'success'
'alert-success'
when 'error'
'alert-danger'
when 'alert'
'alert-warning'
when 'notice'
'alert-info'
else
'alert-warning'
end
end
def label_for(event_state)
result = ''
case event_state
when 'new'
result = 'label label-primary'
when 'withdrawn'
result = 'label label-danger'
when 'unconfirmed'
result = 'label label-success'
when 'confirmed'
result = 'label label-success'
when 'rejected'
result = 'label label-warning'
when 'canceled'
result = 'label label-danger'
end
result
end
def icon_for_todo(bool)
if bool
return 'fa fa-check'
else
return 'fa fa-times'
end
end
def class_for_todo(bool)
if bool
return 'todolist-ok'
else
return 'todolist-missing'
end
end
def normalize_array_length(hashmap, length)
hashmap.each do |_, value|
if value.length < length
value.fill(value[-1], value.length...length)
end
end
end
def active_nav_li(link)
if current_page?(link)
return 'active'
else
return ''
end
end
def show_time(length)
return '0 h 0 min' if length.blank?
h, min = length.divmod(60)
if h == 0
"#{min.round} min"
elsif min == 0
"#{h} h"
else
"#{h} h #{min.round} min"
end
end
def add_association_link(association_name, form_builder, div_class, html_options = {}) 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') link_to_add_association 'Add ' + association_name.to_s.singularize, form_builder, div_class, html_options.merge(class: 'assoc btn btn-success')
end end
@ -225,29 +55,6 @@ module ApplicationHelper
render 'shared/dynamic_association', association_name: association_name, title: title, f: form_builder, hint: options[:hint] render 'shared/dynamic_association', association_name: association_name, title: title, f: form_builder, hint: options[:hint]
end end
# Same as redirect_to(:back) if there is a valid HTTP referer, otherwise redirect_to()
def redirect_back_or_to(options = {}, response_status = {})
if request.env['HTTP_REFERER']
redirect_to :back
else
redirect_to options, response_status
end
end
def event_types(conference)
all = conference.program.event_types.map { |et | et.title.pluralize }
first = all[0...-1]
last = all[-1]
ets = ''
if all.length > 1
ets << first.join(', ')
ets << " and #{last}"
else
ets = all.join
end
return ets
end
def tracks(conference) def tracks(conference)
all = conference.program.tracks.map {|t| t.name} all = conference.program.tracks.map {|t| t.name}
first = all[0...-1] first = all[0...-1]
@ -276,155 +83,19 @@ module ApplicationHelper
return ts return ts
end end
# rubocop:disable Lint/EndAlignment
def word_pluralize(count, singular, plural = nil)
word = if (count == 1 || count =~ /^1(\.0+)?$/)
singular
else
plural || singular.pluralize
end
"#{word}"
end
def markdown(text)
return '' if text.nil?
options = {
autolink: true,
space_after_headers: true,
no_intra_emphasis: true
}
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(escape_html: true), options)
markdown.render(text).html_safe
end
def markdown_hint(text='')
markdown("#{text}\n\nPlease look at [**Markdown Syntax**](https://daringfireball.net/projects/markdown/syntax) to format your text")
end
def omniauth_configured
providers = []
Devise.omniauth_providers.each do |provider|
provider_key = "#{provider}_key"
provider_secret = "#{provider}_secret"
unless Rails.application.secrets.send(provider_key).blank? || Rails.application.secrets.send(provider_secret).blank?
providers << provider
end
providers << provider if !ENV["OSEM_#{provider.upcase}_KEY"].blank? && !ENV["OSEM_#{provider.upcase}_SECRET"].blank?
end
return providers.uniq
end
# Receives a hash, generated from User model, function get_roles
# Outputs the roles of a user, including the conferences for which the user has the roles
# Eg. organizer(oSC13, oSC14), cfp(oSC12, oSC13)
def show_roles(roles)
roles.map{ |x| x[0].titleize + ' (' + x[1].join(', ') + ')' }.join ', '
end
def can_manage_volunteers(conference)
if (current_user.has_role? :organizer, conference) || (current_user.has_role? :volunteers_coordinator, conference)
true
else
false
end
end
def sign_in_path
if ENV['OSEM_ICHAIN_ENABLED'] == 'true'
new_user_ichain_session_path
else
new_user_session_path
end
end
def unread_notifications(user) def unread_notifications(user)
Comment.accessible_by(current_ability).find_since_last_login(user) Comment.accessible_by(current_ability).find_since_last_login(user)
end end
# Returns black or white deppending on what of them contrast more with the
# given color. Useful to print text in a coloured background.
# hexcolor is a hex color of 7 characters, being the first one '#'.
# Reference: https://24ways.org/2010/calculating-color-contrast
def contrast_color(hexcolor)
r = hexcolor[1..2].to_i(16)
g = hexcolor[3..4].to_i(16)
b = hexcolor[5..6].to_i(16)
yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000
(yiq >= 128) ? 'black' : 'white'
end
def td_height(rooms)
td_height = 500 / rooms.length
# we want all least 3 lines in events and td padding = 3px, speaker picture height >= 25px
# and line-height = 17px => (17 * 3) + 6 + 25 = 82
td_height < 82 ? 82 : td_height
end
def room_height(rooms)
room_lines(rooms) * 17
end
def room_lines(rooms)
# line-height = 17px, td padding = 3px
(td_height(rooms) - 6) / 17
end
def event_height(rooms)
event_lines(rooms) * 17
end
def event_lines(rooms)
# line-height = 17px, td padding = 3px, speaker picture height >= 25px
(td_height(rooms) - 31) / 17
end
def speaker_height(rooms)
# td padding = 3px
speaker_height = td_height(rooms) - 6 - event_height(rooms)
# The speaker picture is a circle and the width must be <= 37 to avoid making the cell widther
speaker_height >= 37 ? 37 : speaker_height
end
def speaker_width(rooms)
# speaker picture padding: 4px 2px; and we want the picture to be a circle
speaker_height(rooms) - 4
end
def carousel_item_class(number, carousel_number, num_cols, col)
item_class = 'item'
item_class += ' first' if number == 0
item_class += ' last' if number == (carousel_number - 1)
if (col && ((col / num_cols) == number)) || (!col && number == 0)
item_class += ' active'
end
item_class
end
def selected_scheduled?(schedule)
(schedule == @selected_schedule) ? 'Yes' : 'No'
end
# Recieves a PaperTrail::Version object # Recieves a PaperTrail::Version object
# Outputs the list of attributes that were changed in the version (ignoring changes from one blank value to another) # Outputs the list of attributes that were changed in the version (ignoring changes from one blank value to another)
# Eg: If version.changeset = '{"title"=>[nil, "Premium"], "description"=>[nil, "Premium = Super cool"], "conference_id"=>[nil, 3]}' # Eg: If version.changeset = '{"title"=>[nil, "Premium"], "description"=>[nil, "Premium = Super cool"], "conference_id"=>[nil, 3]}'
# Output will be 'title, description and conference' # Output will be 'title, description and conference'
def updated_attributes(version) def updated_attributes(version)
version.changeset. version.changeset
reject{ |_, values| values[0].blank? && values[1].blank? }. .reject{ |_, values| values[0].blank? && values[1].blank? }
keys.map{ |key| key.gsub('_id', '').tr('_', ' ')}.join(', '). .keys.map{ |key| key.gsub('_id', '').tr('_', ' ')}.join(', ')
reverse.sub(',', ' dna ').reverse .reverse.sub(',', ' dna ').reverse
end
def link_to_user(user_id)
user = User.find_by(id: user_id)
if user
link_to user.name, admin_user_path(id: user_id)
else
'Someone (probably via the console)'
end
end end
# Recieves a model_name and id # Recieves a model_name and id
@ -446,136 +117,23 @@ module ApplicationHelper
object object
end end
def event_change_description(version) def normalize_array_length(hashmap, length)
case hashmap.each do |_, value|
when version.event == 'create' then 'submitted new' if value.length < length
value.fill(value[-1], value.length...length)
when version.changeset['state'] end
case version.changeset['state'][1] end
when 'unconfirmed' then 'accepted'
when 'withdrawn' then 'withdrew'
when 'canceled', 'rejected', 'confirmed' then version.changeset['state'][1]
when 'new' then 'resubmitted'
end end
# Same as redirect_to(:back) if there is a valid HTTP referer, otherwise redirect_to()
def redirect_back_or_to(options = {}, response_status = {})
if request.env['HTTP_REFERER']
redirect_to :back
else else
"updated #{updated_attributes(version)} of" redirect_to options, response_status
end end
end end
def users_role_change_description(version)
version.event == 'create' ? 'added' : 'removed'
end
def subscription_change_description(version)
user = current_or_last_object_state(version.item_type, version.item_id).user
user_name = user.name unless user.id.to_s == version.whodunnit
version.event == 'create' ? "subscribed #{user_name} to" : "unsubscribed #{user_name} from"
end
def registration_change_description(version)
if version.item_type == 'Registration'
user = current_or_last_object_state(version.item_type, version.item_id).user
elsif version.item_type == 'EventsRegistration'
registration_id = current_or_last_object_state(version.item_type, version.item_id).registration_id
user = current_or_last_object_state('Registration', registration_id).user
end
if user.id.to_s == version.whodunnit
case version.event
when 'create' then 'registered to'
when 'update' then "updated #{updated_attributes(version)} of the registration for"
when 'destroy' then 'unregistered from'
end
else
case version.event
when 'create' then "registered #{user.name} to"
when 'update' then "updated #{updated_attributes(version)} of #{user.name}'s registration for"
when 'destroy' then "unregistered #{user.name} from"
end
end
end
def comment_change_description(version)
user = current_or_last_object_state(version.item_type, version.item_id).user
if version.event == 'create'
version.previous.nil? ? 'commented on' : "re-added #{user.name}'s comment on"
else
"deleted #{user.name}'s comment on"
end
end
def vote_change_description(version)
user = current_or_last_object_state(version.item_type, version.item_id).user
if version.event == 'create'
version.previous.nil? ? 'voted on' : "re-added #{user.name}'s vote on"
elsif version.event == 'update'
"updated #{user.name}'s vote on"
else
"deleted #{user.name}'s vote on"
end
end
def user_change_description(version)
if version.event == 'create'
link_to_user(version.item_id) + ' signed up'
elsif version.event == 'update'
if version.changeset.keys.include?('reset_password_sent_at')
'Someone requested password reset of'
elsif version.changeset.keys.include?('confirmed_at') && version.changeset['confirmed_at'][0].nil?
(version.whodunnit.nil? ? link_to_user(version.item_id) : link_to_user(version.whodunnit)) + ' confirmed account of'
elsif version.changeset.keys.include?('confirmed_at') && version.changeset['confirmed_at'][1].nil?
link_to_user(version.whodunnit) + ' unconfirmed account of'
else
link_to_user(version.whodunnit) + " updated #{updated_attributes(version)} of"
end
end
end
def event_schedule_change_description(version)
case version.event
when 'create' then 'scheduled'
when 'update' then 'rescheduled'
when 'destroy' then 'unscheduled'
end
end
def general_change_description(version)
if version.event == 'create'
'created new'
elsif version.event == 'update'
"updated #{updated_attributes(version)} of"
else
'deleted'
end
end
def link_if_alive(version, link_text, link_url)
version.item ? link_to(link_text, link_url) : link_text
end
def canceled_replacement_event_label(event, event_schedule, *label_classes)
if event.state == 'canceled' || event.state == 'withdrawn'
content_tag :span, 'CANCELED', class: (['label', 'label-danger'] + label_classes)
elsif event_schedule.present? && event_schedule.replacement?
content_tag :span, 'REPLACEMENT', class: (['label', 'label-info'] + label_classes)
end
end
def replacement_event_notice(event_schedule)
if event_schedule.present? && event_schedule.replacement?
replaced_event = (event_schedule.intersecting_event_schedules.withdrawn.first || event_schedule.intersecting_event_schedules.canceled.first).event
content_tag :span do
concat content_tag :span, 'Please note that this talk replaces '
concat link_to replaced_event.title, conference_program_proposal_path(@conference.short_title, replaced_event.id)
end
end
end
def quantity_left_of(resource)
"#{resource.quantity - resource.used}/#{resource.quantity}"
end
def concurrent_events(event) def concurrent_events(event)
return nil unless event.scheduled? && event.program.selected_event_schedules return nil unless event.scheduled? && event.program.selected_event_schedules
event_schedule = event.program.selected_event_schedules.find_by(event: event) event_schedule = event.program.selected_event_schedules.find_by(event: event)
@ -592,4 +150,36 @@ module ApplicationHelper
end end
concurrent_events concurrent_events
end end
def speaker_links(event)
safe_join(event.speakers.map{ |speaker| link_to speaker.name, admin_user_path(speaker) }, ',')
end
def speaker_selector_input(form)
users = User.active.pluck(:id, :name, :username, :email).map { |user| [user[0], user[1].blank? ? user[2] : user[1], user[2], user[3]] }.sort_by { |user| user[1].downcase }
form.input :speakers, as: :select,
collection: options_for_select(users.map {|user| ["#{user[1]} (#{user[2]}) #{user[3]}", user[0]]}, @event.speakers.map(&:id)),
include_blank: false, label: 'Speakers', input_html: { class: 'select-help-toggle', multiple: 'true' }
end
def event_types(conference)
conference.program.event_types.map { |et| et.title.pluralize }.to_sentence
end
def sign_in_path
if ENV['OSEM_ICHAIN_ENABLED'] == 'true'
new_user_ichain_session_path
else
new_user_session_path
end
end
##
# ====Gets
# a conference object
# ==== Returns
# class hidden if conference is over
def hidden_if_conference_over(conference)
'hidden' if Date.today > conference.end_date
end
end end

View file

@ -0,0 +1,37 @@
module DateTimeHelper
##
# Includes functions related to date or time manipulations
##
##
# Gets an EventType object, and returns its length in timestamp format (HH:MM)
# ====Gets
# * +Integer+ -> 30
# ====Returns
# * +String+ -> "00:30"
def length_timestamp(length)
[length / 60, length % 60].map { |t| t.to_s.rjust(2, '0') }.join(':')
end
##
# Gets a datetime object
# ====Returns
# * +String+ -> formated datetime object
def format_datetime(obj)
return unless obj
obj.strftime('%Y-%m-%d %H:%M')
end
def show_time(length)
return '0 h 0 min' if length.blank?
h, min = length.divmod(60)
if h == 0
"#{min.round} min"
elsif min == 0
"#{h} h"
else
"#{h} h #{min.round} min"
end
end
end

View file

@ -0,0 +1,42 @@
module EventsHelper
##
# Includes functions related to events
##
##
# ====Returns
# * +String+ -> number of registrations / max allowed registrations
def registered_text(event)
return "Registered: #{event.registrations.count}/#{event.max_attendees}" if event.max_attendees
"Registered: #{event.registrations.count}"
end
##
# Checks if the voting has already started, or if it has already ended
#
def voting_open_or_close(program)
return if program.voting_period?
if program.voting_start_date > Time.current
return 'Voting period has not started yet!'
else # voting_end_date > Date.today because voting_start_date < voting_end_date
return 'Voting period is over!'
end
end
def replacement_event_notice(event_schedule)
if event_schedule.present? && event_schedule.replacement?
replaced_event = (event_schedule.intersecting_event_schedules.withdrawn.first || event_schedule.intersecting_event_schedules.canceled.first).event
content_tag :span do
concat content_tag :span, 'Please note that this talk replaces '
concat link_to replaced_event.title, conference_program_proposal_path(@conference.short_title, replaced_event.id)
end
end
end
def canceled_replacement_event_label(event, event_schedule, *label_classes)
if event.state == 'canceled' || event.state == 'withdrawn'
content_tag :span, 'CANCELED', class: (['label', 'label-danger'] + label_classes)
elsif event_schedule.present? && event_schedule.replacement?
content_tag :span, 'REPLACEMENT', class: (['label', 'label-info'] + label_classes)
end
end
end

View file

@ -0,0 +1,198 @@
module FormatHelper
##
# Includes functions related to formatting (like adding classes, colors)
##
def event_status_icon(event)
case event.state
when 'new'
'fa-eye'
when 'unconfirmed'
'fa-check text-muted'
when 'confirmed'
'fa-check text-success'
when 'rejected', 'withdrawn', 'canceled'
'fa-ban'
end
end
def event_progress_color(progress)
progress = progress.to_i
if progress == 100
'progress-bar-success'
elsif progress >= 85
'progress-bar-info'
elsif progress >= 71
'progress-bar-warning'
else
'progress-bar-danger'
end
end
def target_progress_color(progress)
progress = progress.to_i
result =
case
when progress >= 90 then 'green'
when progress < 90 && progress >= 80 then 'orange'
else 'red'
end
result
end
def days_left_color(days_left)
days_left = days_left.to_i
if days_left > 30
result = 'green'
elsif days_left < 30 && days_left > 10
result = 'orange'
else
result = 'red'
end
result
end
def bootstrap_class_for(flash_type)
case flash_type
when 'success'
'alert-success'
when 'error'
'alert-danger'
when 'alert'
'alert-warning'
when 'notice'
'alert-info'
else
'alert-warning'
end
end
def label_for(event_state)
result = ''
case event_state
when 'new'
result = 'label label-primary'
when 'withdrawn'
result = 'label label-danger'
when 'unconfirmed'
result = 'label label-success'
when 'confirmed'
result = 'label label-success'
when 'rejected'
result = 'label label-warning'
when 'canceled'
result = 'label label-danger'
end
result
end
def icon_for_todo(bool)
if bool
return 'fa fa-check'
else
return 'fa fa-times'
end
end
def class_for_todo(bool)
if bool
return 'todolist-ok'
else
return 'todolist-missing'
end
end
def word_pluralize(count, singular, plural = nil)
word = if (count == 1 || count =~ /^1(\.0+)?$/)
singular
else
plural || singular.pluralize
end
"#{word}"
end
# Returns black or white deppending on what of them contrast more with the
# given color. Useful to print text in a coloured background.
# hexcolor is a hex color of 7 characters, being the first one '#'.
# Reference: https://24ways.org/2010/calculating-color-contrast
def contrast_color(hexcolor)
r = hexcolor[1..2].to_i(16)
g = hexcolor[3..4].to_i(16)
b = hexcolor[5..6].to_i(16)
yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000
(yiq >= 128) ? 'black' : 'white'
end
def td_height(rooms)
td_height = 500 / rooms.length
# we want all least 3 lines in events and td padding = 3px, speaker picture height >= 25px
# and line-height = 17px => (17 * 3) + 6 + 25 = 82
td_height < 82 ? 82 : td_height
end
def room_height(rooms)
room_lines(rooms) * 17
end
def room_lines(rooms)
# line-height = 17px, td padding = 3px
(td_height(rooms) - 6) / 17
end
def event_height(rooms)
event_lines(rooms) * 17
end
def event_lines(rooms)
# line-height = 17px, td padding = 3px, speaker picture height >= 25px
(td_height(rooms) - 31) / 17
end
def speaker_height(rooms)
# td padding = 3px
speaker_height = td_height(rooms) - 6 - event_height(rooms)
# The speaker picture is a circle and the width must be <= 37 to avoid making the cell widther
speaker_height >= 37 ? 37 : speaker_height
end
def speaker_width(rooms)
# speaker picture padding: 4px 2px; and we want the picture to be a circle
speaker_height(rooms) - 4
end
def carousel_item_class(number, carousel_number, num_cols, col)
item_class = 'item'
item_class += ' first' if number == 0
item_class += ' last' if number == (carousel_number - 1)
if (col && ((col / num_cols) == number)) || (!col && number == 0)
item_class += ' active'
end
item_class
end
def selected_scheduled?(schedule)
(schedule == @selected_schedule) ? 'Yes' : 'No'
end
def markdown(text, escape_html=true)
return '' if text.nil?
options = {
autolink: true,
space_after_headers: true,
no_intra_emphasis: true
}
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(escape_html: escape_html), options)
markdown.render(text).html_safe
end
def markdown_hint(text='')
markdown("#{text} Please look at #{link_to '**Markdown Syntax**', 'https://daringfireball.net/projects/markdown/syntax', target: '_blank'} to format your text", false)
end
def quantity_left_of(resource)
return '-/-' if resource.quantity.blank?
"#{resource.quantity - resource.used}/#{resource.quantity}"
end
end

View file

@ -0,0 +1,21 @@
module PathsHelper
##
# Includes functions related to links or redirects
##
def link_to_user(user_id)
user = User.find_by(id: user_id)
if user
link_to user.name, admin_user_path(id: user_id)
else
'Someone (probably via the console)'
end
end
def active_nav_li(link)
if current_page?(link)
return 'active'
else
return ''
end
end
end

View file

@ -0,0 +1,30 @@
module UsersHelper
##
# Includes functions related to users
##
# Set devise_mapping for devise so that we can call the devise help links (views/devise/shared/_links) from anywhere (eg sign_up form in proposals#new)
def devise_mapping
@devise_mapping ||= Devise.mappings[:user]
end
def omniauth_configured
providers = []
Devise.omniauth_providers.each do |provider|
provider_key = "#{provider}_key"
provider_secret = "#{provider}_secret"
unless Rails.application.secrets.send(provider_key).blank? || Rails.application.secrets.send(provider_secret).blank?
providers << provider
end
providers << provider if !ENV["OSEM_#{provider.upcase}_KEY"].blank? && !ENV["OSEM_#{provider.upcase}_SECRET"].blank?
end
return providers.uniq
end
# Receives a hash, generated from User model, function get_roles
# Outputs the roles of a user, including the conferences for which the user has the roles
# Eg. organizer(oSC13, oSC14), cfp(oSC12, oSC13)
def show_roles(roles)
roles.map{ |x| x[0].titleize + ' (' + x[1].join(', ') + ')' }.join ', '
end
end

View file

@ -0,0 +1,112 @@
module VersionsHelper
##
# Groups functions related to change description
##
def link_if_alive(version, link_text, link_url)
version.item ? link_to(link_text, link_url) : link_text
end
def subscription_change_description(version)
user = current_or_last_object_state(version.item_type, version.item_id).user
user_name = user.name unless user.id.to_s == version.whodunnit
version.event == 'create' ? "subscribed #{user_name} to" : "unsubscribed #{user_name} from"
end
def registration_change_description(version)
if version.item_type == 'Registration'
user = current_or_last_object_state(version.item_type, version.item_id).user
elsif version.item_type == 'EventsRegistration'
registration_id = current_or_last_object_state(version.item_type, version.item_id).registration_id
user = current_or_last_object_state('Registration', registration_id).user
end
if user.id.to_s == version.whodunnit
case version.event
when 'create' then 'registered to'
when 'update' then "updated #{updated_attributes(version)} of the registration for"
when 'destroy' then 'unregistered from'
end
else
case version.event
when 'create' then "registered #{user.name} to"
when 'update' then "updated #{updated_attributes(version)} of #{user.name}'s registration for"
when 'destroy' then "unregistered #{user.name} from"
end
end
end
def comment_change_description(version)
user = current_or_last_object_state(version.item_type, version.item_id).user
if version.event == 'create'
version.previous.nil? ? 'commented on' : "re-added #{user.name}'s comment on"
else
"deleted #{user.name}'s comment on"
end
end
def vote_change_description(version)
user = current_or_last_object_state(version.item_type, version.item_id).user
if version.event == 'create'
version.previous.nil? ? 'voted on' : "re-added #{user.name}'s vote on"
elsif version.event == 'update'
"updated #{user.name}'s vote on"
else
"deleted #{user.name}'s vote on"
end
end
def general_change_description(version)
if version.event == 'create'
'created new'
elsif version.event == 'update'
"updated #{updated_attributes(version)} of"
else
'deleted'
end
end
def event_change_description(version)
case
when version.event == 'create' then 'submitted new'
when version.changeset['state']
case version.changeset['state'][1]
when 'unconfirmed' then 'accepted'
when 'withdrawn' then 'withdrew'
when 'canceled', 'rejected', 'confirmed' then version.changeset['state'][1]
when 'new' then 'resubmitted'
end
else
"updated #{updated_attributes(version)} of"
end
end
def event_schedule_change_description(version)
case version.event
when 'create' then 'scheduled'
when 'update' then 'rescheduled'
when 'destroy' then 'unscheduled'
end
end
def user_change_description(version)
if version.event == 'create'
link_to_user(version.item_id) + ' signed up'
elsif version.event == 'update'
if version.changeset.keys.include?('reset_password_sent_at')
'Someone requested password reset of'
elsif version.changeset.keys.include?('confirmed_at') && version.changeset['confirmed_at'][0].nil?
(version.whodunnit.nil? ? link_to_user(version.item_id) : link_to_user(version.whodunnit)) + ' confirmed account of'
elsif version.changeset.keys.include?('confirmed_at') && version.changeset['confirmed_at'][1].nil?
link_to_user(version.whodunnit) + ' unconfirmed account of'
else
link_to_user(version.whodunnit) + " updated #{updated_attributes(version)} of"
end
end
end
def users_role_change_description(version)
version.event == 'create' ? 'added' : 'removed'
end
end

View file

@ -3,32 +3,19 @@ class Ability
# Initializes the ability class # Initializes the ability class
def initialize(user) def initialize(user)
# Order Abilities
# (Check https://github.com/CanCanCommunity/cancancan/wiki/Ability-Precedence)
# Check roles of user, using rolify. Role name is *case sensitive*
# user.is_organizer? or user.has_role? :organizer
# user.is_cfp_of? Conference or user.has_role? :cfp, Conference
# user.is_info_desk_of? Conference
# user.is_volunteers_coordinator_of? Conference
# user.is_attendee_of? Conference
# The following is wrong because a user will only have 'cfp' role for a specific conference
# user.is_cfp? # This is always false
user ||= User.new user ||= User.new
# This is what sets up the different abilities
if user.new_record? if user.new_record?
not_signed_in not_signed_in
# Checks if the user does not have any role and is not an admin
elsif user.roles.any? || user.is_admin
signed_in_with_roles(user)
else else
signed_in(user) signed_in(user)
common_abilities_for_admins(user) if user.roles.any? || user.is_admin?
end end
end end
# Abilities for not signed in users (guests) # Abilities for not signed in users (guests)
def not_signed_in def not_signed_in
can [:index], Organization
can [:index], Conference can [:index], Conference
can [:show], Conference do |conference| can [:show], Conference do |conference|
conference.splashpage && conference.splashpage.public == true conference.splashpage && conference.splashpage.public == true
@ -62,6 +49,10 @@ class Ability
can [:new, :create], Event do |event| can [:new, :create], Event do |event|
event.program.cfp_open? && event.new_record? event.program.cfp_open? && event.new_record?
end end
can [:show, :events], Schedule do |schedule|
schedule.program.schedule_public
end
end end
end end
@ -69,19 +60,20 @@ class Ability
def signed_in(user) def signed_in(user)
# Abilities from not_signed_in user are also inherited # Abilities from not_signed_in user are also inherited
not_signed_in not_signed_in
can :manage, User, id: user.id can :manage, User, id: user.id
can :manage, Registration, user_id: user.id can :manage, Registration, user_id: user.id
can [:new, :create], Registration do |registration| can [:new, :create], Registration do |registration|
conference = registration.conference conference = registration.conference
conference.registration_open? && !conference.registration_limit_exceeded? conference.registration_open? && !conference.registration_limit_exceeded? || conference.program.speakers.confirmed.include?(user)
end end
can :index, Organization
can :index, Ticket can :index, Ticket
can :manage, TicketPurchase, user_id: user.id can :manage, TicketPurchase, user_id: user.id
can [:new, :create], Payment, user_id: user.id can [:new, :create], Payment, user_id: user.id
can [:index, :show], PhysicalTicket, user: user
can [:create, :destroy], Subscription, user_id: user.id can [:create, :destroy], Subscription, user_id: user.id
@ -95,171 +87,42 @@ class Ability
# can manage the commercials of their own events # can manage the commercials of their own events
can :manage, Commercial, commercialable_type: 'Event', commercialable_id: user.events.pluck(:id) can :manage, Commercial, commercialable_type: 'Event', commercialable_id: user.events.pluck(:id)
can [:destroy], Openid
end end
# Abilities for signed in users with roles # Abilities for users with roles wandering around in non-admin views.
def signed_in_with_roles(user) def common_abilities_for_admins(user)
# Abilities from not_signed_in and signed_in are also inherited
signed_in(user)
signed_in_with_organizer_role(user) if user.has_role? :organizer, :any
signed_in_with_cfp_role(user) if user.has_role? :cfp, :any
signed_in_with_info_desk_role(user) if user.has_role? :info_desk, :any
signed_in_with_volunteers_coordinator_role(user) if user.has_role? :volunteers_coordinator, :any
# for users with any role
can :access, Admin can :access, Admin
can [:show], Conference can :manage, :all if user.is_admin?
can :index, Commercial, commercialable_type: 'Conference'
cannot [:edit, :update, :destroy], Question, global: true
# for admins
can :manage, :all if user.is_admin
cannot :revert_object, PaperTrail::Version do |version|
(version.event == 'create' && %w(Conference User Event).include?(version.item_type))
end
cannot :revert_attribute, PaperTrail::Version do |version|
version.event != 'update' || version.item.nil?
end
cannot :destroy, Program
# Do not delete venue, when there are rooms being used
cannot :destroy, Venue do |venue|
venue.conference.program.events.where.not(room_id: nil).any?
end
end
def signed_in_with_organizer_role(user)
# ids of all the conferences for which the user has the 'organizer' role
conf_ids_for_organizer = Conference.with_role(:organizer, user).pluck(:id) conf_ids_for_organizer = Conference.with_role(:organizer, user).pluck(:id)
can :manage, Resource, conference_id: conf_ids_for_organizer
can [:new, :create], Conference if user.has_role?(:organizer, :any)
can :manage, Conference, id: conf_ids_for_organizer
can :manage, Splashpage, conference_id: conf_ids_for_organizer
can :manage, Contact, conference_id: conf_ids_for_organizer
can :manage, EmailSettings, conference_id: conf_ids_for_organizer
can :manage, Campaign, conference_id: conf_ids_for_organizer
can :manage, Target, conference_id: conf_ids_for_organizer
can :manage, Commercial, commercialable_type: 'Conference',
commercialable_id: conf_ids_for_organizer
can :manage, Registration, conference_id: conf_ids_for_organizer
can :manage, RegistrationPeriod, conference_id: conf_ids_for_organizer
can :manage, Question, conference_id: conf_ids_for_organizer
can :manage, Question do |question|
!(question.conferences.pluck(:id) & conf_ids_for_organizer).empty?
end
can :manage, Vposition, conference_id: conf_ids_for_organizer
can :manage, Vday, conference_id: conf_ids_for_organizer
can :manage, Program, conference_id: conf_ids_for_organizer
can :manage, Schedule, program: { conference_id: conf_ids_for_organizer }
can :manage, EventSchedule, schedule: { program: { conference_id: conf_ids_for_organizer } }
can :manage, Cfp, program: { conference_id: conf_ids_for_organizer}
can :manage, Event, program: { conference_id: conf_ids_for_organizer}
can :manage, EventType, program: { conference_id: conf_ids_for_organizer}
can :manage, Track, program: { conference_id: conf_ids_for_organizer}
can :manage, DifficultyLevel, program: { conference_id: conf_ids_for_organizer}
can :manage, Commercial, commercialable_type: 'Event',
commercialable_id: Event.where(program_id: Program.where(conference_id: conf_ids_for_organizer).pluck(:id)).pluck(:id)
can :manage, Venue, conference_id: conf_ids_for_organizer
can :manage, Commercial, commercialable_type: 'Venue',
commercialable_id: Venue.where(conference_id: conf_ids_for_organizer).pluck(:id)
can :manage, Lodging, conference_id: conf_ids_for_organizer
can :manage, Room, venue: { conference_id: conf_ids_for_organizer}
can :manage, Sponsor, conference_id: conf_ids_for_organizer
can :manage, SponsorshipLevel, conference_id: conf_ids_for_organizer
can :manage, Ticket, conference_id: conf_ids_for_organizer
can :index, Comment, commentable_type: 'Event',
commentable_id: Event.where(program_id: Program.where(conference_id: conf_ids_for_organizer).pluck(:id)).pluck(:id)
# Abilities for Role (Conference resource)
can [:index, :show], Role
can [:edit, :update, :toggle_user], Role do |role|
role.resource_type == 'Conference' && (conf_ids_for_organizer.include? role.resource_id)
end
can [:index, :revert_object, :revert_attribute], PaperTrail::Version do |version|
version.item_type == 'User' || (conf_ids_for_organizer.include? version.conference_id)
end
end
def signed_in_with_cfp_role(user)
# ids of all the conferences for which the user has the 'cfp' role
conf_ids_for_cfp = Conference.with_role(:cfp, user).pluck(:id) conf_ids_for_cfp = Conference.with_role(:cfp, user).pluck(:id)
can [:index, :show, :update], Resource, conference_id: conf_ids_for_cfp
can :manage, Event, program: { conference_id: conf_ids_for_cfp }
can :manage, EventType, program: { conference_id: conf_ids_for_cfp }
can :manage, Track, program: { conference_id: conf_ids_for_cfp }
can :manage, DifficultyLevel, program: { conference_id: conf_ids_for_cfp }
can :manage, EmailSettings, conference_id: conf_ids_for_cfp
can :manage, Room, venue: { conference_id: conf_ids_for_cfp }
can :show, Venue, conference_id: conf_ids_for_cfp
can :show, Commercial, commercialable_type: 'Venue', commercialable_id: Venue.where(conference_id: conf_ids_for_cfp).pluck(:id)
can :manage, Cfp, program: { conference_id: conf_ids_for_cfp }
can :manage, Program, conference_id: conf_ids_for_cfp
can :manage, Commercial, commercialable_type: 'Event',
commercialable_id: Event.where(program_id: Program.where(conference_id: conf_ids_for_cfp).pluck(:id)).pluck(:id)
can :index, Comment, commentable_type: 'Event',
commentable_id: Event.where(program_id: Program.where(conference_id: conf_ids_for_cfp).pluck(:id)).pluck(:id)
# Abilities for Role (Conference resource)
can [:index, :show], Role
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
can :toggle_user, Role do |role|
role.resource_type == 'Conference' && role.name == 'cfp' &&
(Conference.with_role(:cfp, user).pluck(:id).include? role.resource_id)
end
can [:index, :revert_object, :revert_attribute], PaperTrail::Version, item_type: 'Event', conference_id: conf_ids_for_cfp
can [:index, :revert_object, :revert_attribute], PaperTrail::Version, item_type: 'Vote', conference_id: conf_ids_for_cfp
can [:index, :revert_object, :revert_attribute], PaperTrail::Version do |version|
version.item_type == 'Commercial' && conf_ids_for_cfp.include?(version.conference_id) &&
(version.object.to_s.include?('Event') || version.object_changes.to_s.include?('Event'))
end
end
def signed_in_with_info_desk_role(user)
# ids of all the conferences for which the user has the 'info_desk' role
conf_ids_for_info_desk = Conference.with_role(:info_desk, user).pluck(:id) conf_ids_for_info_desk = Conference.with_role(:info_desk, user).pluck(:id)
can [:index, :show, :update], Resource, conference_id: conf_ids_for_info_desk if conf_ids_for_organizer
# To access splashpage of their conference if it is not public
can :show, Conference, id: conf_ids_for_organizer
# To access conference/proposals/registrations
can :manage, Registration, conference_id: conf_ids_for_organizer
# To access conference/proposals
can :manage, Event, program: { conference_id: conf_ids_for_organizer }
# To access comment link in menu bar
can :index, Comment, commentable_type: 'Event',
commentable_id: Event.where(program_id: Program.where(conference_id: conf_ids_for_organizer).pluck(:id)).pluck(:id)
end
if conf_ids_for_cfp
# To access comment link in menu bar
can :index, Comment, commentable_type: 'Event',
commentable_id: Event.where(program_id: Program.where(conference_id: conf_ids_for_cfp).pluck(:id)).pluck(:id)
# To access conference/proposals
can :manage, Event, program: { conference_id: conf_ids_for_cfp }
end
if conf_ids_for_info_desk
# To access conference/proposals/registrations
can :manage, Registration, conference_id: conf_ids_for_info_desk can :manage, Registration, conference_id: conf_ids_for_info_desk
can :manage, Question, conference_id: conf_ids_for_info_desk
can :manage, Question do |question|
!(question.conferences.pluck(:id) & conf_ids_for_info_desk).empty?
end
# Abilities for Role (Conference resource)
can [:index, :show], Role
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
can :toggle_user, Role do |role|
role.resource_type == 'Conference' && role.name == 'info_desk' &&
(Conference.with_role(:info_desk, user).pluck(:id).include? role.resource_id)
end
end
def signed_in_with_volunteers_coordinator_role(user)
# ids of all the conferences for which the user has the 'volunteers_coordinator' role
conf_ids_for_volunteers_coordinator = Conference.with_role(:volunteers_coordinator, user).pluck(:id)
can [:index, :show, :update], Resource, conference_id: conf_ids_for_volunteers_coordinator
can :manage, Vposition, conference_id: conf_ids_for_volunteers_coordinator
can :manage, Vday, conference_id: conf_ids_for_volunteers_coordinator
# Abilities for Role (Conference resource)
can [:index, :show], Role
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
can :toggle_user, Role do |role|
role.resource_type == 'Conference' && role.name == 'volunteers_coordinator' &&
(Conference.with_role(:volunteers_coordinator, user).pluck(:id).include? role.resource_id)
end end
end end
end end

286
app/models/admin_ability.rb Normal file
View file

@ -0,0 +1,286 @@
class AdminAbility
include CanCan::Ability
def initialize(user)
# Order Abilities
# (Check https://github.com/CanCanCommunity/cancancan/wiki/Ability-Precedence)
# Check roles of user, using rolify. Role name is *case sensitive*
# user.is_organizer? or user.has_role? :organizer
# user.is_cfp_of? Conference or user.has_role? :cfp, Conference
# user.is_info_desk_of? Conference
# user.is_volunteers_coordinator_of? Conference
# user.is_attendee_of? Conference
# The following is wrong because a user will only have 'cfp' role for a specific conference
# user.is_cfp? # This is always false
user ||= User.new
signed_in_with_roles(user)
end
def common_abilities_for_roles(user)
can :manage, User, id: user.id
can :manage, Registration, user_id: user.id
can :index, Conference
can :show, Registration, &:new_record?
can [:new, :create], Registration do |registration|
conference = registration.conference
conference.registration_open? && !conference.registration_limit_exceeded? || conference.program.speakers.confirmed.include?(user)
end
can :index, Organization
can :index, Ticket
can :manage, TicketPurchase, user_id: user.id
can [:new, :create], Payment, user_id: user.id
can [:create, :destroy], Subscription, user_id: user.id
can [:new, :create], Event do |event|
event.program.cfp_open? && event.new_record?
end
can [:update, :show, :delete, :index], Event do |event|
event.users.include?(user)
end
# can manage the commercials of their own events
can :manage, Commercial, commercialable_type: 'Event', commercialable_id: user.events.pluck(:id)
can [:destroy], Openid
can :access, Admin
can :index, Commercial, commercialable_type: 'Conference'
cannot [:edit, :update, :destroy], Question, global: true
# for admins
can :manage, :all if user.is_admin
# even admin cannot create new users with ICHAIN enabled
cannot [:new, :create], User if ENV['OSEM_ICHAIN_ENABLED'] == 'true'
cannot :revert_object, PaperTrail::Version do |version|
(version.event == 'create' && %w[Conference User Event].include?(version.item_type))
end
cannot :revert_attribute, PaperTrail::Version do |version|
version.event != 'update' || version.item.nil?
end
# Can't create cfp if there are no available cfp types
cannot [:new, :create], Cfp do |cfp|
cfp.program.remaining_cfp_types.empty?
end
cannot :destroy, Program
# Do not delete venue, when there are rooms being used
cannot :destroy, Venue do |venue|
venue.conference.program.events.where.not(room_id: nil).any?
end
# Prevent requests for tracks from being destroyed
cannot :destroy, Track do |track|
track.self_organized?
end
end
# Abilities for signed in users with roles
def signed_in_with_roles(user)
signed_in_with_organization_admin_role(user) if user.has_role? :organization_admin, :any
signed_in_with_organizer_role(user) if user.has_role? :organizer, :any
signed_in_with_cfp_role(user) if user.has_role? :cfp, :any
signed_in_with_info_desk_role(user) if user.has_role? :info_desk, :any
signed_in_with_volunteers_coordinator_role(user) if user.has_role? :volunteers_coordinator, :any
signed_in_with_track_organizer_role(user) if user.has_role? :track_organizer, :any
common_abilities_for_roles(user)
end
def signed_in_with_organization_admin_role(user)
org_ids_for_organization_admin = Organization.with_role(:organization_admin, user).pluck(:id)
conf_ids_for_organization_admin = Conference.where(organization_id: org_ids_for_organization_admin).pluck(:id)
can [:read, :update, :destroy], Organization, id: org_ids_for_organization_admin
can :new, Conference
can :manage, Conference, organization_id: org_ids_for_organization_admin
can [:index, :show], Role
can [:edit, :update], Role do |role|
role.resource_type == 'Organization' && (org_ids_for_organization_admin.include? role.resource_id)
end
signed_in_with_organizer_role(user, conf_ids_for_organization_admin)
end
def signed_in_with_organizer_role(user, conf_ids_for_organization_admin = [])
# ids of all the conferences for which the user has the 'organizer' role and
# conferences that belong to organizations for which user is 'organization_admin'
conf_ids = conf_ids_for_organization_admin.concat(Conference.with_role(:organizer, user).pluck(:id)).uniq
# ids of all the tracks that belong to the programs of the above conferences
track_ids = Track.joins(:program).where('programs.conference_id IN (?)', conf_ids).pluck(:id)
can :manage, Resource, conference_id: conf_ids
can [:read, :update, :destroy], Conference, id: conf_ids
can :manage, Splashpage, conference_id: conf_ids
can :manage, Contact, conference_id: conf_ids
can :manage, EmailSettings, conference_id: conf_ids
can :manage, Campaign, conference_id: conf_ids
can :manage, Target, conference_id: conf_ids
can :manage, Commercial, commercialable_type: 'Conference',
commercialable_id: conf_ids
can :manage, Registration, conference_id: conf_ids
can :manage, RegistrationPeriod, conference_id: conf_ids
can :manage, Question, conference_id: conf_ids
can :manage, Question do |question|
!(question.conferences.pluck(:id) & conf_ids).empty?
end
can :manage, Vposition, conference_id: conf_ids
can :manage, Vday, conference_id: conf_ids
can :manage, Program, conference_id: conf_ids
can :manage, Schedule, program: { conference_id: conf_ids }
can :manage, EventSchedule, schedule: { program: { conference_id: conf_ids } }
can :manage, Cfp, program: { conference_id: conf_ids }
can :manage, Event, program: { conference_id: conf_ids }
can :manage, EventType, program: { conference_id: conf_ids }
can :manage, Track, program: { conference_id: conf_ids }
can :manage, DifficultyLevel, program: { conference_id: conf_ids }
can :manage, Commercial, commercialable_type: 'Event',
commercialable_id: Event.where(program_id: Program.where(conference_id: conf_ids).pluck(:id)).pluck(:id)
can :manage, Venue, conference_id: conf_ids
can :manage, Commercial, commercialable_type: 'Venue',
commercialable_id: Venue.where(conference_id: conf_ids).pluck(:id)
can :manage, Lodging, conference_id: conf_ids
can :manage, Room, venue: { conference_id: conf_ids }
can :manage, Sponsor, conference_id: conf_ids
can :manage, SponsorshipLevel, conference_id: conf_ids
can :manage, Ticket, conference_id: conf_ids
can :index, Comment, commentable_type: 'Event',
commentable_id: Event.where(program_id: Program.where(conference_id: conf_ids).pluck(:id)).pluck(:id)
# Abilities for Role (Conference resource)
can [:index, :show], Role do |role|
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
can [:edit, :update, :toggle_user], Role do |role|
role.resource_type == 'Conference' && (conf_ids.include? role.resource_id) ||
role.resource_type == 'Track' && (track_ids.include? role.resource_id)
end
can [:index, :revert_object, :revert_attribute], PaperTrail::Version do |version|
version.item_type == 'User' || (conf_ids.include? version.conference_id)
end
end
def signed_in_with_cfp_role(user)
# ids of all the conferences for which the user has the 'cfp' role
conf_ids_for_cfp = Conference.with_role(:cfp, user).pluck(:id)
can :show, Conference do |conf|
conf_ids_for_cfp.include?(conf.id)
end
can [:index, :show, :update], Resource, conference_id: conf_ids_for_cfp
can :manage, Event, program: { conference_id: conf_ids_for_cfp }
can :manage, EventType, program: { conference_id: conf_ids_for_cfp }
can :manage, Track, program: { conference_id: conf_ids_for_cfp }
can :manage, DifficultyLevel, program: { conference_id: conf_ids_for_cfp }
can :manage, EmailSettings, conference_id: conf_ids_for_cfp
can :manage, Schedule, program: { conference_id: conf_ids_for_cfp }
can :manage, Room, venue: { conference_id: conf_ids_for_cfp }
can :show, Venue, conference_id: conf_ids_for_cfp
can :show, Commercial, commercialable_type: 'Venue', commercialable_id: Venue.where(conference_id: conf_ids_for_cfp).pluck(:id)
can :manage, Cfp, program: { conference_id: conf_ids_for_cfp }
can :manage, Program, conference_id: conf_ids_for_cfp
can :manage, Commercial, commercialable_type: 'Event',
commercialable_id: Event.where(program_id: Program.where(conference_id: conf_ids_for_cfp).pluck(:id)).pluck(:id)
can :index, Comment, commentable_type: 'Event',
commentable_id: Event.where(program_id: Program.where(conference_id: conf_ids_for_cfp).pluck(:id)).pluck(:id)
# Abilities for Role (Conference resource)
can [:index, :show], Role do |role|
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
can :toggle_user, Role do |role|
role.resource_type == 'Conference' && role.name == 'cfp' &&
(Conference.with_role(:cfp, user).pluck(:id).include? role.resource_id)
end
can [:index, :revert_object, :revert_attribute], PaperTrail::Version, item_type: 'Event', conference_id: conf_ids_for_cfp
can [:index, :revert_object, :revert_attribute], PaperTrail::Version, item_type: 'Vote', conference_id: conf_ids_for_cfp
can [:index, :revert_object, :revert_attribute], PaperTrail::Version do |version|
version.item_type == 'Commercial' && conf_ids_for_cfp.include?(version.conference_id) &&
(version.object.to_s.include?('Event') || version.object_changes.to_s.include?('Event'))
end
end
def signed_in_with_info_desk_role(user)
# ids of all the conferences for which the user has the 'info_desk' role
conf_ids_for_info_desk = Conference.with_role(:info_desk, user).pluck(:id)
can :show, Conference do |conf|
conf_ids_for_info_desk.include?(conf.id)
end
can [:index, :show, :update], Resource, conference_id: conf_ids_for_info_desk
can :manage, Registration, conference_id: conf_ids_for_info_desk
can :manage, Question, conference_id: conf_ids_for_info_desk
can :manage, Question do |question|
!(question.conferences.pluck(:id) & conf_ids_for_info_desk).empty?
end
# Abilities for Role (Conference resource)
can [:index, :show], Role do |role|
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
can :toggle_user, Role do |role|
role.resource_type == 'Conference' && role.name == 'info_desk' &&
(Conference.with_role(:info_desk, user).pluck(:id).include? role.resource_id)
end
end
def signed_in_with_volunteers_coordinator_role(user)
# ids of all the conferences for which the user has the 'volunteers_coordinator' role
conf_ids_for_volunteers_coordinator = Conference.with_role(:volunteers_coordinator, user).pluck(:id)
can :show, Conference do |conf|
conf_ids_for_volunteers_coordinator.include?(conf.id)
end
can [:index, :show, :update], Resource, conference_id: conf_ids_for_volunteers_coordinator
can :manage, Vposition, conference_id: conf_ids_for_volunteers_coordinator
can :manage, Vday, conference_id: conf_ids_for_volunteers_coordinator
# Abilities for Role (Conference resource)
can [:index, :show], Role do |role|
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
can :toggle_user, Role do |role|
role.resource_type == 'Conference' && role.name == 'volunteers_coordinator' &&
(Conference.with_role(:volunteers_coordinator, user).pluck(:id).include? role.resource_id)
end
end
def signed_in_with_track_organizer_role(user)
# ids of all the conferences for which the user has the 'track organizer' role
conf_ids_for_track_organizer = Track.with_role(:track_organizer, user).joins(:program).pluck(:conference_id)
# ids of all the tracks for which the user has the 'track_organizer' role
track_ids_for_track_organizer = Track.with_role(:track_organizer, user).pluck(:id)
can :show, Conference do |conf|
conf_ids_for_track_organizer.include?(conf.id)
end
# Show Program in the admin sidebar
can :show, Program, conference_id: conf_ids_for_track_organizer
# Show Tracks in the admin sidebar
can :update, Track do |track|
track.new_record? && conf_ids_for_track_organizer.include?(track.program.conference_id)
end
can :manage, Track, id: track_ids_for_track_organizer
# Show Roles in the admin sidebar and allow authorization of the index action
can [:index, :show], Role do |role|
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
can :toggle_user, Role do |role|
role.resource_type == 'Track' && track_ids_for_track_organizer.include?(role.resource_id)
end
end
end

View file

@ -1,6 +1,10 @@
# cannot delete program if there are events submitted # cannot delete program if there are events submitted
class Cfp < ActiveRecord::Base class Cfp < ActiveRecord::Base
TYPES = %w(events booths).freeze
scope :for_events, (-> { find_by(cfp_type: 'events') })
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id } has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
belongs_to :program belongs_to :program
@ -8,6 +12,15 @@ class Cfp < ActiveRecord::Base
validates :start_date, :end_date, presence: true validates :start_date, :end_date, presence: true
validate :before_end_of_conference validate :before_end_of_conference
validate :start_after_end_date validate :start_after_end_date
validates :cfp_type,
presence: true,
inclusion: {
in: TYPES
},
uniqueness: {
scope: :program,
case_sensitive: false
}
## ##
# Checks whether cfp date is updated # Checks whether cfp date is updated
@ -16,11 +29,11 @@ class Cfp < ActiveRecord::Base
# * +True+ -> If cfp dates is updated and all other parameters are set # * +True+ -> If cfp dates is updated and all other parameters are set
# * +False+ -> Either cfp date is not updated or one or more parameter is not set # * +False+ -> Either cfp date is not updated or one or more parameter is not set
def notify_on_cfp_date_update? def notify_on_cfp_date_update?
!self.end_date.blank? && !self.start_date.blank?\ !end_date.blank? && !start_date.blank?\
&& (self.start_date_changed? || self.end_date_changed?)\ && (start_date_changed? || end_date_changed?)\
&& self.program.conference.email_settings.send_on_cfp_dates_updated\ && program.conference.email_settings.send_on_cfp_dates_updated\
&& !self.program.conference.email_settings.cfp_dates_updated_subject.blank?\ && !program.conference.email_settings.cfp_dates_updated_subject.blank?\
&& !self.program.conference.email_settings.cfp_dates_updated_body.blank? && !program.conference.email_settings.cfp_dates_updated_body.blank?
end end
## ##
@ -51,23 +64,37 @@ class Cfp < ActiveRecord::Base
end end
def remaining_days(date = Date.today) def remaining_days(date = Date.today)
result = (self.end_date - date).to_i result = (end_date - date).to_i
result > 0 ? result : 0 result > 0 ? result : 0
end end
##
# Checks if the call for papers is currently open
#
# ====Returns
# * +false+ -> If the CFP is not set or today isn't in the CFP period.
# * +true+ -> If today is in the CFP period.
def open?
(start_date..end_date).cover?(Date.current)
end
private private
def before_end_of_conference def before_end_of_conference
errors. if program && program.conference && program.conference.end_date && end_date && (end_date > program.conference.end_date)
add(:end_date, "can't be after the conference end date (#{program.conference.end_date})") if program.conference && program.conference.end_date && end_date && (end_date > program.conference.end_date) errors
.add(:end_date, "can't be after the conference end date (#{program.conference.end_date})")
end
errors. if program && program.conference && program.conference.end_date && start_date && (start_date > program.conference.end_date)
add(:start_date, "can't be after the conference end date (#{program.conference.end_date})") if program.conference && program.conference.end_date && start_date && (start_date > program.conference.end_date) errors
.add(:start_date, "can't be after the conference end date (#{program.conference.end_date})")
end
end end
def start_after_end_date def start_after_end_date
errors. errors
add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date .add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date
end end
def conference_id def conference_id

View file

@ -1,19 +1,19 @@
class Comment < ActiveRecord::Base class Comment < ActiveRecord::Base
acts_as_nested_set scope: [:commentable_id, :commentable_type] acts_as_nested_set scope: %i(commentable_id commentable_type)
validates_presence_of :body validates :body, presence: true
validates_presence_of :user validates :user, presence: true
after_create :send_notification after_create :send_notification
# NOTE: install the acts_as_votable plugin if you # NOTE: install the acts_as_votable plugin if you
# want user to vote on the quality of comments. # want user to vote on the quality of comments.
#acts_as_votable #acts_as_votable
belongs_to :commentable, polymorphic: true belongs_to :commentable, counter_cache: true, polymorphic: true
# NOTE: Comments belong to a user # NOTE: Comments belong to a user
belongs_to :user belongs_to :user
has_paper_trail on: [:create, :destroy], meta: { conference_id: :conference_id } has_paper_trail on: %i(create destroy), meta: { conference_id: :conference_id }
# Helper class method that allows you to build a comment # Helper class method that allows you to build a comment
# by passing a commentable object, a user_id, and comment text # by passing a commentable object, a user_id, and comment text
@ -27,7 +27,7 @@ class Comment < ActiveRecord::Base
#helper method to check if a comment has children #helper method to check if a comment has children
def has_children? def has_children?
self.children.any? children.any?
end end
# Helper class method to lookup all comments assigned # Helper class method to lookup all comments assigned

View file

@ -0,0 +1,11 @@
module RevisionCount
extend ActiveSupport::Concern
included do
after_update :increment_revision
end
def increment_revision
conference.update_column(:revision, conference.revision + 1)
end
end

View file

@ -1,4 +1,5 @@
class Conference < ActiveRecord::Base class Conference < ActiveRecord::Base
include RevisionCount
require 'uri' require 'uri'
serialize :events_per_week, Hash serialize :events_per_week, Hash
# Needed to call 'Conference.with_role' in /models/ability.rb # Needed to call 'Conference.with_role' in /models/ability.rb
@ -6,8 +7,12 @@ class Conference < ActiveRecord::Base
resourcify :roles, dependent: :delete_all resourcify :roles, dependent: :delete_all
default_scope { order('start_date DESC') } default_scope { order('start_date DESC') }
scope :upcoming, (-> { where('end_date >= ?', Date.current) })
scope :past, (-> { where('end_date < ?', Date.current) })
has_paper_trail ignore: [:updated_at, :guid, :revision, :events_per_week], meta: { conference_id: :id } belongs_to :organization
has_paper_trail ignore: %i(updated_at guid revision events_per_week), meta: { conference_id: :id }
has_and_belongs_to_many :questions has_and_belongs_to_many :questions
@ -17,6 +22,7 @@ class Conference < ActiveRecord::Base
has_one :email_settings, dependent: :destroy has_one :email_settings, dependent: :destroy
has_one :program, dependent: :destroy has_one :program, dependent: :destroy
has_one :venue, dependent: :destroy has_one :venue, dependent: :destroy
has_many :physical_tickets, through: :ticket_purchases
has_many :ticket_purchases, dependent: :destroy has_many :ticket_purchases, dependent: :destroy
has_many :payments, dependent: :destroy has_many :payments, dependent: :destroy
has_many :supporters, through: :ticket_purchases, source: :user has_many :supporters, through: :ticket_purchases, source: :user
@ -48,15 +54,17 @@ class Conference < ActiveRecord::Base
mount_uploader :picture, PictureUploader, mount_on: :logo_file_name mount_uploader :picture, PictureUploader, mount_on: :logo_file_name
validates_presence_of :title, validates :title,
:short_title, :short_title,
:start_date, :start_date,
:end_date, :end_date,
:start_hour, :start_hour,
:end_hour :end_hour,
:ticket_layout,
:organization, presence: true
validates_uniqueness_of :short_title validates :short_title, uniqueness: true
validates_format_of :short_title, with: /\A[a-zA-Z0-9_-]*\z/ validates :short_title, format: { with: /\A[a-zA-Z0-9_-]*\z/ }
validates :registration_limit, numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :registration_limit, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
# This validation is needed since a conference with a start date greater than the end date is not possible # This validation is needed since a conference with a start date greater than the end date is not possible
@ -67,6 +75,9 @@ class Conference < ActiveRecord::Base
before_create :create_email_settings before_create :create_email_settings
after_create :create_free_ticket after_create :create_free_ticket
after_update :delete_event_schedules
enum ticket_layout: [:portrait, :landscape]
## ##
# Checks if the user is registered to the conference # Checks if the user is registered to the conference
@ -80,6 +91,20 @@ class Conference < ActiveRecord::Base
user.present? && registrations.where(user_id: user.id).count > 0 user.present? && registrations.where(user_id: user.id).count > 0
end end
##
# Delete all EventSchedules that are not in the hours range
# After the conference has been successfully updated
def delete_event_schedules
if start_hour_changed? || end_hour_changed?
event_schedules = program.event_schedules.select do |event_schedule|
event_schedule.start_time.hour < start_hour ||
event_schedule.end_time.hour > end_hour ||
(event_schedule.end_time.hour == end_hour && event_schedule.end_time.minute > 0)
end
event_schedules.each(&:destroy)
end
end
## ##
# Checks if the registration for the conference is currently open # Checks if the registration for the conference is currently open
# #
@ -113,7 +138,7 @@ class Conference < ActiveRecord::Base
result = [] result = []
if program && program.cfp && program.events if program && program.cfp && program.events
submissions = program.events.group(:week).count submissions = program.events.select(:week).group(:week).order(:week).count
start_week = program.cfp.start_week start_week = program.cfp.start_week
weeks = program.cfp.weeks weeks = program.cfp.weeks
result = calculate_items_per_week(start_week, weeks, submissions) result = calculate_items_per_week(start_week, weeks, submissions)
@ -161,7 +186,7 @@ class Conference < ActiveRecord::Base
registration_period.start_date && registration_period.start_date &&
registration_period.end_date registration_period.end_date
reg = registrations.group(:week).count reg = registrations.group(:week).order(:week).count
start_week = get_registration_start_week start_week = get_registration_start_week
weeks = registration_weeks weeks = registration_weeks
result = calculate_items_per_week(start_week, weeks, reg) result = calculate_items_per_week(start_week, weeks, reg)
@ -169,6 +194,59 @@ class Conference < ActiveRecord::Base
result result
end end
##
# Returns an array with the summarized ticket sales per week.
#
# ====Returns
# * +Array+ -> e.g. [0, 3, 3, 5] -> first week 0, second week 3 tickets sold
def get_tickets_sold_per_week
result = []
if tickets && ticket_purchases && registration_period
tickets_sold = ticket_purchases.paid.group(:week).sum(:quantity)
start_week = get_registration_start_week
weeks = registration_weeks
result = calculate_items_per_week(start_week, weeks, tickets_sold)
end
result
end
##
# Returns an hash with ticket sales by ticket title
# per week.
#
# ====Returns
# * +Array+ -> e.g. 'Free Access' => [0, 3, 3, 5] -> first week 0 tickets sold, second week 3 tickets sold.
def get_tickets_data
result = {}
if tickets && ticket_purchases && registration_period
tickets_per_ticket_id_and_week = ticket_purchases.paid.group(:ticket_id, :week).sum(:quantity)
start_week = get_registration_start_week
weeks = registration_weeks
tickets_by_id_per_week = {}
tickets.each do |ticket|
tickets_by_id_per_week[ticket.id] = {}
(start_week...(start_week + weeks)).each do |week|
tickets_by_id_per_week[ticket.id][week] = 0
end
end
tickets_per_ticket_id_and_week.each do |ticket_week, value|
tickets_by_id_per_week[ticket_week[0]][ticket_week[1]] = value
end
tickets_by_id_per_week.each do |ticket, values|
result[Ticket.find(ticket).title] = pad_array_left_not_kumulative(start_week, values)
end
result['Weeks'] = weeks > 0 ? (1..weeks).to_a : 0
end
result
end
## ##
# Calculates how many weeks the registration is. # Calculates how many weeks the registration is.
# #
@ -180,8 +258,8 @@ class Conference < ActiveRecord::Base
if registration_period && if registration_period &&
registration_period.start_date && registration_period.start_date &&
registration_period.end_date registration_period.end_date
weeks = Date.new(registration_period.start_date.year, 12, 31). weeks = Date.new(registration_period.start_date.year, 12, 31)
strftime('%W').to_i .strftime('%W').to_i
result = get_registration_end_week - get_registration_start_week + 1 result = get_registration_end_week - get_registration_start_week + 1
end end
@ -266,8 +344,8 @@ class Conference < ActiveRecord::Base
# ====Returns # ====Returns
# * +hash+ -> user: submissions # * +hash+ -> user: submissions
def self.get_top_submitter(limit = 5) def self.get_top_submitter(limit = 5)
submitter = EventUser.where('event_role = ?', 'submitter').limit(limit).group(:user_id) submitter = EventUser.select(:user_id).where('event_role = ?', 'submitter').limit(limit).group(:user_id)
counter = submitter.order('count_all desc').count counter = submitter.order('count_all desc').count(:all)
calculate_user_submission_hash(submitter, counter) calculate_user_submission_hash(submitter, counter)
end end
@ -277,10 +355,10 @@ class Conference < ActiveRecord::Base
# ====Returns # ====Returns
# * +hash+ -> user: submissions # * +hash+ -> user: submissions
def get_top_submitter(limit = 5) def get_top_submitter(limit = 5)
submitter = EventUser.joins(:event). submitter = EventUser.joins(:event).select(:user_id)
where('event_role = ? and program_id = ?', 'submitter', Conference.find(id).program.id). .where('event_role = ? and program_id = ?', 'submitter', Conference.find(id).program.id)
limit(limit).group(:user_id) .limit(limit).group(:user_id)
counter = submitter.order('count_all desc').count counter = submitter.order('count_all desc').count(:all)
Conference.calculate_user_submission_hash(submitter, counter) Conference.calculate_user_submission_hash(submitter, counter)
end end
@ -311,9 +389,10 @@ class Conference < ActiveRecord::Base
# ====Returns # ====Returns
# * +hash+ -> hash # * +hash+ -> hash
def scheduled_event_distribution def scheduled_event_distribution
confirmed_events = program.events.where(state: 'confirmed') confirmed_scheduled_events = program.events.confirmed.scheduled(program.selected_schedule.try(:id))
scheduled_value = { 'value' => confirmed_events.where.not(start_time: nil).count, 'color' => 'green' } confirmed_unscheduled_events = program.events.confirmed - confirmed_scheduled_events
unscheduled_value = { 'value' => confirmed_events.where(start_time: nil).count, 'color' => 'red' } scheduled_value = { 'value' => confirmed_scheduled_events.count, 'color' => 'green' }
unscheduled_value = { 'value' => confirmed_unscheduled_events.count, 'color' => 'red' }
{ 'Scheduled' => scheduled_value, 'Unscheduled' => unscheduled_value } { 'Scheduled' => scheduled_value, 'Unscheduled' => unscheduled_value }
end end
@ -380,6 +459,46 @@ class Conference < ActiveRecord::Base
calculate_user_distribution_hash(active_user, unconfirmed_user, dead_user) calculate_user_distribution_hash(active_user, unconfirmed_user, dead_user)
end end
##
# Returns a hash with per ticket sales => { "Title" => { value: number of tickets sold,
# color: generated from the title using a hash function }, ...}
#
# ====Returns
# * +hash+ -> hash
def tickets_sold_distribution
result = {}
if tickets && ticket_purchases
tickets.each do |ticket|
result[ticket.title] = {
'value' => ApplicationController.helpers.humanized_money(ticket.tickets_sold).delete(',').to_i,
'color' => "\##{Digest::MD5.hexdigest(ticket.title)[0..5]}"
}
end
end
result
end
##
# Returns a hash with per ticket turnover => { "Title" => { value: ticket turnover,
# color: generated from the title using a hash function }, ...}
#
# ====Returns
# * +hash+ -> hash
def tickets_turnover_distribution
result = {}
if tickets && ticket_purchases
tickets.each do |ticket|
result[ticket.title] = {
'value' => ApplicationController.helpers.humanized_money(ticket.tickets_turnover).delete(',').to_i,
'color' => "\##{Digest::MD5.hexdigest(ticket.title)[0..5]}"
}
end
end
result
end
## ##
# Calculates the overall program minutes # Calculates the overall program minutes
# #
@ -444,10 +563,10 @@ class Conference < ActiveRecord::Base
# ====Returns # ====Returns
# * +hash+ -> track => {color, value} # * +hash+ -> track => {color, value}
def tracks_distribution(state = nil) def tracks_distribution(state = nil)
if state tracks_grouped = if state
tracks_grouped = program.events.select(:track_id).where('state = ?', state).group(:track_id) program.events.select(:track_id).where('state = ?', state).group(:track_id)
else else
tracks_grouped = program.events.select(:track_id).group(:track_id) program.events.select(:track_id).group(:track_id)
end end
tracks_counted = tracks_grouped.count tracks_counted = tracks_grouped.count
@ -461,13 +580,13 @@ class Conference < ActiveRecord::Base
# ====Returns # ====Returns
# * +ActiveRecord+ # * +ActiveRecord+
def self.get_active_conferences_for_dashboard def self.get_active_conferences_for_dashboard
result = Conference.where('start_date > ?', Time.now). result = Conference.where('start_date > ?', Time.now)
select('id, short_title, color, start_date') .select('id, short_title, color, start_date')
if result.length == 0 if result.empty?
result = Conference. result = Conference
select('id, short_title, color, start_date').limit(2). .select('id, short_title, color, start_date').limit(2)
order(start_date: :desc) .order(start_date: :desc)
end end
result result
end end
@ -531,7 +650,7 @@ class Conference < ActiveRecord::Base
def self.write_event_distribution_to_db def self.write_event_distribution_to_db
week = DateTime.now.end_of_week week = DateTime.now.end_of_week
Conference.where('end_date > ?', Date.today).each do |conference| Conference.where('end_date > ?', Date.today).find_each do |conference|
result = {} result = {}
Event.state_machine.states.each do |state| Event.state_machine.states.each do |state|
count = conference.program.events.where('state = ?', state.name).count count = conference.program.events.where('state = ?', state.name).count
@ -555,11 +674,11 @@ class Conference < ActiveRecord::Base
# * +True+ -> If conference is updated and all other parameters are set # * +True+ -> If conference is updated and all other parameters are set
# * +False+ -> Either conference is not updated or one or more parameter is not set # * +False+ -> Either conference is not updated or one or more parameter is not set
def notify_on_dates_changed? def notify_on_dates_changed?
return false unless self.email_settings.send_on_conference_dates_updated return false unless email_settings.send_on_conference_dates_updated
# do not notify unless one of the dates changed # do not notify unless one of the dates changed
return false unless self.start_date_changed? || self.end_date_changed? return false unless start_date_changed? || end_date_changed?
# do not notify unless the mail content is set up # do not notify unless the mail content is set up
(!email_settings.conference_dates_updated_subject.blank? && !email_settings.conference_dates_updated_body.blank?) (email_settings.conference_dates_updated_subject.present? && email_settings.conference_dates_updated_body.present?)
end end
## ##
@ -569,17 +688,24 @@ class Conference < ActiveRecord::Base
# * +True+ -> If registration dates is updated and all other parameters are set # * +True+ -> If registration dates is updated and all other parameters are set
# * +False+ -> Either registration date is not updated or one or more parameter is not set # * +False+ -> Either registration date is not updated or one or more parameter is not set
def notify_on_registration_dates_changed? def notify_on_registration_dates_changed?
return false unless self.email_settings.send_on_conference_registration_dates_updated return false unless email_settings.send_on_conference_registration_dates_updated
# do not notify unless we allow a registration # do not notify unless we allow a registration
return false unless self.registration_period return false unless registration_period
# do not notify unless one of the dates changed # do not notify unless one of the dates changed
return false unless registration_period.start_date_changed? || registration_period.end_date_changed? return false unless registration_period.start_date_changed? || registration_period.end_date_changed?
# do not notify unless the mail content is set up # do not notify unless the mail content is set up
(!email_settings.conference_registration_dates_updated_subject.blank? && !email_settings.conference_registration_dates_updated_body.blank?) (email_settings.conference_registration_dates_updated_subject.present? && email_settings.conference_registration_dates_updated_body.present?)
end end
##
# Checks if the registration limit has been exceeded
# Additionally, it takes into account the confirmed speakers that haven't registered yet
#
# ====Returns
# * +True+ -> If the registration limit has been reached or exceeded
# * +False+ -> If the registration limit hasn't been exceeded
def registration_limit_exceeded? def registration_limit_exceeded?
registration_limit > 0 && registrations.count >= registration_limit registration_limit > 0 && registrations.count + program.speakers.confirmed.count - program.speakers.confirmed.registered(program.conference).count >= registration_limit
end end
# Returns an hexadecimal color given a collection. The returned color changed # Returns an hexadecimal color given a collection. The returned color changed
@ -611,6 +737,15 @@ class Conference < ActiveRecord::Base
(start_hour..(end_hour - 1)).cover?(current_hour) ? current_hour - start_hour : 0 (start_hour..(end_hour - 1)).cover?(current_hour) ? current_hour - start_hour : 0
end end
##
# Return the current conference object to be used in RevisionCount
#
# ====Returns
# * +ActiveRecord+
def conference
self
end
private private
# Returns a different html colour for every i and consecutive colors are # Returns a different html colour for every i and consecutive colors are
@ -628,8 +763,8 @@ class Conference < ActiveRecord::Base
end end
after_create do after_create do
self.create_contact create_contact
self.create_program create_program
create_roles create_roles
end end
@ -699,7 +834,7 @@ class Conference < ActiveRecord::Base
# Completed weeks # Completed weeks
events_per_week.each do |week, values| events_per_week.each do |week, values|
values.each do |state, value| values.each do |state, value|
if [:confirmed, :unconfirmed].include?(state) if %i(confirmed unconfirmed).include?(state)
unless result[state.to_s.capitalize] unless result[state.to_s.capitalize]
result[state.to_s.capitalize] = {} result[state.to_s.capitalize] = {}
end end
@ -866,10 +1001,10 @@ class Conference < ActiveRecord::Base
# ====Returns # ====Returns
# * +hash+ -> object_type => {color, value} # * +hash+ -> object_type => {color, value}
def calculate_event_distribution(group_by_id, association_symbol, state = nil) def calculate_event_distribution(group_by_id, association_symbol, state = nil)
if state grouped = if state
grouped = program.events.select(group_by_id).where('state = ?', 'confirmed').group(group_by_id) program.events.select(group_by_id).where('state = ?', 'confirmed').group(group_by_id)
else else
grouped = program.events.select(group_by_id).group(group_by_id) program.events.select(group_by_id).group(group_by_id)
end end
counted = grouped.count counted = grouped.count
@ -981,7 +1116,8 @@ class Conference < ActiveRecord::Base
def self.calculate_user_submission_hash(submitters, counter) def self.calculate_user_submission_hash(submitters, counter)
result = ActiveSupport::OrderedHash.new result = ActiveSupport::OrderedHash.new
counter.each do |key, value| counter.each do |key, value|
submitter = submitters.where(user_id: key).first # make PG happy by including the user_id in ORDER
submitter = submitters.where(user_id: key).order(:user_id).first
if submitter if submitter
result[submitter.user] = value result[submitter.user] = value
end end

View file

@ -1,5 +1,6 @@
class Event < ActiveRecord::Base class Event < ActiveRecord::Base
include ActiveRecord::Transitions include ActiveRecord::Transitions
include RevisionCount
has_paper_trail on: [:create, :update], ignore: [:updated_at, :guid, :week], meta: { conference_id: :conference_id } has_paper_trail on: [:create, :update], ignore: [:updated_at, :guid, :week], meta: { conference_id: :conference_id }
acts_as_commentable acts_as_commentable
@ -8,7 +9,13 @@ class Event < ActiveRecord::Base
has_many :event_users, dependent: :destroy has_many :event_users, dependent: :destroy
has_many :users, through: :event_users has_many :users, through: :event_users
has_many :speakers, through: :event_users, source: :user
has_many :speaker_event_users, -> { where(event_role: 'speaker') }, class_name: 'EventUser'
has_many :speakers, through: :speaker_event_users, source: :user
has_one :submitter_event_user, -> { where(event_role: 'submitter') }, class_name: 'EventUser'
has_one :submitter, through: :submitter_event_user, source: :user
has_many :votes, dependent: :destroy has_many :votes, dependent: :destroy
has_many :voters, through: :votes, source: :user has_many :voters, through: :votes, source: :user
has_many :commercials, as: :commercialable, dependent: :destroy has_many :commercials, as: :commercialable, dependent: :destroy
@ -23,6 +30,7 @@ class Event < ActiveRecord::Base
belongs_to :program belongs_to :program
accepts_nested_attributes_for :event_users, allow_destroy: true accepts_nested_attributes_for :event_users, allow_destroy: true
accepts_nested_attributes_for :speakers, allow_destroy: true
accepts_nested_attributes_for :users accepts_nested_attributes_for :users
before_create :generate_guid before_create :generate_guid
@ -33,6 +41,7 @@ class Event < ActiveRecord::Base
validates :abstract, presence: true validates :abstract, presence: true
validates :event_type, presence: true validates :event_type, presence: true
validates :program, presence: true validates :program, presence: true
validates :speakers, presence: true
validates :max_attendees, numericality: { only_integer: true, greater_than_or_equal_to: 1, allow_nil: true } validates :max_attendees, numericality: { only_integer: true, greater_than_or_equal_to: 1, allow_nil: true }
validate :max_attendees_no_more_than_room_size validate :max_attendees_no_more_than_room_size
@ -107,26 +116,22 @@ class Event < ActiveRecord::Base
def average_rating def average_rating
@total_rating = 0 @total_rating = 0
votes.each do |vote| votes.each do |vote|
@total_rating = @total_rating + vote.rating @total_rating += vote.rating
end end
@total = votes.size @total = votes.size
@total_rating > 0 ? number_with_precision(@total_rating / @total.to_f, precision: 2, strip_insignificant_zeros: true) : 0 @total_rating > 0 ? number_with_precision(@total_rating / @total.to_f, precision: 2, strip_insignificant_zeros: true) : 0
end end
def submitter # get event speakers with the event sumbmitter at the first position
result = event_users.where(event_role: 'submitter').first # if the submitter is also a speaker for this event
if result.nil? def speakers_ordered
user = nil speakers_list = speakers.to_a
# Perhaps the event_users haven't been saved, if this is a new proposal
event_users.each do |u| if speakers_list.reject! { |speaker| speaker == submitter }
if u.event_role == 'submitter' speakers_list.unshift(submitter)
user = u.user
end
end
user
else
result.user
end end
speakers_list
end end
def transition_possible?(transition) def transition_possible?(transition)
@ -185,12 +190,12 @@ class Event < ActiveRecord::Base
end end
begin begin
if mail if mail
self.send(transition, send(transition,
send_mail: send_mail_param) send_mail: send_mail_param)
else else
self.send(transition) send(transition)
end end
self.save save
rescue Transitions::InvalidTransition => e rescue Transitions::InvalidTransition => e
alert = "Update state failed. #{e.message}" alert = "Update state failed. #{e.message}"
end end
@ -210,12 +215,12 @@ class Event < ActiveRecord::Base
# Returns +Hash+ # Returns +Hash+
def progress_status def progress_status
{ {
registered: self.program.conference.user_registered?(self.submitter), registered: speakers.all? { |speaker| program.conference.user_registered? speaker },
commercials: self.commercials.any?, commercials: commercials.any?,
biography: !self.submitter.biography.blank?, biographies: speakers.all? { |speaker| !speaker.biography.blank? },
subtitle: !self.subtitle.blank?, subtitle: !subtitle.blank?,
track: (!self.track.blank? unless self.program.tracks.empty?), track: (!track.blank? unless program.tracks.empty?),
difficulty_level: !self.difficulty_level.blank?, difficulty_level: !difficulty_level.blank?,
title: true, title: true,
abstract: true abstract: true
}.with_indifferent_access }.with_indifferent_access
@ -227,7 +232,7 @@ class Event < ActiveRecord::Base
# ====Returns # ====Returns
# * +String+ -> Progress in Percent # * +String+ -> Progress in Percent
def calculate_progress def calculate_progress
result = self.progress_status result = progress_status
(100 * result.values.count(true) / result.values.compact.count).to_s (100 * result.values.count(true) / result.values.compact.count).to_s
end end
@ -247,6 +252,10 @@ class Event < ActiveRecord::Base
event_schedules.find_by(schedule_id: program.selected_schedule_id).try(:start_time) event_schedules.find_by(schedule_id: program.selected_schedule_id).try(:start_time)
end end
def conference
program.conference
end
private private
## ##
@ -280,14 +289,14 @@ class Event < ActiveRecord::Base
def set_week def set_week
self.week = created_at.strftime('%W') self.week = created_at.strftime('%W')
self.without_versioning do without_versioning do
self.save! save!
end end
end end
def before_end_of_conference def before_end_of_conference
errors. errors
add(:created_at, "can't be after the conference end date!") if program.conference && program.conference.end_date && .add(:created_at, "can't be after the conference end date!") if program.conference && program.conference.end_date &&
(Date.today > program.conference.end_date) (Date.today > program.conference.end_date)
end end

View file

@ -10,6 +10,8 @@ class EventSchedule < ActiveRecord::Base
validates :room, presence: true validates :room, presence: true
validates :start_time, presence: true validates :start_time, presence: true
validates :event, uniqueness: { scope: :schedule } validates :event, uniqueness: { scope: :schedule }
validate :start_after_end_hour
validate :start_before_start_hour
scope :confirmed, -> { joins(:event).where('state = ?', 'confirmed') } scope :confirmed, -> { joins(:event).where('state = ?', 'confirmed') }
scope :canceled, -> { joins(:event).where('state = ?', 'canceled') } scope :canceled, -> { joins(:event).where('state = ?', 'canceled') }
@ -37,6 +39,16 @@ class EventSchedule < ActiveRecord::Base
private private
def start_after_end_hour
return unless event && start_time && event.program && event.program.conference && event.program.conference.end_hour
errors.add(:start_time, "can't be after the conference end hour (#{event.program.conference.end_hour})") if start_time.hour >= event.program.conference.end_hour
end
def start_before_start_hour
return unless event && start_time && event.program && event.program.conference && event.program.conference.start_hour
errors.add(:start_time, "can't be before the conference start hour (#{event.program.conference.start_hour})") if start_time.hour < event.program.conference.start_hour
end
def conference_id def conference_id
schedule.program.conference_id schedule.program.conference_id
end end

View file

@ -15,17 +15,13 @@ class EventType < ActiveRecord::Base
alias_attribute :name, :title alias_attribute :name, :title
# If LENGTH_STEP must be divisor of 60, otherwise the schedule wont be displayed properly
LENGTH_STEP = defined?(SCHEDULE_CELL_SIZE) ? SCHEDULE_CELL_SIZE : 15
private private
## ##
# Check if length is multiple of LENGTH_STEP. Used as validation. # Check if length is a divisor of program schedule cell size. Used as validation.
# #
def length_step def length_step
errors.add(:length, "must be multiple of #{LENGTH_STEP}") if length % LENGTH_STEP != 0 errors.add(:length, "must be a divisor of #{program.schedule_interval}") if program && length % program.schedule_interval != 0
end end
def capitalize_color def capitalize_color

View file

@ -1,6 +1,6 @@
class EventUser < ActiveRecord::Base class EventUser < ActiveRecord::Base
# TODO Do we need these roles? # TODO: Do we need these roles?
ROLES = [['Speaker', 'speaker'], ['Submitter', 'submitter'], ['Moderator', 'moderator']] ROLES = [%w[Speaker speaker], %w[Submitter submitter], %w[Moderator moderator]]
belongs_to :event belongs_to :event
belongs_to :user belongs_to :user

View file

@ -0,0 +1,17 @@
class Organization < ActiveRecord::Base
resourcify :roles, dependent: :delete_all
has_many :conferences, dependent: :destroy
after_create :create_roles
validates :name, presence: true
mount_uploader :picture, PictureUploader, mount_on: :picture
private
def create_roles
roles.where(name: 'organization_admin').first_or_create(description: 'For the administrators of an organization and its conferences')
end
end

View file

@ -0,0 +1,7 @@
class PhysicalTicket < ActiveRecord::Base
belongs_to :ticket_purchase
has_one :ticket, through: :ticket_purchase
has_one :conference, through: :ticket_purchase
has_one :user, through: :ticket_purchase
has_many :ticket_scannings
end

View file

@ -5,11 +5,12 @@ class Program < ActiveRecord::Base
belongs_to :conference belongs_to :conference
has_one :cfp, dependent: :destroy has_many :cfps, dependent: :destroy
has_many :event_types, dependent: :destroy has_many :event_types, dependent: :destroy
has_many :tracks, dependent: :destroy has_many :tracks, dependent: :destroy
has_many :difficulty_levels, dependent: :destroy has_many :difficulty_levels, dependent: :destroy
has_many :schedules, dependent: :destroy has_many :schedules, dependent: :destroy
has_many :event_schedules, through: :schedules
belongs_to :selected_schedule, class_name: 'Schedule' belongs_to :selected_schedule, class_name: 'Schedule'
has_many :events, dependent: :destroy do has_many :events, dependent: :destroy do
def require_registration def require_registration
@ -38,12 +39,17 @@ class Program < ActiveRecord::Base
where(state: :confirmed, is_highlight: true) where(state: :confirmed, is_highlight: true)
end end
end end
has_many :event_schedules, through: :events
has_many :event_users, through: :events has_many :event_users, through: :events
has_many :speakers, -> { distinct }, through: :event_users, source: :user do has_many :speakers, -> { distinct }, through: :event_users, source: :user do
def confirmed def confirmed
joins(:events).where(events: { state: :confirmed }) joins(:events).where(events: { state: :confirmed })
end end
def registered(conference)
joins(:registrations).where('registrations.conference_id = ?', conference.id)
end
end end
accepts_nested_attributes_for :event_types, allow_destroy: true accepts_nested_attributes_for :event_types, allow_destroy: true
@ -52,11 +58,15 @@ class Program < ActiveRecord::Base
# validates :conference_id, presence: true, uniqueness: true # validates :conference_id, presence: true, uniqueness: true
validates :rating, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 10, only_integer: true } validates :rating, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 10, only_integer: true }
validates :schedule_interval, numericality: { greater_than_or_equal_to: 5, less_than_or_equal_to: 60 }, presence: true
validate :schedule_interval_divisor_60
validate :voting_start_date_before_end_date validate :voting_start_date_before_end_date
validate :voting_dates_exist validate :voting_dates_exist
after_create :create_event_types after_create :create_event_types
after_create :create_difficulty_levels after_create :create_difficulty_levels
after_save :unschedule_unfit_events, if: :schedule_interval_changed?
after_save :normalize_event_types_length, if: :schedule_interval_changed?
validate :check_languages_format validate :check_languages_format
# Returns all event_schedules for the selected schedule ordered by start_time # Returns all event_schedules for the selected schedule ordered by start_time
@ -115,7 +125,7 @@ class Program < ActiveRecord::Base
# * +false+ -> If rating is not enabled # * +false+ -> If rating is not enabled
# * +true+ -> If rating is enabled # * +true+ -> If rating is enabled
def rating_enabled? def rating_enabled?
self.rating && self.rating > 0 rating && rating > 0
end end
## ##
@ -139,7 +149,7 @@ class Program < ActiveRecord::Base
end end
def languages_list def languages_list
self.languages.split(',').map {|l| ISO_639.find(l).english_name} if self.languages.present? languages.split(',').map {|l| ISO_639.find(l).english_name} if languages.present?
end end
## ##
@ -153,6 +163,23 @@ class Program < ActiveRecord::Base
EventSchedule.where(schedule: selected_schedule).where(start_time: parsed_date..(parsed_date + 1)).any? EventSchedule.where(schedule: selected_schedule).where(start_time: parsed_date..(parsed_date + 1)).any?
end end
##
# Provides backwards compatibility for when the program had one cfp
#
# ====Returns
# * +ActiveRecord+ -> The program's cfp with cfp_type == 'events'
def cfp
return nil if cfps.for_events.blank?
cfps.for_events
end
##
# ====Returns
# * +Array+ -> The types of cfps for which a cfp doesn't exist yet
def remaining_cfp_types
Cfp::TYPES - cfps.pluck(:cfp_type)
end
private private
## ##
@ -161,10 +188,10 @@ class Program < ActiveRecord::Base
def create_event_types def create_event_types
EventType.create(title: 'Talk', length: 30, color: '#FF0000', description: 'Presentation in lecture format', EventType.create(title: 'Talk', length: 30, color: '#FF0000', description: 'Presentation in lecture format',
minimum_abstract_length: 0, minimum_abstract_length: 0,
maximum_abstract_length: 500, program_id: self.id) maximum_abstract_length: 500, program_id: id)
EventType.create(title: 'Workshop', length: 60, color: '#0000FF', description: 'Interactive hands-on practice', EventType.create(title: 'Workshop', length: 60, color: '#0000FF', description: 'Interactive hands-on practice',
minimum_abstract_length: 0, minimum_abstract_length: 0,
maximum_abstract_length: 500, program_id: self.id) maximum_abstract_length: 500, program_id: id)
true true
end end
@ -174,13 +201,13 @@ class Program < ActiveRecord::Base
def create_difficulty_levels def create_difficulty_levels
DifficultyLevel.create(title: 'Easy', DifficultyLevel.create(title: 'Easy',
description: 'Events are understandable for everyone without knowledge of the topic.', description: 'Events are understandable for everyone without knowledge of the topic.',
color: '#70EF69', program_id: self.id) color: '#32CB2A', program_id: id)
DifficultyLevel.create(title: 'Medium', DifficultyLevel.create(title: 'Medium',
description: 'Events require a basic understanding of the topic.', description: 'Events require a basic understanding of the topic.',
color: '#EEEF69', program_id: self.id) color: '#E6B65B', program_id: id)
DifficultyLevel.create(title: 'Hard', DifficultyLevel.create(title: 'Hard',
description: 'Events require expert knowledge of the topic.', description: 'Events require expert knowledge of the topic.',
color: '#EF6E69', program_id: self.id) color: '#EF6E69', program_id: id)
true true
end end
@ -188,15 +215,42 @@ class Program < ActiveRecord::Base
# Check if languages string has the right format. Used as validation. # Check if languages string has the right format. Used as validation.
# #
def check_languages_format def check_languages_format
return unless self.languages.present? return unless languages.present?
# All white spaces are removed to allow languages to be separated by ',' and ', '. The languages string without spaces is saved # All white spaces are removed to allow languages to be separated by ',' and ', '. The languages string without spaces is saved
self.languages = self.languages.delete(' ').downcase self.languages = languages.delete(' ').downcase
errors.add(:languages, 'must be two letters separated by commas') && return unless errors.add(:languages, 'must be two letters separated by commas') && return unless
self.languages.match(/^$|(\A[a-z][a-z](,[a-z][a-z])*\z)/).present? languages.match(/^$|(\A[a-z][a-z](,[a-z][a-z])*\z)/).present?
languages_array = self.languages.split(',') languages_array = languages.split(',')
# We check that languages are not repeated # We check that languages are not repeated
errors.add(:languages, "can't be repeated") && return unless languages_array.uniq!.nil? errors.add(:languages, "can't be repeated") && return unless languages_array.uniq!.nil?
# We check if every language is a valid ISO 639-1 language # We check if every language is a valid ISO 639-1 language
errors.add(:languages, 'must be ISO 639-1 valid codes') unless languages_array.select{ |x| ISO_639.find(x).nil? }.empty? errors.add(:languages, 'must be ISO 639-1 valid codes') unless languages_array.select{ |x| ISO_639.find(x).nil? }.empty?
end end
##
# Check if schedule_interval is a divisor of 60 minutes
#
def schedule_interval_divisor_60
errors.add(:schedule_interval, 'must be a divisor of 60') if schedule_interval > 0 && 60 % schedule_interval > 0
end
##
# Unschedule all the events which don't fit
#
def unschedule_unfit_events
unfit_schedules = event_schedules.select do |event_schedule|
event_schedule.start_time.min % schedule_interval > 0
end
EventSchedule.where(id: unfit_schedules.map(&:id)).destroy_all
end
##
# Change event type length according schedule interval
#
def normalize_event_types_length
event_types.each do |event_type|
new_length = event_type.length > schedule_interval ? event_type.length - (event_type.length % schedule_interval) : schedule_interval
event_type.update_attributes length: new_length
end
end
end end

View file

@ -12,6 +12,6 @@ class Question < ActiveRecord::Base
private private
def existing_answers def existing_answers
errors.add(:base, 'Must have answers') if self.answers.blank? errors.add(:base, 'Must have answers') if answers.blank?
end end
end end

View file

@ -2,14 +2,13 @@ class Registration < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :conference belongs_to :conference
has_and_belongs_to_many :events
has_and_belongs_to_many :qanswers has_and_belongs_to_many :qanswers
has_and_belongs_to_many :vchoices has_and_belongs_to_many :vchoices
has_many :events_registrations has_many :events_registrations
has_many :events, through: :events_registrations, dependent: :destroy has_many :events, through: :events_registrations, dependent: :destroy
has_paper_trail ignore: [:updated_at, :week], meta: { conference_id: :conference_id } has_paper_trail ignore: %i(updated_at week), meta: { conference_id: :conference_id }
accepts_nested_attributes_for :user accepts_nested_attributes_for :user
accepts_nested_attributes_for :qanswers accepts_nested_attributes_for :qanswers
@ -24,7 +23,7 @@ class Registration < ActiveRecord::Base
validates :user, presence: true validates :user, presence: true
validates_uniqueness_of :user_id, scope: :conference_id, message: 'already Registered!' validates :user_id, uniqueness: { scope: :conference_id, message: 'already Registered!' }
validate :registration_limit_not_exceed, on: :create validate :registration_limit_not_exceed, on: :create
validate :registration_to_events_only_if_present validate :registration_to_events_only_if_present

View file

@ -10,15 +10,15 @@ class RegistrationPeriod < ActiveRecord::Base
private private
def before_end_of_conference def before_end_of_conference
errors. errors
add(:start_date, "can't be after the conference end date (#{conference.end_date})") if conference && conference.end_date && start_date && (start_date > conference.end_date) .add(:start_date, "can't be after the conference end date (#{conference.end_date})") if conference && conference.end_date && start_date && (start_date > conference.end_date)
errors. errors
add(:end_date, "can't be after the conference end date (#{conference.end_date})") if conference && conference.end_date && end_date && (end_date > conference.end_date) .add(:end_date, "can't be after the conference end date (#{conference.end_date})") if conference && conference.end_date && end_date && (end_date > conference.end_date)
end end
def start_date_before_end_date def start_date_before_end_date
errors. errors
add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date .add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date
end end
end end

View file

@ -1,10 +1,12 @@
class Resource < ActiveRecord::Base class Resource < ActiveRecord::Base
belongs_to :conference belongs_to :conference
validate :used_less_than_quantity validates :name, :used, :quantity, presence: true
validates :used, :quantity, numericality: { greater_than_or_equal_to: 0, only_integer: true }
validate :used_no_more_than_quantity
private private
def used_less_than_quantity def used_no_more_than_quantity
errors.add(:used, 'can not be higher than total quantity') unless used <= quantity errors.add(:used, 'cannot be higher than total quantity') if used.present? && quantity.present? && used > quantity
end end
end end

View file

@ -1,26 +0,0 @@
#
# suseconferenceclient relies on a 'revision' attribute for caching and
# doing some calculations.
#
# It should be incremented after any change in the conference or in any
# associated models
#
# This observer updates the revision column in a non-intrusive way,
# preventing validations, callbacks or exceptions to be triggered
#
# Relying on paper_trail could also be an option, but a 'revision' column
# in table 'conferences' looks like a more simple and straightforward solution
#
class RevisionObserver < ActiveRecord::Observer
observe :conference, :event, :room, :track
def after_save(model)
begin
conference = model.kind_of?(Conference) ? model : model.conference
conference.reload.increment(:revision)
conference.update_column(:revision, conference.revision)
rescue
nil
end
end
end

View file

@ -1,4 +1,5 @@
class Room < ActiveRecord::Base class Room < ActiveRecord::Base
include RevisionCount
belongs_to :venue belongs_to :venue
has_many :event_schedules, dependent: :destroy has_many :event_schedules, dependent: :destroy
@ -10,6 +11,10 @@ class Room < ActiveRecord::Base
validates :size, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true validates :size, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
def conference
venue.conference
end
private private
def generate_guid def generate_guid

View file

@ -6,5 +6,5 @@ class Sponsor < ActiveRecord::Base
mount_uploader :picture, PictureUploader, mount_on: :logo_file_name mount_uploader :picture, PictureUploader, mount_on: :logo_file_name
validates_presence_of :name, :website_url, :sponsorship_level validates :name, :website_url, :sponsorship_level, presence: true
end end

View file

@ -1,5 +1,5 @@
class SponsorshipLevel < ActiveRecord::Base class SponsorshipLevel < ActiveRecord::Base
validates_presence_of :title validates :title, presence: true
belongs_to :conference belongs_to :conference
acts_as_list scope: :conference acts_as_list scope: :conference
has_many :sponsors has_many :sponsors

View file

@ -1,9 +1,9 @@
class Subscription < ActiveRecord::Base class Subscription < ActiveRecord::Base
validates_uniqueness_of :user_id, scope: [:conference_id] validates :user_id, uniqueness: { scope: [:conference_id] }
belongs_to :conference belongs_to :conference
belongs_to :user belongs_to :user
has_paper_trail on: [:create, :destroy], ignore: [:updated_at], meta: { conference_id: :conference_id } has_paper_trail on: %i(create destroy), ignore: [:updated_at], meta: { conference_id: :conference_id }
validates_uniqueness_of :user_id, scope: :conference_id, message: 'already subscribed!' validates :user_id, uniqueness: { scope: :conference_id, message: 'already subscribed!' }
end end

View file

@ -13,7 +13,7 @@ class Ticket < ActiveRecord::Base
validates :price_cents, :price_currency, :title, presence: true validates :price_cents, :price_currency, :title, presence: true
validates_numericality_of :price_cents, greater_than_or_equal_to: 0 validates :price_cents, numericality: { greater_than_or_equal_to: 0 }
def bought?(user) def bought?(user)
buyers.include?(user) buyers.include?(user)
@ -56,7 +56,7 @@ class Ticket < ActiveRecord::Base
end end
def tickets_sold def tickets_sold
ticket_purchases.sum(:quantity) ticket_purchases.paid.sum(:quantity)
end end
def tickets_turnover def tickets_turnover
@ -66,7 +66,7 @@ class Ticket < ActiveRecord::Base
private private
def tickets_of_conference_have_same_currency def tickets_of_conference_have_same_currency
unless Ticket.where(conference_id: conference_id).all?{|t| t.price_currency == self.price_currency } unless Ticket.where(conference_id: conference_id).all?{|t| t.price_currency == price_currency }
errors.add(:price_currency, 'is different from the existing tickets of this conference.') errors.add(:price_currency, 'is different from the existing tickets of this conference.')
end end
end end

View file

@ -2,10 +2,11 @@ class TicketPurchase < ActiveRecord::Base
belongs_to :ticket belongs_to :ticket
belongs_to :user belongs_to :user
belongs_to :conference belongs_to :conference
belongs_to :payment
validates :ticket_id, :user_id, :conference_id, :quantity, presence: true validates :ticket_id, :user_id, :conference_id, :quantity, presence: true
validates_numericality_of :quantity, greater_than: 0 validates :quantity, numericality: { greater_than: 0 }
delegate :title, to: :ticket delegate :title, to: :ticket
delegate :description, to: :ticket delegate :description, to: :ticket
@ -13,21 +14,25 @@ class TicketPurchase < ActiveRecord::Base
delegate :price_cents, to: :ticket delegate :price_cents, to: :ticket
delegate :price_currency, to: :ticket delegate :price_currency, to: :ticket
has_many :physical_tickets
scope :paid, -> { where(paid: true) } scope :paid, -> { where(paid: true) }
scope :unpaid, -> { where(paid: false) } scope :unpaid, -> { where(paid: false) }
scope :by_conference, ->(conference) { where(conference_id: conference.id) } scope :by_conference, ->(conference) { where(conference_id: conference.id) }
scope :by_user, ->(user) { where(user_id: user.id) } scope :by_user, ->(user) { where(user_id: user.id) }
after_create :set_week
def self.purchase(conference, user, purchases) def self.purchase(conference, user, purchases)
errors = [] errors = []
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
conference.tickets.each do |ticket| conference.tickets.each do |ticket|
quantity = purchases[ticket.id.to_s].to_i quantity = purchases[ticket.id.to_s].to_i
# if the user bought the ticket and is still unpaid, just update the quantity # if the user bought the ticket and is still unpaid, just update the quantity
if ticket.bought?(user) && ticket.unpaid?(user) purchase = if ticket.bought?(user) && ticket.unpaid?(user)
purchase = update_quantity(conference, quantity, ticket, user) update_quantity(conference, quantity, ticket, user)
else else
purchase = purchase_ticket(conference, quantity, ticket, user) purchase_ticket(conference, quantity, ticket, user)
end end
if purchase && !purchase.save if purchase && !purchase.save
@ -43,8 +48,8 @@ class TicketPurchase < ActiveRecord::Base
purchase = new(ticket_id: ticket.id, purchase = new(ticket_id: ticket.id,
conference_id: conference.id, conference_id: conference.id,
user_id: user.id, user_id: user.id,
quantity: quantity, quantity: quantity)
paid: ticket.price_cents.zero?) purchase.pay(nil) if ticket.price_cents.zero?
end end
purchase purchase
end end
@ -58,4 +63,18 @@ class TicketPurchase < ActiveRecord::Base
purchase.quantity = quantity if quantity > 0 purchase.quantity = quantity if quantity > 0
purchase purchase
end end
def pay(payment)
update_attributes(paid: true, payment: payment)
PhysicalTicket.transaction do
quantity.times { physical_tickets.create }
end
end
end
private
def set_week
self.week = created_at.strftime('%W')
save!
end end

View file

@ -0,0 +1,3 @@
class TicketScanning < ActiveRecord::Base
belongs_to :physical_ticket
end

View file

@ -1,5 +1,10 @@
class Track < ActiveRecord::Base class Track < ActiveRecord::Base
include RevisionCount
resourcify :roles, dependent: :delete_all
belongs_to :program belongs_to :program
belongs_to :submitter, class_name: 'User'
has_many :events, dependent: :nullify has_many :events, dependent: :nullify
has_paper_trail only: [:name, :description, :color], meta: { conference_id: :conference_id } has_paper_trail only: [:name, :description, :color], meta: { conference_id: :conference_id }
@ -7,9 +12,37 @@ class Track < ActiveRecord::Base
before_create :generate_guid before_create :generate_guid
validates :name, presence: true validates :name, presence: true
validates :color, format: /\A#[0-9A-F]{6}\z/ validates :color, format: /\A#[0-9A-F]{6}\z/
validates :short_name,
presence: true,
format: /\A[a-zA-Z0-9_-]*\z/,
uniqueness: {
scope: :program
}
validates :state, presence: true, if: :self_organized?
validates :cfp_active, inclusion: { in: [true, false] }, if: :self_organized?
before_validation :capitalize_color before_validation :capitalize_color
after_create :create_organizer_role, if: :self_organized?
def conference
program.conference
end
##
# Checks if the track is self-organized
# ====Returns
# * +true+ -> If the track has a submitter
# * +false+ -> if the track doesn't have a submitter
def self_organized?
return true if submitter
false
end
def to_param
short_name
end
private private
def generate_guid def generate_guid
@ -27,4 +60,10 @@ class Track < ActiveRecord::Base
def conference_id def conference_id
program.conference_id program.conference_id
end end
##
# Creates the role of the track organizer
def create_organizer_role
Role.where(name: 'track_organizer', resource: self).first_or_create(description: 'For the organizers of the Track')
end
end end

View file

@ -6,6 +6,11 @@ end
class User < ActiveRecord::Base class User < ActiveRecord::Base
rolify rolify
has_many :physical_tickets, through: :ticket_purchases do
def by_conference(conference)
where('ticket_purchases.conference_id = ?', conference)
end
end
has_many :users_roles has_many :users_roles
has_many :roles, through: :users_roles, dependent: :destroy has_many :roles, through: :users_roles, dependent: :destroy
@ -25,10 +30,10 @@ class User < ActiveRecord::Base
# :lockable, :timeoutable and :omniauthable # :lockable, :timeoutable and :omniauthable
devise_modules = [] devise_modules = []
if ENV['OSEM_ICHAIN_ENABLED'] == 'true' devise_modules += if ENV['OSEM_ICHAIN_ENABLED'] == 'true'
devise_modules += [ :ichain_authenticatable, :ichain_registerable, :omniauthable, omniauth_providers: [] ] [:ichain_authenticatable, :ichain_registerable, :omniauthable, omniauth_providers: []]
else else
devise_modules += [:database_authenticatable, :registerable, [:database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable, :confirmable,
:omniauthable, omniauth_providers: [:suse, :google, :facebook, :github]] :omniauthable, omniauth_providers: [:suse, :google, :facebook, :github]]
end end
@ -41,6 +46,7 @@ class User < ActiveRecord::Base
has_many :event_users, dependent: :destroy has_many :event_users, dependent: :destroy
has_many :events, -> { uniq }, through: :event_users has_many :events, -> { uniq }, through: :event_users
has_many :presented_events, -> { joins(:event_users).where(event_users: {event_role: 'speaker'}).uniq }, through: :event_users, source: :event
has_many :registrations, dependent: :destroy has_many :registrations, dependent: :destroy
has_many :events_registrations, through: :registrations has_many :events_registrations, through: :registrations
has_many :ticket_purchases, dependent: :destroy has_many :ticket_purchases, dependent: :destroy
@ -49,9 +55,11 @@ class User < ActiveRecord::Base
has_many :votes, dependent: :destroy has_many :votes, dependent: :destroy
has_many :voted_events, through: :votes, source: :events has_many :voted_events, through: :votes, source: :events
has_many :subscriptions, dependent: :destroy has_many :subscriptions, dependent: :destroy
has_many :tracks, foreign_key: 'submitter_id'
accepts_nested_attributes_for :roles accepts_nested_attributes_for :roles
scope :admin, -> { where(is_admin: true) } scope :admin, -> { where(is_admin: true) }
scope :active, -> { where(is_disabled: false) }
validates :email, presence: true validates :email, presence: true
@ -72,7 +80,7 @@ class User < ActiveRecord::Base
# * +true+ if the user attended the event # * +true+ if the user attended the event
# * +false+ if the user did not attend the event # * +false+ if the user did not attend the event
def attended_event? event def attended_event? event
event_registration = event.events_registrations.find_by(registration: self.registrations) event_registration = event.events_registrations.find_by(registration: registrations)
return false unless event_registration.present? return false unless event_registration.present?
event_registration.attended event_registration.attended
@ -91,7 +99,7 @@ class User < ActiveRecord::Base
end end
def subscribed? conference def subscribed? conference
self.subscriptions.find_by(conference_id: conference.id).present? subscriptions.find_by(conference_id: conference.id).present?
end end
def supports? conference def supports? conference
@ -206,8 +214,8 @@ class User < ActiveRecord::Base
# Check if biography has an allowed number of words. Used as validation. # Check if biography has an allowed number of words. Used as validation.
# #
def biography_limit def biography_limit
if self.biography.present? if biography.present?
errors.add(:biography, 'is limited to 150 words.') if self.biography.split.length > 150 errors.add(:biography, 'is limited to 150 words.') if biography.split.length > 150
end end
end end
end end

View file

@ -35,7 +35,7 @@ class Venue < ActiveRecord::Base
def notify_on_venue_changed? def notify_on_venue_changed?
return false unless conference.try(:email_settings).try(:send_on_venue_updated) return false unless conference.try(:email_settings).try(:send_on_venue_updated)
# do not notify unless the address changed # do not notify unless the address changed
return false unless self.name_changed? || self.street_changed? || self.city_changed? || self.country_changed? return false unless name_changed? || street_changed? || city_changed? || country_changed?
# do not notify unless the mail content is set up # do not notify unless the mail content is set up
(!conference.email_settings.venue_updated_subject.blank? && !conference.email_settings.venue_updated_body.blank?) (!conference.email_settings.venue_updated_subject.blank? && !conference.email_settings.venue_updated_body.blank?)
end end

View file

@ -4,5 +4,5 @@ class Vposition < ActiveRecord::Base
has_many :vchoices has_many :vchoices
has_many :vdays, through: :vchoices has_many :vdays, through: :vchoices
validates_presence_of :title, :vdays validates :title, :vdays, presence: true
end end

View file

@ -26,6 +26,6 @@ class EventSerializer < ActiveModel::Serializer
end end
def length def length
object.event_type.try(:length) || EventType::LENGTH_STEP object.event_type.try(:length) || object.event_type.program.schedule_interval
end end
end end

View file

@ -3,7 +3,5 @@ class SpeakerSerializer < ActiveModel::Serializer
attributes :name, :affiliation, :biography attributes :name, :affiliation, :biography
def name delegate :name, to: :object
object.name
end
end end

View file

@ -52,6 +52,10 @@ class PictureUploader < CarrierWave::Uploader::Base
"system/#{object_class_name}/#{mounted_as}/#{model.id}" "system/#{object_class_name}/#{mounted_as}/#{model.id}"
end end
def image
@image ||= MiniMagick::Image.open(file.file)
end
# Create different versions of your uploaded files: # Create different versions of your uploaded files:
version :large do version :large do
process resize_to_fit: [300, 300] process resize_to_fit: [300, 300]

View file

@ -0,0 +1,12 @@
%dt
Start Date
%dd
= @cfp.start_date.strftime('%A, %B %e. %Y')
%dt
End Date
%dd
= @cfp.end_date.strftime('%A, %B %e. %Y')
%dt
Days Left
%dd
= pluralize(@cfp.remaining_days, 'day')

View file

@ -0,0 +1,38 @@
%dt
Start Date:
%dd#start_date
= @cfp.start_date.strftime('%A, %B %-d. %Y')
%dt
End Date:
%dd#end_date
= @cfp.end_date.strftime('%A, %B %-d. %Y')
%dt
Days Left:
%dd
= pluralize(@cfp.remaining_days, 'day')
%dt
Event types:
%dd
= event_types(@conference)
%dt
Tracks:
%dd
= tracks(@conference)
%dt
Public Schedule
%dd#schedule_public
- if @program.schedule_public
Yes
- else
No
%dt
Schedule changeable?
%dd#schedule_changes
- if @program.schedule_fluid
Yes
- else
No
%dt
Rating Levels
%dd#rating
= @program.rating

View file

@ -4,8 +4,9 @@
%h1 Call for Papers %h1 Call for Papers
.row .row
.col-md-8 .col-md-8
= semantic_form_for(@cfp, url: admin_conference_program_cfp_path(@conference.short_title), html: {multipart: true}) do |f| = semantic_form_for(@cfp, url: (@cfp.new_record? ? admin_conference_program_cfps_path : admin_conference_program_cfp_path(@conference.short_title, @cfp)), html: {multipart: true}) do |f|
= f.input :start_date, as: :string, input_html: { id: 'registration-period-start-datepicker', start_date: @conference.start_date, end_date: @conference.end_date, readonly: 'readonly' } = f.input :start_date, as: :string, input_html: { id: 'registration-period-start-datepicker', start_date: @conference.start_date, end_date: @conference.end_date, readonly: 'readonly' }
= f.input :end_date, as: :string, input_html: { id: 'registration-period-end-datepicker', readonly: 'readonly' } = f.input :end_date, as: :string, input_html: { id: 'registration-period-end-datepicker', readonly: 'readonly' }
= f.input :cfp_type, as: :select, collection: (@cfp.new_record? ? @program.remaining_cfp_types : [@cfp.cfp_type] + @program.remaining_cfp_types).map {|type| ["#{type.capitalize}", type]}, include_blank: false, label: 'Type', input_html: { class: 'select-help-toggle' }
%p.text-right %p.text-right
= f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' }

View file

@ -0,0 +1,36 @@
.row
.col-md-12
.page-header
%h1 Call for Papers
%p.text-muted
Call for people to submit events to your conference
- if @program.cfps
.row
.col-md-12
%table.table.table-hover.datatable#tickets
%thead
%th Type
%th Start Date
%th End Date
%th Days Left
%th Actions
%tbody
- @program.cfps.each do |cfp|
%tr
%td
= link_to(admin_conference_program_cfp_path(@conference.short_title, cfp.id)) do
= cfp.cfp_type.capitalize
%td
= cfp.start_date.strftime('%A, %B %-d. %Y')
%td
= cfp.end_date.strftime('%A, %B %-d. %Y')
%td
= pluralize(cfp.remaining_days, 'day')
%td
.btn-group
= link_to 'Edit', edit_admin_conference_program_cfp_path(@conference.short_title, cfp.id), method: :get, class: 'btn btn-primary'
= link_to 'Delete', admin_conference_program_cfp_path(@conference.short_title, cfp.id), method: 'delete', class: 'btn btn-danger', data: { confirm: 'Are you sure you want to delete the CfP?' }
- if can? :new, @program.cfps.new
.row
.col-md-12.text-right
= link_to 'Create Call for Papers', new_admin_conference_program_cfp_path(@conference.short_title), class: 'btn btn-primary'

View file

@ -4,55 +4,13 @@
%h1 Call for Papers %h1 Call for Papers
%p.text-muted %p.text-muted
Call for people to submit events to your conference Call for people to submit events to your conference
- if @cfp
.row .row
.col-md-8 .col-md-8
%dl.dl-horizontal %dl.dl-horizontal
%dt = render "#{@cfp.cfp_type}_cfp"
Start Date:
%dd#start_date
= @cfp.start_date.strftime('%A, %B %-d. %Y')
%dt
End Date:
%dd#end_date
= @cfp.end_date.strftime('%A, %B %-d. %Y')
%dt
Days Left:
%dd
= pluralize(@cfp.remaining_days, 'day')
%dt
Event types:
%dd
= event_types(@conference)
%dt
Tracks:
%dd
= tracks(@conference)
%dt
Public Schedule
%dd#schedule_public
- if @program.schedule_public
Yes
- else
No
%dt
Schedule changeable?
%dd#schedule_changes
- if @program.schedule_fluid
Yes
- else
No
%dt
Rating Levels
%dd#rating
= @program.rating
.row .row
.col-md-12.text-right .col-md-12.text-right
= link_to(edit_admin_conference_program_cfp_path(@conference.short_title), class: 'btn btn-primary') do = link_to(edit_admin_conference_program_cfp_path(@conference.short_title, @cfp.id), class: 'btn btn-primary') do
Edit Edit
= link_to(admin_conference_program_cfp_path(@conference.short_title), method: 'delete', class: 'btn btn-danger', data: { confirm: 'Are you sure you want to delete the CfP?' }) do = link_to(admin_conference_program_cfp_path(@conference.short_title, @cfp.id), method: 'delete', class: 'btn btn-danger', data: { confirm: 'Are you sure you want to delete the CfP?' }) do
Delete Delete
- else
.row
.col-md-12.text-right
= link_to 'Create Call for Papers', new_admin_conference_program_cfp_path(@conference.short_title), class: 'btn btn-primary'

View file

@ -1,12 +1,12 @@
.list-group .list-group
%li.list-group-item %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)}" }
%h4 %h4
Conference progress Conference progress
.progress .progress
.progress-bar{ 'role' => 'progressbar', 'aria-valuenow' => "#{conference_progress['process']}", 'aria-valuemin' => '0', .progress-bar{ 'role' => 'progressbar', 'aria-valuenow' => "#{conference_progress['process']}", 'aria-valuemin' => '0',
'aria-valuemax' => '100', 'style' => "width: #{conference_progress['process']}%;" } 'aria-valuemax' => '100', 'style' => "width: #{conference_progress['process']}%;" }
= conference_progress['process'] + '%' = conference_progress['process'] + '%'
%li{ 'class' => "list-group-item #{class_for_todo(conference_progress['registration'])}" } %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['registration'])}" }
%span{ 'class' => icon_for_todo(conference_progress['registration']) } %span{ 'class' => icon_for_todo(conference_progress['registration']) }
- if can? :update, @conference - if can? :update, @conference
- if conference.registration_period - if conference.registration_period
@ -15,46 +15,48 @@
= link_to 'Set up registration period', new_admin_conference_registration_period_path(conference_progress['short_title']) = link_to 'Set up registration period', new_admin_conference_registration_period_path(conference_progress['short_title'])
- else - else
Set up registration period Set up registration period
%li{ 'class' => "list-group-item #{class_for_todo(conference_progress['cfp'])}" } %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['cfp'])}" }
%span{ 'class' => icon_for_todo(conference_progress['cfp']) } %span{ 'class' => icon_for_todo(conference_progress['cfp']) }
- if can? :update, Cfp.new(program_id: @program.id) - if can? :update, Cfp.new(program_id: @program.id)
= link_to 'Set up call for papers', admin_conference_program_cfp_path(conference_progress['short_title']) = link_to 'Set up call for papers', admin_conference_program_cfps_path(conference_progress['short_title'])
- else - else
Set up call for papers Set up call for papers
%li{ 'class' => "list-group-item #{class_for_todo(conference_progress['venue'])}" } %li{'class'=>"list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['venue'])}"}
%span{'class'=>icon_for_todo(conference_progress['venue'])} %span{'class'=>icon_for_todo(conference_progress['venue'])}
- if can? :update, @conference.venue - if can? :update, Venue.new(conference: @conference)
- @conference.reload
- if conference.venue - if conference.venue
= link_to 'Add venue', edit_admin_conference_venue_path(conference_progress['short_title']) = link_to 'Add venue', edit_admin_conference_venue_path(conference_progress['short_title'])
- else - else
= link_to 'Add venue', new_admin_conference_venue_path(conference_progress['short_title']) = link_to 'Add venue', new_admin_conference_venue_path(conference_progress['short_title'])
- else - else
- @conference.reload
Add venue Add venue
%li{ 'class' => "list-group-item #{class_for_todo(conference_progress['rooms'])}" } %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['rooms'])}" }
%span{ 'class' => icon_for_todo(conference_progress['rooms']) } %span{ 'class' => icon_for_todo(conference_progress['rooms']) }
- if @conference.venue && (can? :update, @conference.venue.rooms.build) - if @conference.venue && (can? :update, @conference.venue.rooms.build)
= link_to 'Add rooms', admin_conference_venue_rooms_path(conference_progress['short_title']) = link_to 'Add rooms', admin_conference_venue_rooms_path(conference_progress['short_title'])
- else - else
Add rooms Add rooms
%li{ 'class' => "list-group-item #{class_for_todo(conference_progress['tracks'])}" } %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['tracks'])}" }
%span{ 'class' => icon_for_todo(conference_progress['tracks']) } %span{ 'class' => icon_for_todo(conference_progress['tracks']) }
- if can? :update, @conference.program.tracks.build - if can? :update, @conference.program.tracks.build
= link_to 'Add tracks', admin_conference_program_tracks_path(conference_progress['short_title']) = link_to 'Add tracks', admin_conference_program_tracks_path(conference_progress['short_title'])
- else - else
Add tracks Add tracks
%li{ 'class' => "list-group-item #{class_for_todo(conference_progress['event_types'])}" } %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['event_types'])}" }
%span{ 'class' => icon_for_todo(conference_progress['event_types']) } %span{ 'class' => icon_for_todo(conference_progress['event_types']) }
- if can? :update, @conference.program.event_types.build - if can? :update, @conference.program.event_types.build
= link_to 'Add event types', admin_conference_program_event_types_path(conference_progress['short_title']) = link_to 'Add event types', admin_conference_program_event_types_path(conference_progress['short_title'])
- else - else
Add event types Add event types
%li{ 'class' => "list-group-item #{class_for_todo(conference_progress['difficulty_levels'])}" } %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['difficulty_levels'])}" }
%span{ 'class' => icon_for_todo(conference_progress['difficulty_levels']) } %span{ 'class' => icon_for_todo(conference_progress['difficulty_levels']) }
- if can? :update, @conference.program.difficulty_levels.build - if can? :update, @conference.program.difficulty_levels.build
= link_to 'Add difficulty levels', admin_conference_program_difficulty_levels_path(conference_progress['short_title']) = link_to 'Add difficulty levels', admin_conference_program_difficulty_levels_path(conference_progress['short_title'])
- else - else
Add difficulty levels Add difficulty levels
%li{ class: "list-group-item #{class_for_todo(conference_progress['splashpage'])}" } %li{ class: "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['splashpage'])}" }
%span{ 'class' => icon_for_todo(conference_progress['splashpage']) } %span{ 'class' => icon_for_todo(conference_progress['splashpage']) }
- if can? :update, @conference - if can? :update, @conference
= link_to 'Set up a Splashpage', admin_conference_splashpage_path(conference_progress['short_title']) = link_to 'Set up a Splashpage', admin_conference_splashpage_path(conference_progress['short_title'])

Some files were not shown because too many files have changed in this diff Show more