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.test
.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:
Enabled: true
# Inherit style from another configuration
inherit_from: .rubocop_todo.yml
Style/HashSyntax:
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
# Apply rule to all cops
AllCops:
Include:
- '**/Rakefile'
@ -136,3 +12,22 @@ AllCops:
- 'bundle/**/*'
- 'config/**/*'
- '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;'
- RAILS_ENV=test bundle exec rake db:migrate --trace
script:
- 'bundle exec rubocop -Dc .rubocop.yml'
- 'bundle exec rspec --color --format documentation'
- "./travis_script.sh $TEST_SUITE"
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
```
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.
### Test Suite
@ -169,6 +169,18 @@ config.ichain_force_test_username = "testuser"
# set email of 'testuser'
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
...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!
14. **Research**
* 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
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"]

16
Gemfile
View file

@ -18,10 +18,9 @@ gem 'rails_12factor', group: :production
gem 'responders', '~> 2.0'
# as the database for Active Record
# choose only one
gem 'mysql2'
# for observing records
gem 'rails-observers'
# gem 'pg'
# for tracking data changes
gem 'paper_trail'
@ -126,8 +125,11 @@ gem 'country_select'
# as PDF generator
gem 'prawn_rails'
gem 'rqrcode'
gem 'prawn-qrcode', '~> 0.2.2.1'
# to render XLS spreadsheets
gem 'axlsx', git: 'https://github.com/randym/axlsx.git'
gem 'axlsx_rails'
# as error catcher
@ -178,7 +180,7 @@ gem 'cloudinary'
# for setting app configuration in the environment
gem 'dotenv-rails'
# For countable.js
# For countable.js
gem "countable-rails", "~> 0.0.1"
# Both are not in a group as we use it also for rake data:demo
@ -193,13 +195,17 @@ gem 'stripe'
# Provides Sprockets implementation for Rails Asset Pipeline
gem 'sprockets-rails'
# for multiple speakers select on proposal/event forms
gem 'selectize-rails'
# Use guard and spring for testing in development
group :development do
# to launch specs when files are modified
gem 'guard-rspec', '~> 4.2.8'
gem 'spring-commands-rspec'
gem 'haml_lint', '~> 0.24.0'
# for static code analisys
gem 'rubocop', require: false
gem 'rubocop', '~> 0.49.0', require: false
# as database
gem 'sqlite3'
# 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
remote: https://rubygems.org/
remote: https://rails-assets.org/
@ -58,17 +68,13 @@ GEM
user_agent_parser
uuidtools
arel (6.0.3)
ast (2.2.0)
ast (2.3.0)
autoprefixer-rails (5.1.9)
execjs
json
awesome_nested_set (3.0.0.rc.5)
activerecord (>= 4.0.0, < 5)
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 (>= 2.0.1)
rails (>= 3.1)
@ -83,9 +89,7 @@ GEM
momentjs-rails (>= 2.8.1)
browser (0.6.0)
builder (3.2.2)
byebug (3.1.2)
columnize (~> 0.8)
debugger-linecache (~> 1.2)
byebug (9.0.6)
cancancan (1.13.1)
capybara (2.6.2)
addressable
@ -111,6 +115,7 @@ GEM
chart-js-rails (0.0.6)
railties (> 3.1)
chronic (0.10.2)
chunky_png (1.3.8)
cliver (0.3.2)
cloudinary (1.1.6)
aws_cf_signer
@ -124,7 +129,6 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.10.0)
columnize (0.8.9)
countable-rails (0.0.1)
railties (>= 3.1)
countries (1.2.5)
@ -146,7 +150,6 @@ GEM
dante (0.2.0)
database_cleaner (1.3.0)
debug_inspector (0.0.2)
debugger-linecache (1.2.0)
delayed_job (4.1.1)
activesupport (>= 3.0, < 5.0)
delayed_job_active_record (4.1.0)
@ -209,6 +212,12 @@ GEM
activesupport (>= 4.0.1)
haml (>= 3.1, < 5.0)
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)
hike (1.2.3)
hoptoad_notifier (2.4.11)
@ -230,7 +239,7 @@ GEM
thor (>= 0.14, < 2.0)
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (1.8.3)
json (1.8.6)
json-schema (2.5.0)
addressable (~> 2.3)
jwt (1.0.0)
@ -256,12 +265,13 @@ GEM
mime-types (>= 1.16, < 3)
method_source (0.8.2)
mime-types (2.99.1)
mimemagic (0.3.2)
mina (0.3.8)
open4 (~> 1.3.4)
rake
mini_magick (4.5.1)
mini_portile2 (2.1.0)
minitest (5.9.0)
mini_portile2 (2.2.0)
minitest (5.10.1)
momentjs-rails (2.8.1)
railties (>= 3.1)
monetize (1.4.0)
@ -280,9 +290,8 @@ GEM
mysql2 (0.4.2)
netrc (0.11.0)
nio4r (1.2.1)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
nokogiri (1.8.0)
mini_portile2 (~> 2.2.0)
oauth2 (0.9.4)
faraday (>= 0.8, < 0.10)
jwt (~> 1.0)
@ -310,11 +319,12 @@ GEM
rack-openid (~> 1.3.1)
open4 (1.3.4)
orm_adapter (0.5.0)
paper_trail (5.0.1)
paper_trail (5.2.1)
activerecord (>= 3.0, < 6.0)
activesupport (>= 3.0, < 6.0)
request_store (~> 1.1)
parser (2.3.0.3)
parallel (1.11.2)
parser (2.4.0.0)
ast (~> 2.2)
pdf-core (0.2.5)
phantomjs (2.1.1.0)
@ -322,7 +332,6 @@ GEM
actionpack
activesupport
rails (>= 3.0.0)
pkg-config (1.1.7)
poltergeist (1.9.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
@ -332,6 +341,9 @@ GEM
prawn (1.0.0)
pdf-core (~> 0.2.2)
ttfunk (~> 1.1.1)
prawn-qrcode (0.2.2.1)
prawn (>= 0.11.1)
rqrcode (>= 0.4.1)
prawn_rails (0.0.11)
prawn (>= 0.11.1)
railties (>= 3.0.0)
@ -375,17 +387,15 @@ GEM
rails-assets-waypoints (4.0.0)
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.7)
rails-dom-testing (1.0.8)
activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0)
nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
rails-i18n (4.0.8)
i18n (~> 0.7)
railties (~> 4.0)
rails-observers (0.1.2)
activemodel (~> 4.0)
rails_12factor (0.0.3)
rails_serve_static_assets
rails_stdout_logging
@ -396,7 +406,8 @@ GEM
activesupport (= 4.2.7.1)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.1.0)
rainbow (2.2.2)
rake
rake (10.5.0)
rb-fsevent (0.9.4)
rb-inotify (0.9.4)
@ -418,6 +429,8 @@ GEM
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
rolify (5.1.0)
rqrcode (0.10.1)
chunky_png (~> 1.0)
rspec (3.0.0)
rspec-core (~> 3.0.0)
rspec-expectations (~> 3.0.0)
@ -442,15 +455,16 @@ GEM
rspec-mocks (~> 3.0.0)
rspec-support (~> 3.0.0)
rspec-support (3.0.2)
rubocop (0.37.0)
parser (>= 2.3.0.2, < 3.0)
rubocop (0.49.1)
parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 0.3)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-oembed (0.8.14)
ruby-openid (2.5.0)
ruby-progressbar (1.7.5)
ruby-progressbar (1.8.1)
rubyzip (1.2.1)
safe_yaml (1.0.4)
sass (3.2.19)
@ -459,7 +473,8 @@ GEM
sass (~> 3.2.2)
sprockets (~> 2.8, < 2.12)
sprockets-rails (~> 2.0)
shoulda-matchers (2.6.1)
selectize-rails (0.12.4)
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
simplecov (0.11.2)
docile (~> 1.1.0)
@ -489,10 +504,11 @@ GEM
dante (>= 0.2.0)
multi_json (>= 1.0.0)
stripe (>= 1.31.0, <= 1.43)
sysexits (1.2.0)
term-ansicolor (1.3.2)
tins (~> 1.0)
thor (0.19.1)
thread_safe (0.3.5)
thread_safe (0.3.6)
tilt (1.4.1)
timecop (0.7.1)
timers (1.1.0)
@ -501,14 +517,14 @@ GEM
ttfunk (1.1.1)
turbolinks (2.5.3)
coffee-rails
tzinfo (1.2.2)
tzinfo (1.2.3)
thread_safe (~> 0.1)
uglifier (3.0.0)
execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
unicode-display_width (0.3.1)
unicode-display_width (1.3.0)
unicode_utils (1.4.0)
unobtrusive_flash (3.1.0)
railties
@ -545,6 +561,7 @@ DEPENDENCIES
ahoy_matey
autoprefixer-rails
awesome_nested_set (~> 3.0.0.rc.5)
axlsx!
axlsx_rails
bootstrap-sass (~> 3.3.4.1)
bootstrap-switch-rails (~> 3.0.0)
@ -574,6 +591,7 @@ DEPENDENCIES
gravtastic
guard-rspec (~> 4.2.8)
haml-rails
haml_lint (~> 0.24.0)
hoptoad_notifier (~> 2.3)
iso-639
jquery-datatables-rails (~> 2.2.1)
@ -596,6 +614,7 @@ DEPENDENCIES
phantomjs
piwik_analytics (~> 1.0.1)
poltergeist
prawn-qrcode (~> 0.2.2.1)
prawn_rails
rails (~> 4.2)
rails-assets-bootstrap-markdown!
@ -610,17 +629,18 @@ DEPENDENCIES
rails-assets-trianglify!
rails-assets-waypoints!
rails-i18n (~> 4.0.0)
rails-observers
rails_12factor
rdoc-generator-fivefish
redcarpet
responders (~> 2.0)
rolify
rqrcode
rspec-activemodel-mocks
rspec-rails
rubocop
rubocop (~> 0.49.0)
ruby-oembed
sass-rails (>= 4.0.2)
selectize-rails
shoulda-matchers
spring-commands-rspec
sprockets-rails
@ -637,4 +657,4 @@ DEPENDENCIES
whenever
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
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
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
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">
</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
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_GITHUB_KEY | *string* | OMNIAUTH Developer Key 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_PORT | *int* | The port on the smtp server
| OSEM_SMTP_USERNAME | *string* | The user for the smtp server
@ -70,7 +122,7 @@ $ bundle exec rake logo:reprocess
```
### 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
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
# 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
# boxes will only be checked for updates when the user runs

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,7 +15,7 @@ module Admin
redirect_to admin_conference_campaigns_path(conference_id: @conference.short_title),
notice: 'Campaign successfully created.'
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'
end
end
@ -29,7 +29,7 @@ module Admin
redirect_to admin_conference_campaigns_path(conference_id: @conference.short_title),
notice: "Campaign '#{@campaign.name}' successfully updated."
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'
end
end

View file

@ -2,51 +2,52 @@ module Admin
class CfpsController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title
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 new
@cfp = @program.build_cfp
@cfp = @program.cfps.new
end
def edit; end
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?
if @cfp.save
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.'
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
end
end
def update
@cfp = @program.cfp
@cfp.assign_attributes(cfp_params)
send_mail_on_cfp_dates_updates = @cfp.notify_on_cfp_date_update?
if @cfp.update_attributes(cfp_params)
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.'
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
end
end
def 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
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('. ')}."
end
end
@ -54,7 +55,7 @@ module Admin
private
def cfp_params
params.require(:cfp).permit(:start_date, :end_date)
params.require(:cfp).permit(:start_date, :end_date, :cfp_type)
end
end
end

View file

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

View file

@ -21,8 +21,8 @@ module Admin
@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
@deactive_conferences = Conference.
get_conferences_without_active_for_dashboard(@active_conferences) # conferences without active
@deactive_conferences = Conference
.get_conferences_without_active_for_dashboard(@active_conferences) # conferences without active
@conferences = @active_conferences + @deactive_conferences
@recent_users = User.limit(5).order(created_at: :desc)
@ -37,6 +37,9 @@ module Admin
@registrations = {}
@registration_weeks = [0]
@tickets = {}
@ticket_weeks = [0]
@conferences.each do |c|
# Event submissions over time chart
@submissions[c.short_title] = c.get_submissions_per_week
@ -45,6 +48,10 @@ module Admin
# Conference registrations over time chart
@registrations[c.short_title] = c.get_registrations_per_week
@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
@cfp_weeks = @cfp_weeks.max
@ -55,6 +62,10 @@ module Admin
@registrations = normalize_array_length(@registrations, @registration_weeks)
@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
@user_distribution = Conference.user_distribution
end
@ -65,7 +76,7 @@ module Admin
def create
@conference = Conference.new(conference_params)
@conference.organization = Organization.find_or_create_by(name: 'organization')
if @conference.save
# user that creates the conference becomes organizer of that conference
current_user.add_role :organizer, @conference
@ -73,7 +84,7 @@ module Admin
redirect_to admin_conference_path(id: @conference.short_title),
notice: 'Conference was successfully created.'
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'
end
end
@ -82,7 +93,6 @@ module Admin
short_title = @conference.short_title
@conference.assign_attributes(conference_params)
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)
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
@total_submissions = @program.events.count
@new_submissions = @program.events.
where('created_at > ?', current_user.last_sign_in_at).count
@new_submissions = @program.events
.where('created_at > ?', current_user.last_sign_in_at).count
@program_length = @conference.current_program_hours
@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')
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
@event_type_distribution = @conference.event_type_distribution
@event_type_distribution_confirmed = @conference.event_type_distribution(:confirmed)
@difficulty_levels_distribution = @conference.difficulty_levels_distribution
@difficulty_levels_distribution_confirmed = @conference.
difficulty_levels_distribution(:confirmed)
@difficulty_levels_distribution_confirmed = @conference
.difficulty_levels_distribution(:confirmed)
@tracks_distribution = @conference.tracks_distribution
@tracks_distribution_confirmed = @conference.tracks_distribution(:confirmed)
@ -187,16 +211,7 @@ module Admin
:vpositions_attributes, :use_volunteers, :color,
:sponsorship_levels_attributes, :sponsors_attributes,
:targets, :targets_attributes,
:campaigns, :campaigns_attributes, :registration_limit)
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)
:campaigns, :campaigns_attributes, :registration_limit, :organization_id, :ticket_layout)
end
end
end

View file

@ -20,7 +20,7 @@ module Admin
redirect_to admin_conference_program_difficulty_levels_path(conference_id: @conference.short_title),
notice: 'Difficulty level successfully created.'
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
end
end
@ -30,7 +30,7 @@ module Admin
redirect_to admin_conference_program_difficulty_levels_path(conference_id: @conference.short_title),
notice: 'Difficulty level successfully updated.'
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
end
end

View file

@ -18,7 +18,7 @@ module Admin
redirect_to admin_conference_program_event_types_path(conference_id: @conference.short_title),
notice: 'Event type successfully created.'
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
end
end
@ -28,7 +28,7 @@ module Admin
redirect_to admin_conference_program_event_types_path(conference_id: @conference.short_title),
notice: 'Event type successfully updated.'
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
end
end

View file

@ -5,7 +5,7 @@ module Admin
load_and_authorize_resource :event, through: :program
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
# you get lost in timezone conversions...
@ -50,8 +50,8 @@ module Admin
@ratings = @event.votes.includes(:user)
@difficulty_levels = @program.difficulty_levels
@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_changes(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 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 LIKE ?', "%\nevent_id: #{@event.id}\n%")
end
@ -79,6 +79,7 @@ module Admin
end
def update
@languages = @program.languages_list
if @event.update_attributes(event_params)
if request.xhr?
@ -89,12 +90,29 @@ module Admin
end
else
@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
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
send_mail = @event.program.conference.email_settings.send_on_accepted
@ -161,7 +179,8 @@ module Admin
# Set only in admin/events controller
:track_id, :state, :language, :is_highlight, :max_attendees,
# Not used anymore?
:proposal_additional_speakers, :user, :users_attributes)
:proposal_additional_speakers, :user, :users_attributes,
speaker_ids: [])
end
def comment_params

View file

@ -16,7 +16,7 @@ module Admin
redirect_to admin_conference_lodgings_path(conference_id: @conference.short_title),
notice: 'Lodging successfully created.'
else
flash[:error] = "Creating Lodging failed: #{@lodging.errors.full_messages.join('. ')}."
flash.now[:error] = "Creating Lodging failed: #{@lodging.errors.full_messages.join('. ')}."
render :new
end
end
@ -28,7 +28,7 @@ module Admin
redirect_to admin_conference_lodgings_path(conference_id: @conference.short_title),
notice: 'Lodging successfully updated.'
else
flash[:error] = "Update Lodging failed: #{@lodging.errors.full_messages.join('. ')}."
flash.now[:error] = "Update Lodging failed: #{@lodging.errors.full_messages.join('. ')}."
render :edit
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.assign_attributes(program_params)
send_mail_on_schedule_public = @program.notify_on_schedule_public?
event_schedules_count_was = @program.event_schedules.count
if @program.update_attributes(program_params)
ConferenceScheduleUpdateMailJob.perform_later(@conference) if send_mail_on_schedule_public
respond_to do |format|
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
format.js { render json: {} }
end
else
respond_to do |format|
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
end
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
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

View file

@ -1,5 +1,5 @@
module Admin
class RegistrationPeriodsController < ApplicationController
class RegistrationPeriodsController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource through: :conference, singleton: true
@ -16,7 +16,7 @@ module Admin
redirect_to admin_conference_registration_period_path(@conference.short_title),
notice: 'Registration Period successfully updated.'
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
end
end
@ -36,7 +36,7 @@ module Admin
redirect_to admin_conference_registration_period_path(@conference.short_title),
notice: 'Registration Period successfully updated.'
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('. ')}."
render :edit
end

View file

@ -24,7 +24,7 @@ module Admin
redirect_to admin_conference_registrations_path(@conference.short_title),
notice: "Successfully updated registration for #{@registration.user.email}!"
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('. ')}."
render :edit
end

View file

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

View file

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

View file

@ -8,14 +8,26 @@ module Admin
def index
@roles = Role.where(resource: @conference)
tracks = @conference.program.tracks.where.not(submitter: nil)
@roles += Role.where(resource: tracks)
authorize! :index, @role
end
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
end
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
end
@ -23,11 +35,17 @@ module Admin
role_name = @role.name
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
else
@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
end
end
@ -36,8 +54,14 @@ module Admin
user = User.find_by(email: user_params[:email])
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
redirect_to admin_conference_role_path(@conference.short_title, @role.name),
redirect_to url,
error: 'Could not find user. Please provide a valid email!'
return
end
@ -49,17 +73,23 @@ module Admin
return
end
if @role.resource_type == 'Conference'
role_resource = @conference
elsif @role.resource_type == 'Track'
role_resource = @track
end
# Remove user
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}"
else
flash[:error] = "Could not remove role #{@role.name} from user #{user.email}"
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}"
# 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}"
else
flash[:error] = "Coud not add role #{@role.name} to #{user.email}"
@ -67,7 +97,7 @@ module Admin
respond_to do |format|
format.js
format.html { redirect_to admin_conference_role_path(@conference.short_title, @role.name) }
format.html { redirect_to url }
end
end
@ -77,7 +107,12 @@ module Admin
# Set 'organizer' as default role, when there is no other selection
@selection = params[:id] ? params[:id].parameterize.underscore : 'organizer'
@role = Role.find_by(name: @selection, resource: @conference)
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)
end
end
def role_params

View file

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

View file

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

View file

@ -19,7 +19,7 @@ module Admin
redirect_to admin_conference_sponsorship_levels_path(conference_id: @conference.short_title),
notice: 'Sponsorship level successfully created.'
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
end
end
@ -30,7 +30,7 @@ module Admin
conference_id: @conference.short_title),
notice: 'Sponsorship level successfully updated.'
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
end
end

View file

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

View file

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

View file

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

View file

@ -6,6 +6,17 @@ module Admin
@user = User.new
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
@users = User.all
end
@ -50,8 +61,9 @@ module Admin
private
def user_params
params.require(:user).permit(:email, :name, :email_public, :biography, :nickname, :affiliation, :is_admin, :username, :login, :is_disabled,
:tshirt, :mobile, :volunteer_experience, :languages, :to_confirm, role_ids: [])
params.require(:user).permit(:email, :name, :email_public, :biography, :nickname, :affiliation, :is_admin,
:username, :login, :is_disabled, :tshirt, :mobile, :volunteer_experience,
:languages, :to_confirm, :password, role_ids: [])
end
end
end

View file

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

View file

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

View file

@ -38,12 +38,12 @@ class ConferenceRegistrationsController < ApplicationController
def create
@registration = @conference.registrations.new(registration_params)
if current_user.nil?
# @user variable needs to be set so that _sign_up_form_embedded works properly
@user = @registration.build_user(user_params)
else
@user = current_user
end
@user = if current_user.nil?
# @user variable needs to be set so that _sign_up_form_embedded works properly
@registration.build_user(user_params)
else
current_user
end
@registration.user = @user
authorize! :create, @registration
@ -65,7 +65,7 @@ class ConferenceRegistrationsController < ApplicationController
notice: 'You are now registered and will be receiving E-Mail notifications.'
end
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('. ')}."
render :new
end
@ -76,7 +76,7 @@ class ConferenceRegistrationsController < ApplicationController
redirect_to conference_conference_registration_path(@conference.short_title),
notice: 'Registration was successfully updated.'
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('. ')}."
render :edit
end
@ -108,8 +108,8 @@ class ConferenceRegistrationsController < ApplicationController
end
def registration_params
params.require(:registration).
permit(
params.require(:registration)
.permit(
:conference_id, :arrival, :departure,
:volunteer,
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
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.'
else
@total_amount_to_pay = Ticket.total_price(@conference, current_user, paid: false)
@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
end
end
@ -38,6 +38,8 @@ class PaymentsController < ApplicationController
end
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

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
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)
@speakers_ordered = @event.speakers_ordered
end
def new
@ -40,7 +39,7 @@ class ProposalsController < ApplicationController
if @user.save
sign_in(@user)
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'
return
end
@ -48,16 +47,13 @@ class ProposalsController < ApplicationController
# User which creates the proposal is both `submitter` and `speaker` of proposal
# by default.
# TODO: Allow submitter to add speakers to proposals
@event.event_users.new(user: current_user,
event_role: 'submitter')
@event.event_users.new(user: current_user,
event_role: 'speaker')
@event.speakers = [current_user]
@event.submitter = current_user
if @event.save
ahoy.track 'Event submission', title: 'New submission'
redirect_to conference_program_proposals_path(@conference.short_title), notice: 'Proposal was successfully submitted.'
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'
end
end
@ -69,7 +65,7 @@ class ProposalsController < ApplicationController
redirect_to conference_program_proposals_path(conference_id: @conference.short_title),
notice: 'Proposal was successfully updated.'
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'
end
end
@ -147,7 +143,9 @@ class ProposalsController < ApplicationController
def event_params
params.require(:event).permit(:event_type_id, :track_id, :difficulty_level_id,
:title, :subtitle, :abstract, :description,
:require_registration, :max_attendees, :language)
:require_registration, :max_attendees, :language,
speaker_ids: []
)
end
def user_params

View file

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

View file

@ -1,7 +1,8 @@
class SchedulesController < ApplicationController
load_and_authorize_resource
protect_from_forgery with: :null_session
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
def show
@ -13,7 +14,7 @@ class SchedulesController < ApplicationController
@events_xml = schedules.map(&:event).group_by{ |event| event.time.to_date } if schedules
@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_period = @conference.end_hour - @conf_start

View file

@ -11,7 +11,7 @@ class TicketPurchasesController < ApplicationController
redirect_to new_conference_payment_path,
notice: 'Please pay here to get tickets.'
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.'
else
redirect_to conference_tickets_path(@conference.short_title),
@ -23,6 +23,10 @@ class TicketPurchasesController < ApplicationController
end
end
def index
@unpaid_ticket_purchases = current_user.ticket_purchases.by_conference(@conference).unpaid
end
private
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)
redirect_to @user, notice: 'User was successfully updated.'
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
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
##
# Returns a string build from the start and end date of the given conference.
#
# If the conference is only one day long
@ -34,43 +33,9 @@ module ApplicationHelper
result
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
##
# 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}"
# Returns time with conference timezone
def time_with_timezone(time)
time.strftime('%F %R') + ' ' + @conference.timezone.to_s
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)
@ -78,141 +43,6 @@ module ApplicationHelper
:user
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 = {})
link_to_add_association 'Add ' + association_name.to_s.singularize, form_builder, div_class, html_options.merge(class: 'assoc btn btn-success')
end
@ -225,29 +55,6 @@ module ApplicationHelper
render 'shared/dynamic_association', association_name: association_name, title: title, f: form_builder, hint: options[:hint]
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)
all = conference.program.tracks.map {|t| t.name}
first = all[0...-1]
@ -276,155 +83,19 @@ module ApplicationHelper
return ts
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)
Comment.accessible_by(current_ability).find_since_last_login(user)
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
# 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]}'
# Output will be 'title, description and conference'
def updated_attributes(version)
version.changeset.
reject{ |_, values| values[0].blank? && values[1].blank? }.
keys.map{ |key| key.gsub('_id', '').tr('_', ' ')}.join(', ').
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
version.changeset
.reject{ |_, values| values[0].blank? && values[1].blank? }
.keys.map{ |key| key.gsub('_id', '').tr('_', ' ')}.join(', ')
.reverse.sub(',', ' dna ').reverse
end
# Recieves a model_name and id
@ -446,136 +117,23 @@ module ApplicationHelper
object
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 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"
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 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"
# 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
"deleted #{user.name}'s comment on"
redirect_to options, response_status
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)
return nil unless event.scheduled? && event.program.selected_event_schedules
event_schedule = event.program.selected_event_schedules.find_by(event: event)
@ -592,4 +150,36 @@ module ApplicationHelper
end
concurrent_events
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

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
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
# This is what sets up the different abilities
if user.new_record?
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
signed_in(user)
common_abilities_for_admins(user) if user.roles.any? || user.is_admin?
end
end
# Abilities for not signed in users (guests)
def not_signed_in
can [:index], Organization
can [:index], Conference
can [:show], Conference do |conference|
conference.splashpage && conference.splashpage.public == true
@ -62,6 +49,10 @@ class Ability
can [:new, :create], Event do |event|
event.program.cfp_open? && event.new_record?
end
can [:show, :events], Schedule do |schedule|
schedule.program.schedule_public
end
end
end
@ -69,19 +60,20 @@ class Ability
def signed_in(user)
# Abilities from not_signed_in user are also inherited
not_signed_in
can :manage, User, id: user.id
can :manage, Registration, user_id: user.id
can [:new, :create], Registration do |registration|
conference = registration.conference
conference.registration_open? && !conference.registration_limit_exceeded?
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 [:index, :show], PhysicalTicket, user: user
can [:create, :destroy], Subscription, user_id: user.id
@ -95,171 +87,42 @@ class Ability
# can manage the commercials of their own events
can :manage, Commercial, commercialable_type: 'Event', commercialable_id: user.events.pluck(:id)
can [:destroy], Openid
end
# Abilities for signed in users with roles
def signed_in_with_roles(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
# Abilities for users with roles wandering around in non-admin views.
def common_abilities_for_admins(user)
can :access, Admin
can [:show], Conference
can :index, Commercial, commercialable_type: 'Conference'
cannot [:edit, :update, :destroy], Question, global: true
# for admins
can :manage, :all if user.is_admin
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)
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)
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)
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?
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
# 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)
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
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)
if conf_ids_for_info_desk
# To access conference/proposals/registrations
can :manage, Registration, conference_id: conf_ids_for_info_desk
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
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 }
belongs_to :program
@ -8,6 +12,15 @@ class Cfp < ActiveRecord::Base
validates :start_date, :end_date, presence: true
validate :before_end_of_conference
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
@ -16,11 +29,11 @@ class Cfp < ActiveRecord::Base
# * +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
def notify_on_cfp_date_update?
!self.end_date.blank? && !self.start_date.blank?\
&& (self.start_date_changed? || self.end_date_changed?)\
&& self.program.conference.email_settings.send_on_cfp_dates_updated\
&& !self.program.conference.email_settings.cfp_dates_updated_subject.blank?\
&& !self.program.conference.email_settings.cfp_dates_updated_body.blank?
!end_date.blank? && !start_date.blank?\
&& (start_date_changed? || end_date_changed?)\
&& program.conference.email_settings.send_on_cfp_dates_updated\
&& !program.conference.email_settings.cfp_dates_updated_subject.blank?\
&& !program.conference.email_settings.cfp_dates_updated_body.blank?
end
##
@ -51,23 +64,37 @@ class Cfp < ActiveRecord::Base
end
def remaining_days(date = Date.today)
result = (self.end_date - date).to_i
result = (end_date - date).to_i
result > 0 ? result : 0
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
def before_end_of_conference
errors.
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)
if program && 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.
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)
if program && 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
def start_after_end_date
errors.
add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date
errors
.add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date
end
def conference_id

View file

@ -1,19 +1,19 @@
class Comment < ActiveRecord::Base
acts_as_nested_set scope: [:commentable_id, :commentable_type]
validates_presence_of :body
validates_presence_of :user
acts_as_nested_set scope: %i(commentable_id commentable_type)
validates :body, presence: true
validates :user, presence: true
after_create :send_notification
# NOTE: install the acts_as_votable plugin if you
# want user to vote on the quality of comments.
#acts_as_votable
belongs_to :commentable, polymorphic: true
belongs_to :commentable, counter_cache: true, polymorphic: true
# NOTE: Comments belong to a 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
# 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
def has_children?
self.children.any?
children.any?
end
# 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
include RevisionCount
require 'uri'
serialize :events_per_week, Hash
# Needed to call 'Conference.with_role' in /models/ability.rb
@ -6,8 +7,12 @@ class Conference < ActiveRecord::Base
resourcify :roles, dependent: :delete_all
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
@ -17,6 +22,7 @@ class Conference < ActiveRecord::Base
has_one :email_settings, dependent: :destroy
has_one :program, dependent: :destroy
has_one :venue, dependent: :destroy
has_many :physical_tickets, through: :ticket_purchases
has_many :ticket_purchases, dependent: :destroy
has_many :payments, dependent: :destroy
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
validates_presence_of :title,
:short_title,
:start_date,
:end_date,
:start_hour,
:end_hour
validates :title,
:short_title,
:start_date,
:end_date,
:start_hour,
:end_hour,
:ticket_layout,
:organization, presence: true
validates_uniqueness_of :short_title
validates_format_of :short_title, with: /\A[a-zA-Z0-9_-]*\z/
validates :short_title, uniqueness: true
validates :short_title, format: { with: /\A[a-zA-Z0-9_-]*\z/ }
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
@ -67,6 +75,9 @@ class Conference < ActiveRecord::Base
before_create :create_email_settings
after_create :create_free_ticket
after_update :delete_event_schedules
enum ticket_layout: [:portrait, :landscape]
##
# 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
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
#
@ -113,7 +138,7 @@ class Conference < ActiveRecord::Base
result = []
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
weeks = program.cfp.weeks
result = calculate_items_per_week(start_week, weeks, submissions)
@ -161,7 +186,7 @@ class Conference < ActiveRecord::Base
registration_period.start_date &&
registration_period.end_date
reg = registrations.group(:week).count
reg = registrations.group(:week).order(:week).count
start_week = get_registration_start_week
weeks = registration_weeks
result = calculate_items_per_week(start_week, weeks, reg)
@ -169,6 +194,59 @@ class Conference < ActiveRecord::Base
result
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.
#
@ -180,8 +258,8 @@ class Conference < ActiveRecord::Base
if registration_period &&
registration_period.start_date &&
registration_period.end_date
weeks = Date.new(registration_period.start_date.year, 12, 31).
strftime('%W').to_i
weeks = Date.new(registration_period.start_date.year, 12, 31)
.strftime('%W').to_i
result = get_registration_end_week - get_registration_start_week + 1
end
@ -266,8 +344,8 @@ class Conference < ActiveRecord::Base
# ====Returns
# * +hash+ -> user: submissions
def self.get_top_submitter(limit = 5)
submitter = EventUser.where('event_role = ?', 'submitter').limit(limit).group(:user_id)
counter = submitter.order('count_all desc').count
submitter = EventUser.select(:user_id).where('event_role = ?', 'submitter').limit(limit).group(:user_id)
counter = submitter.order('count_all desc').count(:all)
calculate_user_submission_hash(submitter, counter)
end
@ -277,10 +355,10 @@ class Conference < ActiveRecord::Base
# ====Returns
# * +hash+ -> user: submissions
def get_top_submitter(limit = 5)
submitter = EventUser.joins(:event).
where('event_role = ? and program_id = ?', 'submitter', Conference.find(id).program.id).
limit(limit).group(:user_id)
counter = submitter.order('count_all desc').count
submitter = EventUser.joins(:event).select(:user_id)
.where('event_role = ? and program_id = ?', 'submitter', Conference.find(id).program.id)
.limit(limit).group(:user_id)
counter = submitter.order('count_all desc').count(:all)
Conference.calculate_user_submission_hash(submitter, counter)
end
@ -311,9 +389,10 @@ class Conference < ActiveRecord::Base
# ====Returns
# * +hash+ -> hash
def scheduled_event_distribution
confirmed_events = program.events.where(state: 'confirmed')
scheduled_value = { 'value' => confirmed_events.where.not(start_time: nil).count, 'color' => 'green' }
unscheduled_value = { 'value' => confirmed_events.where(start_time: nil).count, 'color' => 'red' }
confirmed_scheduled_events = program.events.confirmed.scheduled(program.selected_schedule.try(:id))
confirmed_unscheduled_events = program.events.confirmed - confirmed_scheduled_events
scheduled_value = { 'value' => confirmed_scheduled_events.count, 'color' => 'green' }
unscheduled_value = { 'value' => confirmed_unscheduled_events.count, 'color' => 'red' }
{ 'Scheduled' => scheduled_value, 'Unscheduled' => unscheduled_value }
end
@ -380,6 +459,46 @@ class Conference < ActiveRecord::Base
calculate_user_distribution_hash(active_user, unconfirmed_user, dead_user)
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
#
@ -444,11 +563,11 @@ class Conference < ActiveRecord::Base
# ====Returns
# * +hash+ -> track => {color, value}
def tracks_distribution(state = nil)
if state
tracks_grouped = program.events.select(:track_id).where('state = ?', state).group(:track_id)
else
tracks_grouped = program.events.select(:track_id).group(:track_id)
end
tracks_grouped = if state
program.events.select(:track_id).where('state = ?', state).group(:track_id)
else
program.events.select(:track_id).group(:track_id)
end
tracks_counted = tracks_grouped.count
calculate_track_distribution_hash(tracks_grouped, tracks_counted)
@ -461,13 +580,13 @@ class Conference < ActiveRecord::Base
# ====Returns
# * +ActiveRecord+
def self.get_active_conferences_for_dashboard
result = Conference.where('start_date > ?', Time.now).
select('id, short_title, color, start_date')
result = Conference.where('start_date > ?', Time.now)
.select('id, short_title, color, start_date')
if result.length == 0
result = Conference.
select('id, short_title, color, start_date').limit(2).
order(start_date: :desc)
if result.empty?
result = Conference
.select('id, short_title, color, start_date').limit(2)
.order(start_date: :desc)
end
result
end
@ -531,7 +650,7 @@ class Conference < ActiveRecord::Base
def self.write_event_distribution_to_db
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 = {}
Event.state_machine.states.each do |state|
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
# * +False+ -> Either conference is not updated or one or more parameter is not set
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
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
(!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
##
@ -569,17 +688,24 @@ class Conference < ActiveRecord::Base
# * +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
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
return false unless self.registration_period
return false unless registration_period
# do not notify unless one of the dates changed
return false unless registration_period.start_date_changed? || registration_period.end_date_changed?
# 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
##
# 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?
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
# 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
end
##
# Return the current conference object to be used in RevisionCount
#
# ====Returns
# * +ActiveRecord+
def conference
self
end
private
# Returns a different html colour for every i and consecutive colors are
@ -628,8 +763,8 @@ class Conference < ActiveRecord::Base
end
after_create do
self.create_contact
self.create_program
create_contact
create_program
create_roles
end
@ -699,7 +834,7 @@ class Conference < ActiveRecord::Base
# Completed weeks
events_per_week.each do |week, values|
values.each do |state, value|
if [:confirmed, :unconfirmed].include?(state)
if %i(confirmed unconfirmed).include?(state)
unless result[state.to_s.capitalize]
result[state.to_s.capitalize] = {}
end
@ -780,7 +915,7 @@ class Conference < ActiveRecord::Base
hash[key] = 0
end
end
Hash[ hash.sort ]
Hash[hash.sort]
end
##
@ -866,11 +1001,11 @@ class Conference < ActiveRecord::Base
# ====Returns
# * +hash+ -> object_type => {color, value}
def calculate_event_distribution(group_by_id, association_symbol, state = nil)
if state
grouped = program.events.select(group_by_id).where('state = ?', 'confirmed').group(group_by_id)
else
grouped = program.events.select(group_by_id).group(group_by_id)
end
grouped = if state
program.events.select(group_by_id).where('state = ?', 'confirmed').group(group_by_id)
else
program.events.select(group_by_id).group(group_by_id)
end
counted = grouped.count
calculate_distribution_hash(grouped, counted, association_symbol)
@ -981,7 +1116,8 @@ class Conference < ActiveRecord::Base
def self.calculate_user_submission_hash(submitters, counter)
result = ActiveSupport::OrderedHash.new
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
result[submitter.user] = value
end

View file

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

View file

@ -10,6 +10,8 @@ class EventSchedule < ActiveRecord::Base
validates :room, presence: true
validates :start_time, presence: true
validates :event, uniqueness: { scope: :schedule }
validate :start_after_end_hour
validate :start_before_start_hour
scope :confirmed, -> { joins(:event).where('state = ?', 'confirmed') }
scope :canceled, -> { joins(:event).where('state = ?', 'canceled') }
@ -37,6 +39,16 @@ class EventSchedule < ActiveRecord::Base
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
schedule.program.conference_id
end

View file

@ -15,17 +15,13 @@ class EventType < ActiveRecord::Base
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
##
# 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
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
def capitalize_color

View file

@ -1,6 +1,6 @@
class EventUser < ActiveRecord::Base
# TODO Do we need these roles?
ROLES = [['Speaker', 'speaker'], ['Submitter', 'submitter'], ['Moderator', 'moderator']]
# TODO: Do we need these roles?
ROLES = [%w[Speaker speaker], %w[Submitter submitter], %w[Moderator moderator]]
belongs_to :event
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
has_one :cfp, dependent: :destroy
has_many :cfps, dependent: :destroy
has_many :event_types, dependent: :destroy
has_many :tracks, dependent: :destroy
has_many :difficulty_levels, dependent: :destroy
has_many :schedules, dependent: :destroy
has_many :event_schedules, through: :schedules
belongs_to :selected_schedule, class_name: 'Schedule'
has_many :events, dependent: :destroy do
def require_registration
@ -38,12 +39,17 @@ class Program < ActiveRecord::Base
where(state: :confirmed, is_highlight: true)
end
end
has_many :event_schedules, through: :events
has_many :event_users, through: :events
has_many :speakers, -> { distinct }, through: :event_users, source: :user do
def confirmed
joins(:events).where(events: { state: :confirmed })
end
def registered(conference)
joins(:registrations).where('registrations.conference_id = ?', conference.id)
end
end
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 :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_dates_exist
after_create :create_event_types
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
# 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
# * +true+ -> If rating is enabled
def rating_enabled?
self.rating && self.rating > 0
rating && rating > 0
end
##
@ -139,7 +149,7 @@ class Program < ActiveRecord::Base
end
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
##
@ -153,6 +163,23 @@ class Program < ActiveRecord::Base
EventSchedule.where(schedule: selected_schedule).where(start_time: parsed_date..(parsed_date + 1)).any?
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
##
@ -161,10 +188,10 @@ class Program < ActiveRecord::Base
def create_event_types
EventType.create(title: 'Talk', length: 30, color: '#FF0000', description: 'Presentation in lecture format',
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',
minimum_abstract_length: 0,
maximum_abstract_length: 500, program_id: self.id)
maximum_abstract_length: 500, program_id: id)
true
end
@ -174,13 +201,13 @@ class Program < ActiveRecord::Base
def create_difficulty_levels
DifficultyLevel.create(title: 'Easy',
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',
description: 'Events require a basic understanding of the topic.',
color: '#EEEF69', program_id: self.id)
color: '#E6B65B', program_id: id)
DifficultyLevel.create(title: 'Hard',
description: 'Events require expert knowledge of the topic.',
color: '#EF6E69', program_id: self.id)
color: '#EF6E69', program_id: id)
true
end
@ -188,15 +215,42 @@ class Program < ActiveRecord::Base
# Check if languages string has the right format. Used as validation.
#
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
self.languages = self.languages.delete(' ').downcase
self.languages = languages.delete(' ').downcase
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_array = self.languages.split(',')
languages.match(/^$|(\A[a-z][a-z](,[a-z][a-z])*\z)/).present?
languages_array = languages.split(',')
# We check that languages are not repeated
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
errors.add(:languages, 'must be ISO 639-1 valid codes') unless languages_array.select{ |x| ISO_639.find(x).nil? }.empty?
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

View file

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

View file

@ -2,14 +2,13 @@ class Registration < ActiveRecord::Base
belongs_to :user
belongs_to :conference
has_and_belongs_to_many :events
has_and_belongs_to_many :qanswers
has_and_belongs_to_many :vchoices
has_many :events_registrations
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 :qanswers
@ -24,7 +23,7 @@ class Registration < ActiveRecord::Base
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_to_events_only_if_present

View file

@ -10,15 +10,15 @@ class RegistrationPeriod < ActiveRecord::Base
private
def before_end_of_conference
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)
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)
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)
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)
end
def start_date_before_end_date
errors.
add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date
errors
.add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date
end
end

View file

@ -1,10 +1,12 @@
class Resource < ActiveRecord::Base
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
def used_less_than_quantity
errors.add(:used, 'can not be higher than total quantity') unless used <= quantity
def used_no_more_than_quantity
errors.add(:used, 'cannot be higher than total quantity') if used.present? && quantity.present? && used > quantity
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
include RevisionCount
belongs_to :venue
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
def conference
venue.conference
end
private
def generate_guid

View file

@ -6,5 +6,5 @@ class Sponsor < ActiveRecord::Base
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

View file

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

View file

@ -1,9 +1,9 @@
class Subscription < ActiveRecord::Base
validates_uniqueness_of :user_id, scope: [:conference_id]
validates :user_id, uniqueness: { scope: [:conference_id] }
belongs_to :conference
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

View file

@ -13,7 +13,7 @@ class Ticket < ActiveRecord::Base
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)
buyers.include?(user)
@ -56,7 +56,7 @@ class Ticket < ActiveRecord::Base
end
def tickets_sold
ticket_purchases.sum(:quantity)
ticket_purchases.paid.sum(:quantity)
end
def tickets_turnover
@ -66,7 +66,7 @@ class Ticket < ActiveRecord::Base
private
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.')
end
end

View file

@ -2,10 +2,11 @@ class TicketPurchase < ActiveRecord::Base
belongs_to :ticket
belongs_to :user
belongs_to :conference
belongs_to :payment
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 :description, to: :ticket
@ -13,10 +14,14 @@ class TicketPurchase < ActiveRecord::Base
delegate :price_cents, to: :ticket
delegate :price_currency, to: :ticket
has_many :physical_tickets
scope :paid, -> { where(paid: true) }
scope :unpaid, -> { where(paid: false) }
scope :by_conference, -> (conference) { where(conference_id: conference.id) }
scope :by_user, -> (user) { where(user_id: user.id) }
scope :by_conference, ->(conference) { where(conference_id: conference.id) }
scope :by_user, ->(user) { where(user_id: user.id) }
after_create :set_week
def self.purchase(conference, user, purchases)
errors = []
@ -24,11 +29,11 @@ class TicketPurchase < ActiveRecord::Base
conference.tickets.each do |ticket|
quantity = purchases[ticket.id.to_s].to_i
# if the user bought the ticket and is still unpaid, just update the quantity
if ticket.bought?(user) && ticket.unpaid?(user)
purchase = update_quantity(conference, quantity, ticket, user)
else
purchase = purchase_ticket(conference, quantity, ticket, user)
end
purchase = if ticket.bought?(user) && ticket.unpaid?(user)
update_quantity(conference, quantity, ticket, user)
else
purchase_ticket(conference, quantity, ticket, user)
end
if purchase && !purchase.save
errors.push(purchase.errors.full_messages)
@ -43,8 +48,8 @@ class TicketPurchase < ActiveRecord::Base
purchase = new(ticket_id: ticket.id,
conference_id: conference.id,
user_id: user.id,
quantity: quantity,
paid: ticket.price_cents.zero?)
quantity: quantity)
purchase.pay(nil) if ticket.price_cents.zero?
end
purchase
end
@ -58,4 +63,18 @@ class TicketPurchase < ActiveRecord::Base
purchase.quantity = quantity if quantity > 0
purchase
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

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
include RevisionCount
resourcify :roles, dependent: :delete_all
belongs_to :program
belongs_to :submitter, class_name: 'User'
has_many :events, dependent: :nullify
has_paper_trail only: [:name, :description, :color], meta: { conference_id: :conference_id }
@ -7,9 +12,37 @@ class Track < ActiveRecord::Base
before_create :generate_guid
validates :name, presence: true
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
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
def generate_guid
@ -27,4 +60,10 @@ class Track < ActiveRecord::Base
def conference_id
program.conference_id
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

View file

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

View file

@ -35,7 +35,7 @@ class Venue < ActiveRecord::Base
def notify_on_venue_changed?
return false unless conference.try(:email_settings).try(:send_on_venue_updated)
# 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
(!conference.email_settings.venue_updated_subject.blank? && !conference.email_settings.venue_updated_body.blank?)
end

View file

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

View file

@ -60,7 +60,7 @@ class ConferenceSerializer < ActiveModel::Serializer
def date_range
if defined? date_string(object.start_date, object.end_date)
date_string(object.start_date, object.end_date).try(:split, ',').try(:first)
date_string(object.start_date, object.end_date).try(:split, ',').try(:first)
end
end
end

View file

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

View file

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

View file

@ -52,6 +52,10 @@ class PictureUploader < CarrierWave::Uploader::Base
"system/#{object_class_name}/#{mounted_as}/#{model.id}"
end
def image
@image ||= MiniMagick::Image.open(file.file)
end
# Create different versions of your uploaded files:
version :large do
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
.row
.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 :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
= 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
%p.text-muted
Call for people to submit events to your conference
- if @cfp
.row
.col-md-8
%dl.dl-horizontal
%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
.row
.col-md-12.text-right
= link_to(edit_admin_conference_program_cfp_path(@conference.short_title), class: 'btn btn-primary') do
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
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'
.row
.col-md-8
%dl.dl-horizontal
= render "#{@cfp.cfp_type}_cfp"
.row
.col-md-12.text-right
= link_to(edit_admin_conference_program_cfp_path(@conference.short_title, @cfp.id), class: 'btn btn-primary') do
Edit
= 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

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