diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..e235b236 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +Dockerfile +docker-compose.* diff --git a/.gitignore b/.gitignore index 4d5fd427..ce3e7f0f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,7 @@ pickle-email-*.html .env.development .env.test .env.local +docker-compose.env +docker-compose.yml +.DS_Store +.byebug_history diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index 2c51e966..b79ddc17 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `haml-lint --auto-gen-config` -# on 2017-04-25 04:36:49 +0530 using Haml-Lint version 0.24.0. +# 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 @@ -8,9 +8,13 @@ linters: - # Offense count: 952 + # Offense count: 945 LineLength: exclude: + - "app/views/admin/booths/_change_state_dropdown.html.haml" + - "app/views/admin/booths/_form.html.haml" + - "app/views/admin/booths/index.html.haml" + - "app/views/admin/booths/show.html.haml" - "app/views/admin/campaigns/_form.html.haml" - "app/views/admin/campaigns/index.html.haml" - "app/views/admin/cfps/_form.html.haml" @@ -49,10 +53,11 @@ linters: - "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/reports.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" @@ -73,6 +78,7 @@ linters: - "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/_users_with_org_admin_role.haml" - "app/views/admin/roles/index.html.haml" - "app/views/admin/roles/show.html.haml" - "app/views/admin/rooms/_form.html.haml" @@ -105,6 +111,9 @@ linters: - "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/booths/_form.html.haml" + - "app/views/booths/index.html.haml" + - "app/views/booths/show.html.haml" - "app/views/commercials/edit.html.haml" - "app/views/commercials/new.html.haml" - "app/views/conference_registrations/_form.html.haml" @@ -112,6 +121,7 @@ linters: - "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/_booths.html.haml" - "app/views/conferences/_call_for_paper.html.haml" - "app/views/conferences/_conference_details.html.haml" - "app/views/conferences/_gallery.html.haml" @@ -143,7 +153,9 @@ linters: - "app/views/layouts/_admin_sidebar_index.html.haml" - "app/views/layouts/_messages.html.haml" - "app/views/layouts/_navigation.html.haml" + - "app/views/layouts/_user_menu.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" @@ -169,11 +181,21 @@ linters: - "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" + - "app/views/conferences/_call_for_tracks.html.haml" + - "app/views/admin/tracks/_change_state_dropdown.html.haml" + - "app/views/proposals/_encouragement_text.html.haml" + - "app/views/admin/schedules/_form.html.haml" - # Offense count: 222 + # Offense count: 223 InstanceVariables: exclude: + - "app/views/admin/booths/_change_state_dropdown.html.haml" - "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" @@ -184,6 +206,7 @@ linters: - "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" @@ -226,6 +249,12 @@ linters: - "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" + - "app/views/admin/cfps/_tracks_cfp.html.haml" + - "app/views/conferences/_call_for_tracks.html.haml" + - "app/views/admin/tracks/_change_state_dropdown.html.haml" + - "app/views/admin/schedules/_form.html.haml" # Offense count: 32 IdNames: @@ -244,15 +273,14 @@ linters: - "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" + - "app/views/admin/cfps/_tracks_cfp.html.haml" - # Offense count: 8 + # Offense count: 4 UnnecessaryInterpolation: exclude: - "app/views/admin/conferences/_doughnut_chart.html.haml" - "app/views/admin/conferences/_recent_submissions.html.haml" - - "app/views/admin/events/reports.html.haml" - - "app/views/admin/reports/_events_with_requirements.html.haml" - - "app/views/admin/reports/_events_without_commercials.html.haml" - "app/views/proposals/_proposal_form.html.haml" - "app/views/proposals/new.html.haml" @@ -282,6 +310,7 @@ linters: SpaceInsideHashAttributes: exclude: - "app/views/admin/conferences/_todo_list.html.haml" + - "app/views/admin/emails/index.html.haml" - "app/views/admin/questions/_form.html.haml" - "app/views/admin/questions/index.html.haml" - "app/views/admin/registrations/index.html.haml" @@ -327,12 +356,13 @@ linters: - "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: 27 + # Offense count: 23 ClassesBeforeIds: exclude: - "app/views/admin/emails/index.html.haml" - - "app/views/admin/events/reports.html.haml" - "app/views/admin/events/show.html.haml" - "app/views/admin/reports/index.html.haml" - "app/views/admin/users/show.html.haml" @@ -356,6 +386,7 @@ linters: - "app/views/admin/tracks/show.html.haml" - "app/views/admin/venues/show.html.haml" - "app/views/admin/versions/index.html.haml" + - "app/views/booths/index.html.haml" - "app/views/conference_registrations/_form.html.haml" - "app/views/conference_registrations/_ticket.html.haml" - "app/views/conference_registrations/show.html.haml" @@ -380,12 +411,27 @@ linters: # Offense count: 14 ConsecutiveSilentScripts: exclude: + - "app/views/admin/booths/_change_state_dropdown.html.haml" - "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/booths/index.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: @@ -406,22 +452,9 @@ linters: - "app/views/schedules/_event.html.haml" - "app/views/schedules/_schedule_item.html.haml" - # Offense count: 38 - TrailingWhitespace: - exclude: - - "app/views/admin/users/_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: 7 ClassAttributeWithStaticValue: exclude: - "app/views/conferences/_gallery.html.haml" - "app/views/layouts/_navigation.html.haml" - - "app/views/schedules/events.html.haml" \ No newline at end of file + - "app/views/schedules/events.html.haml" diff --git a/.rubocop.yml b/.rubocop.yml index c333e43a..9ab6f6b3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -13,100 +13,6 @@ AllCops: - 'config/**/*' - 'bin/*' -#################### Style ############################### - -# 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 redundunt curly braces when it is obvious that hash is used -Style/BracesAroundHashParameters: - Enabled: true - -# Avoid the use of the case equality operator `===` -Style/CaseEquality: - Enabled: true - -# Use nested module/class definitions instead of compact style -Style/ClassAndModuleChildren: - Enabled: true - -# Checks the . position in multi-line method calls. -Style/DotPosition: - Enabled: true - -# Checks for uses of double negation (!!) to convert something to a boolean value. -Style/DoubleNegation: - 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 - -# Use the new Ruby 1.9 hash syntax -Style/HashSyntax: - Enabled: true - EnforcedStyle: ruby19 - -# Checks for uses of if with negated condition. Use unless instead -Style/NegatedIf: - Enabled: true - -# Do not compare with nil. Use .nil? instead -Style/NilComparison: - Enabled: true - -# Checks for redundant uses of self. -Style/RedundantSelf: - Enabled: true - -# Checks that operators have space around them, except for ** which should not have surrounding space. -Style/SpaceAroundOperators: - Enabled: true - -# Use single quotes unless there's string interpolation -Style/StringLiterals: - Enabled: true - -# This cop checks for tabs where spaces should be used. -Style/Tab: - Enabled: true - -# Avoid trailing blank lines -Style/TrailingBlankLines: - Enabled: true - -# Avoid trailing whitespace -Style/TrailingWhitespace: - Enabled: true - -# This cop checks for numeric comparisons that can be replaced by a predicate method. -Style/ZeroLengthPredicate: - Enabled: true #################### Metrics ############################### @@ -123,77 +29,6 @@ Metrics/ClassLength: Metrics/BlockLength: Exclude: - 'spec/models/conference_spec.rb' - -#################### Lint ############################### - -# 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 - -# Things deprecated in current ruby API -Lint/DeprecatedClassMethods: - 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 - -# Do not use duplicated keys in hash literals. -Lint/DuplicatedKey: - Enabled: true - -#################### Rails ############################### - -# Enforce Rails specific style -Rails: - Enabled: true - -# Avoid use of old-style attribute validation -Rails/Validation: - Enabled: true - -# Looks for delegations, that could have been created automatically with delegate method -Rails/Delegate: - Enabled: true - -#################### Performance ############################### - -# Identifies places where gsub can be replaced by tr or delete. -Performance/StringReplacement: - Enabled: true + - 'spec/features/ability_spec.rb' + - 'spec/models/ability_spec.rb' + - 'spec/models/admin_ability_spec.rb' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e0c6dd97..a91f2ae9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,12 +1,12 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2017-05-06 17:45:40 +0530 using RuboCop version 0.48.1. +# on 2017-07-14 12:03:16 +0000 using RuboCop version 0.49.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 13 +# Offense count: 15 # Cop supports --auto-correct. # Configuration parameters: Include, TreatCommentsAsGroupSeparators. # Include: **/Gemfile, **/gems.rb @@ -14,29 +14,271 @@ Bundler/OrderedGems: Exclude: - 'Gemfile' -# Offense count: 29 -Lint/AmbiguousBlockAssociation: +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: with_first_parameter, with_fixed_indentation +Layout/AlignParameters: Exclude: - - 'app/models/comment.rb' - - 'app/models/event.rb' - - 'app/models/event_schedule.rb' - - 'app/models/ticket_purchase.rb' - - 'app/models/user.rb' - - 'spec/controllers/admin/conferences_controller_spec.rb' - - 'spec/controllers/admin/event_schedules_controller_spec.rb' - - 'spec/controllers/admin/registration_periods_controller_spec.rb' - - 'spec/controllers/proposals_controller_spec.rb' - - 'spec/controllers/schedules_controller_spec.rb' - - 'spec/models/user_spec.rb' - - 'spec/controllers/admin/users_controller_spec.rb' + - 'Vagrantfile' + +# Offense count: 9 +# Cop supports --auto-correct. +Layout/ClosingParenthesisIndentation: + Exclude: + - 'app/controllers/conference_registrations_controller.rb' + - 'spec/support/omniauth_macros.rb' + +# Offense count: 14 +# Cop supports --auto-correct. +Layout/CommentIndentation: + Exclude: + - 'app/controllers/admin/comments_controller.rb' + - 'app/controllers/admin/difficulty_levels_controller.rb' + - 'app/models/conference.rb' + - 'app/models/program.rb' + - 'app/models/track.rb' + - 'spec/features/volunteers_spec.rb' # Offense count: 1 # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyleAlignWith, SupportedStylesAlignWith. -# SupportedStylesAlignWith: either, start_of_block, start_of_line -Lint/BlockAlignment: +Layout/EmptyLineAfterMagicComment: Exclude: - - 'lib/tasks/demo_data_for_development.rake' + - 'spec/models/conference_spec.rb' + +# Offense count: 104 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: empty_lines, no_empty_lines +Layout/EmptyLinesAroundBlockBody: + Enabled: false + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Exclude: + - 'app/models/payment.rb' + +# Offense count: 9 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Exclude: + - 'Guardfile' + - 'app/controllers/application_controller.rb' + - 'config.ru' + - 'db/migrate/20140623101032_create_ahoy_events.rb' + - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' + - 'db/migrate/20140719160903_create_delayed_jobs.rb' + - 'spec/models/conference_spec.rb' + +# Offense count: 42 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: consistent, special_for_inner_method_call, special_for_inner_method_call_in_parentheses +Layout/FirstParameterIndentation: + Enabled: false + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_brackets +Layout/IndentArray: + Exclude: + - 'app/models/conference.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: IndentationWidth. +Layout/IndentAssignment: + Exclude: + - 'app/helpers/format_helper.rb' + - 'app/models/conference.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_braces +Layout/IndentHash: + Exclude: + - 'app/models/user.rb' + - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: normal, rails +Layout/IndentationConsistency: + Exclude: + - 'app/controllers/users_controller.rb' + - 'app/models/event.rb' + - 'spec/controllers/subscriptions_controller_spec.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +Layout/LeadingCommentSpace: + Exclude: + - 'Guardfile' + - 'app/models/comment.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineArrayBraceLayout: + Exclude: + - 'app/controllers/conference_registrations_controller.rb' + +# Offense count: 5 +# Cop supports --auto-correct. +Layout/MultilineBlockLayout: + Exclude: + - 'app/serializers/conference_serializer.rb' + +# Offense count: 6 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineHashBraceLayout: + Exclude: + - 'app/serializers/conference_serializer.rb' + - 'spec/models/event_spec.rb' + +# Offense count: 40 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodCallBraceLayout: + Enabled: false + +# Offense count: 55 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: aligned, indented, indented_relative_to_receiver +Layout/MultilineMethodCallIndentation: + Enabled: false + +# Offense count: 23 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: aligned, indented +Layout/MultilineOperationIndentation: + Exclude: + - 'app/controllers/admin/events_controller.rb' + - 'app/controllers/application_controller.rb' + - 'app/models/conference.rb' + - 'app/models/event.rb' + - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/SpaceAfterComma: + Exclude: + - 'lib/tasks/data_demo.rake' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: space, no_space +Layout/SpaceAroundEqualsInParameterDefault: + Exclude: + - 'app/helpers/format_helper.rb' + - 'app/models/event.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceAroundOperators: + Exclude: + - 'lib/tasks/data.rake' + +# Offense count: 416 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: space, no_space +Layout/SpaceBeforeBlockBraces: + Enabled: false + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/SpaceBeforeComma: + Exclude: + - 'lib/tasks/data_demo.rake' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceBeforeFirstArg: + Exclude: + - 'spec/controllers/admin/roles_controller_spec.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/SpaceBeforeSemicolon: + Exclude: + - 'Guardfile' + +# Offense count: 51 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Exclude: + - 'app/controllers/admin/comments_controller.rb' + - 'app/controllers/admin/events_controller.rb' + - 'app/controllers/admin/questions_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/models/program.rb' + - 'app/models/ticket.rb' + - 'app/models/user.rb' + - 'lib/tasks/events_registrations.rake' + - 'spec/controllers/admin/event_schedules_controller_spec.rb' + - 'spec/controllers/admin/schedules_controller_spec.rb' + - 'spec/features/splashpage_spec.rb' + - 'spec/models/ability_spec.rb' + - 'spec/models/user_spec.rb' + +# Offense count: 19 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideHashLiteralBraces: + Exclude: + - 'app/controllers/admin/conferences_controller.rb' + - 'app/controllers/api/v1/speakers_controller.rb' + - 'app/models/conference.rb' + - 'app/models/event_type.rb' + - 'app/models/user.rb' + - 'spec/models/event_spec.rb' + - 'spec/models/payment_spec.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Layout/SpaceInsidePercentLiteralDelimiters: + Exclude: + - 'Gemfile' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: final_newline, final_blank_line +Layout/TrailingBlankLines: + Exclude: + - 'lib/tasks/event_attatchments.rake' + - 'lib/tasks/roles.rake' + +# Offense count: 13 +Lint/AmbiguousBlockAssociation: + Exclude: + - 'spec/controllers/admin/conferences_controller_spec.rb' + - 'spec/controllers/admin/event_schedules_controller_spec.rb' + - 'spec/controllers/admin/registration_periods_controller_spec.rb' + - 'spec/controllers/admin/users_controller_spec.rb' + - 'spec/controllers/proposals_controller_spec.rb' + - 'spec/controllers/schedules_controller_spec.rb' + - 'spec/models/user_spec.rb' # Offense count: 2 Lint/DuplicatedKey: @@ -50,6 +292,12 @@ Lint/IneffectiveAccessModifier: - 'app/models/commercial.rb' - 'app/models/conference.rb' +# Offense count: 2 +Lint/ScriptPermission: + Exclude: + - 'Guardfile' + - 'Rakefile' + # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. @@ -57,197 +305,48 @@ Lint/UnusedBlockArgument: Exclude: - 'lib/tasks/user.rake' -# Offense count: 108 +# Offense count: 114 Metrics/AbcSize: - Max: 75 + Max: 86 -# Offense count: 202 +# Offense count: 233 # Configuration parameters: CountComments, ExcludedMethods. Metrics/BlockLength: - Max: 487 + Max: 471 -# Offense count: 21 +# Offense count: 23 Metrics/CyclomaticComplexity: Max: 12 + Exclude: + - 'app/models/track.rb' -# Offense count: 1991 +# Offense count: 2353 # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Metrics/LineLength: Max: 619 -# Offense count: 115 +# Offense count: 120 # Configuration parameters: CountComments. Metrics/MethodLength: Max: 56 - -# Offense count: 2 -# Configuration parameters: CountComments. -Metrics/ModuleLength: - Max: 472 Exclude: - - 'app/helpers/application_helper.rb' - - -# Offense count: 14 -Metrics/PerceivedComplexity: - Max: 15 - -# Offense count: 11 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, Include. -# SupportedStyles: action, filter -# Include: app/controllers/**/*.rb -Rails/ActionFilter: - Exclude: - - 'app/controllers/admin/base_controller.rb' - - 'app/controllers/admin/registrations_controller.rb' - - 'app/controllers/application_controller.rb' - - 'app/controllers/conference_registrations_controller.rb' - - 'app/controllers/subscriptions_controller.rb' - - 'app/controllers/ticket_purchases_controller.rb' - - 'app/controllers/tickets_controller.rb' - - 'app/controllers/users/omniauth_callbacks_controller.rb' - -# Offense count: 7 -# Cop supports --auto-correct. -# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent. -Rails/Blank: - Exclude: - - 'app/models/program.rb' - - 'app/models/user.rb' - - 'spec/factories/event_schedule.rb' - -# Offense count: 139 -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: strict, flexible -Rails/Date: - Enabled: false + - 'app/models/admin_ability.rb' # Offense count: 3 -# Cop supports --auto-correct. -# Configuration parameters: Whitelist. -# Whitelist: find_by_sql -Rails/DynamicFindBy: - Exclude: - - 'app/controllers/admin/events_controller.rb' - - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' - -# Offense count: 4 -Rails/FilePath: - Exclude: - - 'spec/features/lodgings_spec.rb' - - 'spec/features/sponsor_spec.rb' - - 'spec/spec_helper.rb' - -# Offense count: 6 -# Cop supports --auto-correct. -# Configuration parameters: Include. -# Include: app/models/**/*.rb -Rails/FindBy: - Exclude: - - 'app/models/conference.rb' - - 'app/models/event.rb' - - 'app/models/openid.rb' - - 'app/models/ticket_purchase.rb' - - 'app/models/user.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: Include. -# Include: app/models/**/*.rb -Rails/FindEach: - Exclude: - - 'app/models/conference.rb' - -# Offense count: 7 -# Configuration parameters: Include. -# Include: app/models/**/*.rb -Rails/HasAndBelongsToMany: - Exclude: - - 'app/models/conference.rb' - - 'app/models/qanswer.rb' - - 'app/models/question.rb' - - 'app/models/registration.rb' - - 'app/models/vchoice.rb' - -# Offense count: 170 -# Cop supports --auto-correct. -# Configuration parameters: Include. -# Include: spec/**/*, test/**/* -Rails/HttpPositionalArguments: - Enabled: false - -# Offense count: 2 -Rails/OutputSafety: +# Configuration parameters: CountComments. +Metrics/ModuleLength: + Max: 159 Exclude: - 'app/helpers/format_helper.rb' - - 'app/models/commercial.rb' -# Offense count: 10 -# Cop supports --auto-correct. -Rails/PluralizationGrammar: +# Offense count: 15 +Metrics/PerceivedComplexity: + Max: 16 Exclude: - - 'spec/models/conference_spec.rb' + - 'app/models/track.rb' -# Offense count: 22 -# Cop supports --auto-correct. -# Configuration parameters: NotNilAndNotEmpty, NotBlank, UnlessBlank. -Rails/Present: - Exclude: - - 'app/helpers/users_helper.rb' - - 'app/models/campaign.rb' - - 'app/models/cfp.rb' - - 'app/models/email_settings.rb' - - 'app/models/event.rb' - - 'app/models/program.rb' - - 'app/models/venue.rb' - -# Offense count: 52 -# Configuration parameters: Include. -# Include: db/migrate/*.rb -Rails/ReversibleMigration: - Exclude: - - 'db/migrate/20140530082708_remove_color_defaults.rb' - - 'db/migrate/20140605125153_update_event_states.rb' - - 'db/migrate/20140610173021_change_person_id_to_user_id_in_registrations.rb' - - 'db/migrate/20140611123926_change_person_id_to_user_id_in_votes.rb' - - 'db/migrate/20140623150541_drop_person_and_event_person_tables.rb' - - 'db/migrate/20140731165107_move_conference_contact_details_to_contact.rb' - - 'db/migrate/20140801164901_move_conference_media_to_commercial.rb' - - 'db/migrate/20140801170430_move_event_media_to_commercial.rb' - - 'db/migrate/20140820093735_migrating_supporter_registrations_to_ticket_users.rb' - - 'db/migrate/20140821103643_split_ticket_price_in_price_and_currency.rb' - - 'db/migrate/20140825093132_move_splashpage_attributes_from_conference_to_splashpage.rb' - - 'db/migrate/20140930092923_move_sponsor_email_to_contact.rb' - - 'db/migrate/20141117222919_drop_splash_descriptions_and_photo.rb' - - 'db/migrate/20141130182139_drop_table_event_attachments.rb' - -# Offense count: 5 -# Configuration parameters: Blacklist. -# Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters -Rails/SkipsModelValidations: - Exclude: - - 'app/controllers/payments_controller.rb' - - 'app/models/revision_observer.rb' - - 'db/migrate/20140730104658_migrate_roles_for_cancancan.rb' - - 'lib/tasks/user.rake' - -# Offense count: 46 -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: strict, flexible -Rails/TimeZone: - Exclude: - - 'app/models/comment.rb' - - 'app/models/conference.rb' - - 'lib/tasks/dump_db.rake' - - 'spec/controllers/admin/comments_controller_spec.rb' - - 'spec/controllers/admin/programs_controller_spec.rb' - - 'spec/factories/users.rb' - - 'spec/models/campaign_spec.rb' - - 'spec/models/conference_spec.rb' - -# Offense count: 18 +# Offense count: 20 Style/AccessorMethodName: Exclude: - 'app/controllers/admin/events_controller.rb' @@ -259,33 +358,17 @@ Style/AccessorMethodName: # Offense count: 1 # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: with_first_parameter, with_fixed_indentation -Style/AlignParameters: - Exclude: - - 'Vagrantfile' - -# Offense count: 2 -# Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: is_a?, kind_of? Style/ClassCheck: Exclude: - 'app/models/email_settings.rb' - - 'app/models/revision_observer.rb' # Offense count: 1 Style/ClassVars: Exclude: - 'spec/support/kneet_connections.rb' -# Offense count: 9 -# Cop supports --auto-correct. -Style/ClosingParenthesisIndentation: - Exclude: - - 'app/controllers/conference_registrations_controller.rb' - - 'spec/support/omniauth_macros.rb' - # Offense count: 2 # Cop supports --auto-correct. Style/ColonMethodCall: @@ -293,50 +376,20 @@ Style/ColonMethodCall: - 'app/models/commercial.rb' - 'app/models/contact.rb' -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: Keywords. -# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW -Style/CommentAnnotation: - Exclude: - - 'app/models/event_user.rb' - -# Offense count: 14 -# Cop supports --auto-correct. -Style/CommentIndentation: - Exclude: - - 'app/controllers/admin/comments_controller.rb' - - 'app/controllers/admin/difficulty_levels_controller.rb' - - 'app/models/conference.rb' - - 'app/models/program.rb' - - 'app/models/track.rb' - - 'spec/features/volunteers_spec.rb' - # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly, IncludeTernaryExpressions. # SupportedStyles: assign_to_condition, assign_inside_condition Style/ConditionalAssignment: Exclude: - - 'app/controllers/admin/volunteers_controller.rb' - - 'app/controllers/conference_registrations_controller.rb' - 'app/helpers/format_helper.rb' - - 'app/models/conference.rb' - - 'app/models/ticket_purchase.rb' - - 'app/models/user.rb' - 'db/migrate/20140610165551_migrate_data_person_to_user.rb' - 'db/migrate/20140820124117_undo_wrong_migration20140801080705_add_users_to_events.rb' -# Offense count: 436 +# Offense count: 464 Style/Documentation: Enabled: false -# Offense count: 1 -# Cop supports --auto-correct. -Style/ElseAlignment: - Exclude: - - 'app/helpers/format_helper.rb' - # Offense count: 2 # Cop supports --auto-correct. Style/EmptyCaseCondition: @@ -344,25 +397,6 @@ Style/EmptyCaseCondition: - 'app/helpers/format_helper.rb' - 'app/helpers/versions_helper.rb' -# Offense count: 1 -# Cop supports --auto-correct. -Style/EmptyLineAfterMagicComment: - Exclude: - - 'spec/models/conference_spec.rb' - -# Offense count: 109 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: empty_lines, no_empty_lines -Style/EmptyLinesAroundBlockBody: - Enabled: false - -# Offense count: 1 -# Cop supports --auto-correct. -Style/EmptyLinesAroundExceptionHandlingKeywords: - Exclude: - - 'app/models/payment.rb' - # Offense count: 1 # Cop supports --auto-correct. Style/EmptyLiteral: @@ -384,19 +418,6 @@ Style/EmptyMethod: - 'db/migrate/20130206192339_rename_attending_social_events_with_partner.rb' - 'db/migrate/20130216122155_set_registration_defaults_to_false.rb' -# Offense count: 9 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. -Style/ExtraSpacing: - Exclude: - - 'Guardfile' - - 'app/controllers/application_controller.rb' - - 'config.ru' - - 'db/migrate/20140623101032_create_ahoy_events.rb' - - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' - - 'db/migrate/20140719160903_create_delayed_jobs.rb' - - 'spec/models/conference_spec.rb' - # Offense count: 2 # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms. # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS @@ -405,14 +426,7 @@ Style/FileName: - 'Gemfile' - 'Vagrantfile' -# Offense count: 42 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: consistent, special_for_inner_method_call, special_for_inner_method_call_in_parentheses -Style/FirstParameterIndentation: - Enabled: false - -# Offense count: 23 +# Offense count: 24 # Configuration parameters: MinBodyLength. Style/GuardClause: Enabled: false @@ -431,6 +445,7 @@ Style/HashSyntax: # Configuration parameters: MaxLineLength. Style/IfUnlessModifier: Exclude: + - 'app/controllers/admin/booths_controller.rb' - 'app/controllers/admin/events_controller.rb' - 'app/controllers/api/v1/events_controller.rb' - 'app/controllers/conference_registrations_controller.rb' @@ -438,6 +453,7 @@ Style/IfUnlessModifier: - 'app/helpers/application_helper.rb' - 'app/models/commercial.rb' - 'app/models/conference.rb' + - 'app/models/email_settings.rb' - 'app/models/ticket_purchase.rb' - 'app/models/user.rb' - 'db/migrate/20151031092713_change_conference_id_to_venue_id_in_rooms.rb' @@ -445,59 +461,6 @@ Style/IfUnlessModifier: - 'spec/controllers/admin/conferences_controller_spec.rb' - 'spec/support/flash.rb' -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_brackets -Style/IndentArray: - Exclude: - - 'app/models/conference.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: IndentationWidth. -Style/IndentAssignment: - Exclude: - - 'app/helpers/format_helper.rb' - - 'app/models/conference.rb' - -# Offense count: 3 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_braces -Style/IndentHash: - Exclude: - - 'app/models/user.rb' - - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' - -# Offense count: 3 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: normal, rails -Style/IndentationConsistency: - Exclude: - - 'app/controllers/users_controller.rb' - - 'app/models/event.rb' - - 'spec/controllers/subscriptions_controller_spec.rb' - -# Offense count: 6 -# Cop supports --auto-correct. -# Configuration parameters: Width, IgnoredPatterns. -Style/IndentationWidth: - Exclude: - - 'app/helpers/format_helper.rb' - - 'app/serializers/conference_serializer.rb' - - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' - - 'lib/tasks/demo_data_for_development.rake' - - 'spec/models/ability_spec.rb' - -# Offense count: 4 -# Cop supports --auto-correct. -Style/LeadingCommentSpace: - Exclude: - - 'Guardfile' - - 'app/models/comment.rb' - # Offense count: 8 # Cop supports --auto-correct. Style/LineEndConcatenation: @@ -516,29 +479,6 @@ Style/MethodDefParentheses: - 'app/models/user.rb' - 'lib/tasks/demo_data_for_development.rake' -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: symmetrical, new_line, same_line -Style/MultilineArrayBraceLayout: - Exclude: - - 'app/controllers/conference_registrations_controller.rb' - -# Offense count: 5 -# Cop supports --auto-correct. -Style/MultilineBlockLayout: - Exclude: - - 'app/serializers/conference_serializer.rb' - -# Offense count: 6 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: symmetrical, new_line, same_line -Style/MultilineHashBraceLayout: - Exclude: - - 'app/serializers/conference_serializer.rb' - - 'spec/models/event_spec.rb' - # Offense count: 7 # Cop supports --auto-correct. Style/MultilineIfModifier: @@ -549,33 +489,6 @@ Style/MultilineIfModifier: - 'app/models/event.rb' - 'app/models/registration_period.rb' -# Offense count: 40 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: symmetrical, new_line, same_line -Style/MultilineMethodCallBraceLayout: - Enabled: false - -# Offense count: 55 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: aligned, indented, indented_relative_to_receiver -Style/MultilineMethodCallIndentation: - Enabled: false - -# Offense count: 27 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: aligned, indented -Style/MultilineOperationIndentation: - Exclude: - - 'app/controllers/admin/events_controller.rb' - - 'app/controllers/application_controller.rb' - - 'app/models/ability.rb' - - 'app/models/conference.rb' - - 'app/models/event.rb' - - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' - # Offense count: 2 # Cop supports --auto-correct. Style/MutableConstant: @@ -644,14 +557,14 @@ Style/ParenthesesAroundCondition: - 'app/controllers/application_controller.rb' - 'app/helpers/format_helper.rb' -# Offense count: 17 +# Offense count: 14 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Exclude: - 'Gemfile' - 'app/controllers/admin/users_controller.rb' - - 'app/models/ability.rb' + - 'app/models/cfp.rb' - 'app/models/comment.rb' - 'app/models/commercial.rb' - 'app/models/conference.rb' @@ -659,8 +572,8 @@ Style/PercentLiteralDelimiters: - 'app/models/registration.rb' - 'app/models/subscription.rb' - 'app/uploaders/picture_uploader.rb' - - 'spec/models/ability_spec.rb' - 'spec/models/program_spec.rb' + - 'app/models/track.rb' # Offense count: 2 # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist. @@ -688,12 +601,6 @@ Style/PreferredHashMethods: Style/RaiseArgs: EnforcedStyle: compact -# Offense count: 1 -# Cop supports --auto-correct. -Style/RedundantBegin: - Exclude: - - 'app/models/revision_observer.rb' - # Offense count: 1 # Cop supports --auto-correct. Style/RedundantParentheses: @@ -718,14 +625,6 @@ Style/RegexpLiteral: Exclude: - 'Guardfile' -# Offense count: 3 -# Cop supports --auto-correct. -Style/SelfAssignment: - Exclude: - - 'app/models/event.rb' - - 'db/migrate/20141104131625_generate_username.rb' - - 'spec/support/save_feature_failures.rb' - # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. @@ -742,103 +641,6 @@ Style/SingleLineMethods: Exclude: - 'Guardfile' -# Offense count: 1 -# Cop supports --auto-correct. -Style/SpaceAfterComma: - Exclude: - - 'lib/tasks/data_demo.rake' - -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: space, no_space -Style/SpaceAroundEqualsInParameterDefault: - Exclude: - - 'app/helpers/format_helper.rb' - - 'app/models/event.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment. -Style/SpaceAroundOperators: - Exclude: - - 'lib/tasks/data.rake' - -# Offense count: 319 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: space, no_space -Style/SpaceBeforeBlockBraces: - Enabled: false - -# Offense count: 1 -# Cop supports --auto-correct. -Style/SpaceBeforeComma: - Exclude: - - 'lib/tasks/data_demo.rake' - -# Offense count: 1 -# Cop supports --auto-correct. -Style/SpaceBeforeSemicolon: - Exclude: - - 'Guardfile' - -# Offense count: 2 -# Cop supports --auto-correct. -Style/SpaceInsideArrayPercentLiteral: - Exclude: - - 'spec/models/ability_spec.rb' - -# Offense count: 62 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. -# SupportedStyles: space, no_space -# SupportedStylesForEmptyBraces: space, no_space -Style/SpaceInsideBlockBraces: - Exclude: - - 'app/controllers/admin/comments_controller.rb' - - 'app/controllers/admin/events_controller.rb' - - 'app/controllers/admin/questions_controller.rb' - - 'app/helpers/application_helper.rb' - - 'app/models/program.rb' - - 'app/models/ticket.rb' - - 'app/models/user.rb' - - 'lib/tasks/events_registrations.rake' - - 'spec/controllers/admin/event_schedules_controller_spec.rb' - - 'spec/controllers/admin/schedules_controller_spec.rb' - - 'spec/features/splashpage_spec.rb' - - 'spec/models/ability_spec.rb' - - 'spec/models/user_spec.rb' - -# Offense count: 5 -# Cop supports --auto-correct. -Style/SpaceInsideBrackets: - Exclude: - - 'app/models/conference.rb' - - 'app/models/user.rb' - -# Offense count: 25 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces. -# SupportedStyles: space, no_space, compact -# SupportedStylesForEmptyBraces: space, no_space -Style/SpaceInsideHashLiteralBraces: - Exclude: - - 'app/controllers/admin/conferences_controller.rb' - - 'app/controllers/api/v1/speakers_controller.rb' - - 'app/models/ability.rb' - - 'app/models/conference.rb' - - 'app/models/event_type.rb' - - 'app/models/user.rb' - - 'spec/models/event_spec.rb' - - 'spec/models/payment_spec.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -Style/SpaceInsidePercentLiteralDelimiters: - Exclude: - - 'Gemfile' - # Offense count: 16 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline. @@ -860,14 +662,14 @@ Style/StringLiteralsInInterpolation: Exclude: - 'lib/tasks/dump_db.rake' -# Offense count: 60 +# Offense count: 73 # Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. +# Configuration parameters: EnforcedStyle, MinSize, SupportedStyles. # SupportedStyles: percent, brackets Style/SymbolArray: Enabled: false -# Offense count: 10 +# Offense count: 11 # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. # IgnoredMethods: respond_to, define_method @@ -877,6 +679,7 @@ Style/SymbolProc: - 'app/controllers/admin/questions_controller.rb' - 'app/helpers/application_helper.rb' - 'app/models/ability.rb' + - 'app/models/admin_ability.rb' - 'db/migrate/20140730104658_migrate_roles_for_cancancan.rb' - 'spec/controllers/admin/conferences_controller_spec.rb' - 'spec/support/flash.rb' @@ -889,15 +692,6 @@ Style/TernaryParentheses: Exclude: - 'app/helpers/format_helper.rb' -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: final_newline, final_blank_line -Style/TrailingBlankLines: - Exclude: - - 'lib/tasks/event_attatchments.rake' - - 'lib/tasks/roles.rake' - # Offense count: 23 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline. @@ -908,12 +702,6 @@ Style/TrailingCommaInLiteral: - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb' - 'spec/models/conference_spec.rb' -# Offense count: 1 -# Cop supports --auto-correct. -Style/TrailingWhitespace: - Exclude: - - 'Gemfile' - # Offense count: 3 # Cop supports --auto-correct. Style/UnneededInterpolation: @@ -927,11 +715,3 @@ Style/UnneededInterpolation: Style/VariableNumber: Exclude: - 'spec/models/ticket_purchase_spec.rb' - -# Offense count: 6 -# Cop supports --auto-correct. -# Configuration parameters: SupportedStyles, WordRegex. -# SupportedStyles: percent, brackets -Style/WordArray: - EnforcedStyle: percent - MinSize: 3 diff --git a/.travis.yml b/.travis.yml index 0be912e1..6650fbf9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,12 @@ dist: trusty language: ruby cache: bundler rvm: - - 2.2.3 + - 2.4.0 before_install: - "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc" - "echo `phantomjs -v`" addons: - mariadb: '10.1' + mariadb: '10.2' notifications: email: on_success: change @@ -24,6 +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 haml-lint app/views' - - 'bundle exec rspec --color --format documentation' + - "./travis_script.sh $TEST_SUITE" +env: + - TEST_SUITE=rspec + - TEST_SUITE=linters +matrix: + fast_finish: true diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..7e9792fa --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c0688999..5193725d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,59 +71,6 @@ You can access the app [localhost:3000](http://localhost:3000). Whatever you cha * If you are already a contributor and you get a positive review, you can merge your pull-request yourself * If you are not already a contributor please request a merge via the pull-request comments -### Getting Started - -* When you get involved with OSEM for the first time, you can choose issues labeled as [Junior]( https://github.com/openSUSE/osem/issues?q=is%3Aissue+is%3Aopen+label%3AJunior) -* Leave a comment on the issue that you want to work on it - * We expect you to work on it and show progress by either opening a PR or commenting on the issue - * If you change your mind, and do not want to work on the issue any more, please be fair to others and leave a comment to let us know - * Do **not** work on issues that are assigned to others. If you are uncertain, ask and wait for a **contributor** to reply -* Avoid working on issues that have no label - * If you have opened a new issue, please wait for a contributor to add relevant labels -* If an issue is a feature, we should first have a rough idea on how we want to implement it - * If there is already such a discussion on the issue, you can go ahead and pick this up - * If not, please first leave a comment on how you want to implement it and wait for contributors' feedback - -### Commits -* Commit title should be short and descriptive - * title (or summary line) is the first line of the commit message - * that says what the commit is doing - * in no more than 50 characters - * starting with a word like 'Fix' or 'Add' or 'Change' - * **without** a period (.) at the end - * followed by a blank line -* Commit messages are - * up to 72 characters - * with break lines -* Reference the issue(s) the commit closes - * https://help.github.com/articles/closing-issues-via-commit-messages - * If you haven't done so since the beginning, you should reference the issue when you squash your commits - -### Pull Requests workflow -Please open a pull request (PR) only when you have finished coding, and your changes are ready to be reviewed for merging. - -* Title - * Include a comprehensive title about what this PR is doing - * Referencing the issue number on the PR title is not giving any information about what this PR is about - * The title should be short (50 characters maximum); you can add more information in the description -* Description - * Add a couple of lines about what is the problem you are trying to solve and how you have addressed it - * Add bullet points about the new things you are introducing, if applicable - * Reference the issue(s) you are solving - * Add a screenshot of your change, if you are working on something that changes how the app looks like -* Automated checks - * We automatically run the [test suite](https://github.com/openSUSE/osem/blob/master/CONTRIBUTING.md#test-suite) and security checks on every PR - * Check back later to see if all checks were successful, if not, address them or leave a comment to ask for help -* Pushing new changes to your PR - * Always add **new** commits; this tremendously helps reviewers - * Do not squash commits, unless explicitly requested by the reviewer -* Take care of your PR - * Make sure you check the status of your PR regularly - * Address your reviews, make the necessary changes, ask if something is not clear to you - * Rebase against newest changes, when needed; we cannot properly review PRs that are not rebased - -Reviewing your PR might take some time, as we are all volunteers. Please be responsive and respectful. - ### Coding Style We are using [rubocop](https://github.com/bbatsov/rubocop) as a style checker. It is checking code style each time the test suite runs. You can run it locally with @@ -141,16 +88,6 @@ We are using [rspec](http://rspec.info/)+[capybara](http://jnicklas.github.io/ca vagrant exec bundle exec rspec ``` -### Review App of your PR - -OSEM uses [Review Apps](https://devcenter.heroku.com/articles/github-integration-review-apps) on Heroku. - -* The review app can be manually created by a maintainer, and when that happens you will see a relevant message in the PR - -* Please help reviewers by adding the necessary data relevant to your PR, -eg. if your PR is doing something related to conference registrations, go to the review app and make sure there is a conference with registrations. - - ### Email Notifications **Note**: We use [letter_opener](https://github.com/ryanb/letter_opener) in development environment. You can check out your mails by visiting [localhost:3000/letter_opener](http://localhost:3000/letter_opener). @@ -181,44 +118,6 @@ 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! - -1. **Bug** - * A bug in the application, something is wrong and needs to be fixed! - * Ideally the issue includes details on how to reproduce the bug - * Reproduce the bug in master branch, and send a PR that solves it -2. **Design** - * Related to the looks and/or usability of the application; needs attention from someone who understands front-end and UX - * If you are good with graphics and design, give it a shot! -3. **Documentation** - * Related to the documentation of our application, eg our INSTALL.md file or a wiki page with instructions on how to use the app, or part of it. - * If you are working on a documentation issue, make sure you are covering all cases. -4. **Epic** - * We may, or may not, solve this, thus it is epic. It's bigger than a feature request, because it fundamentally changes or affects the app, or a significant part of it. - * Do **not** work on this without prior discussion with the maintainers, it's called epic for a reason! -5. **Feature** - * This is a new feature for something new in the app! - * If an issue is labeled *Feature*, don't work on the issue, unless the maintainers have decided on how to proceed - * Ideally, leave a comment with your proposed solution in the issue and wait for feedback -6. **Grooming** - * This is working, but could look better, thus needs some attention and grooming. -7. **Hacktoberfest** - * This is for the issues included in the coding event of Hacktoberfest. You can ignore it, when the event is not on -8. **in progress** -9. **Junior** - * For new comers! RoR beginners or people unfamiliar with the application. Where you must start if you are interested in a mentoring program we participate in. -10. **need feedback** - * Maintainers' attention is needed to decide if this is something we want in the app, and/or how it should be implemented -11. **Operation** -12. **ready** -13. **Refactorization** - * 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 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..dab432ca --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +FROM ruby:2.3 + +MAINTAINER TheAssassin + +# 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"] diff --git a/Gemfile b/Gemfile index 497c56b1..22dc5f6e 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,7 @@ if Gem::Version.new(Bundler::VERSION) < Gem::Version.new('1.8.4') end # as web framework -gem 'rails', '~> 4.2' +gem 'rails', '~> 4.2.8' # enables serving assets in production and setting your logger to standard out # both of which are required to run an application on a twelve-factor provider @@ -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' @@ -58,7 +57,7 @@ gem 'unobtrusive_flash', '>=3' gem 'transitions', :require => %w( transitions active_record/transitions ) # for comments -gem 'awesome_nested_set', '~> 3.0.0.rc.5' +gem 'awesome_nested_set', '~> 3.1.3' gem 'acts_as_commentable_with_threading' # as templating language @@ -84,6 +83,8 @@ gem 'jquery-ui-rails', '~> 4.2.1' # for languages validation gem 'iso-639' +gem 'thor', '0.19.1' + # frontend javascripts source 'https://rails-assets.org' do # for placeholder images @@ -126,8 +127,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 +182,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 @@ -196,14 +200,19 @@ gem 'sprockets-rails' # for multiple speakers select on proposal/event forms gem 'selectize-rails' +# Nokogiri < 1.8.1 is subject to: +# CVE-2017-0663, CVE-2017-7375, CVE-2017-7376, CVE-2017-9047, CVE-2017-9048, +# CVE-2017-9049, CVE-2017-9050 +gem 'nokogiri', '>= 1.8.1' + # 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 'guard-rspec' gem 'spring-commands-rspec' gem 'haml_lint', '~> 0.24.0' # for static code analisys - gem 'rubocop', '~> 0.48.1', require: false + gem 'rubocop', '~> 0.49.0', require: false # as database gem 'sqlite3' # to open mails @@ -218,7 +227,7 @@ end group :test do # as test framework - gem 'rspec-rails' + gem 'rspec-rails', '~> 3.5', '>= 3.5.2' gem 'database_cleaner' gem 'capybara' gem 'poltergeist' diff --git a/Gemfile.lock b/Gemfile.lock index 4f002b4a..dc730e82 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,41 +1,50 @@ +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/ specs: - actionmailer (4.2.7.1) - actionpack (= 4.2.7.1) - actionview (= 4.2.7.1) - activejob (= 4.2.7.1) + actionmailer (4.2.9) + actionpack (= 4.2.9) + actionview (= 4.2.9) + activejob (= 4.2.9) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.7.1) - actionview (= 4.2.7.1) - activesupport (= 4.2.7.1) + actionpack (4.2.9) + actionview (= 4.2.9) + activesupport (= 4.2.9) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.7.1) - activesupport (= 4.2.7.1) + actionview (4.2.9) + activesupport (= 4.2.9) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) - rails-html-sanitizer (~> 1.0, >= 1.0.2) + rails-html-sanitizer (~> 1.0, >= 1.0.3) active_model_serializers (0.9.4) activemodel (>= 3.2) - activejob (4.2.7.1) - activesupport (= 4.2.7.1) + activejob (4.2.9) + activesupport (= 4.2.9) globalid (>= 0.3.0) - activemodel (4.2.7.1) - activesupport (= 4.2.7.1) + activemodel (4.2.9) + activesupport (= 4.2.9) builder (~> 3.1) - activerecord (4.2.7.1) - activemodel (= 4.2.7.1) - activesupport (= 4.2.7.1) + activerecord (4.2.9) + activemodel (= 4.2.9) + activesupport (= 4.2.9) arel (~> 6.0) - activesupport (4.2.7.1) + activesupport (4.2.9) i18n (~> 0.7) - json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) @@ -48,7 +57,8 @@ GEM awesome_nested_set (>= 2.0) acts_as_list (0.4.0) activerecord (>= 3.0) - addressable (2.3.6) + addressable (2.5.1) + public_suffix (~> 2.0, >= 2.0.2) ahoy_matey (1.0.0) addressable browser (>= 0.4.0) @@ -57,18 +67,13 @@ GEM request_store user_agent_parser uuidtools - arel (6.0.3) + arel (6.0.4) ast (2.3.0) - autoprefixer-rails (5.1.9) + autoprefixer-rails (7.1.1) execjs - json - awesome_nested_set (3.0.0.rc.5) - activerecord (>= 4.0.0, < 5) + awesome_nested_set (3.1.3) + activerecord (>= 4.0.0, < 5.2) 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) @@ -82,10 +87,8 @@ GEM bootstrap3-datetimepicker-rails (3.0.3) momentjs-rails (>= 2.8.1) browser (0.6.0) - builder (3.2.2) - byebug (3.1.2) - columnize (~> 0.8) - debugger-linecache (~> 1.2) + builder (3.2.3) + byebug (9.0.6) cancancan (1.13.1) capybara (2.6.2) addressable @@ -103,28 +106,24 @@ GEM activesupport (>= 3.2.0) carrierwave fastimage - celluloid (0.15.2) - timers (~> 1.1.0) - celluloid-io (0.15.0) - celluloid (>= 0.15.0) - nio4r (>= 0.5.0) 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 rest-client cocoon (1.2.6) - coderay (1.1.0) - coffee-rails (4.1.1) + coderay (1.1.1) + coffee-rails (4.2.2) coffee-script (>= 2.2.0) - railties (>= 4.0.0, < 5.1.x) + railties (>= 4.0.0) coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.10.0) - columnize (0.8.9) + coffee-script-source (1.12.2) + concurrent-ruby (1.0.5) countable-rails (0.0.1) railties (>= 3.1) countries (1.2.5) @@ -139,18 +138,17 @@ GEM term-ansicolor (~> 1.3) thor (~> 0.19.1) tins (~> 1.6.0) - crack (0.4.2) + crack (0.4.3) safe_yaml (~> 1.0.0) currencies (0.4.2) daemons (1.1.9) 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) - activerecord (>= 3.0, < 5) + delayed_job (4.1.3) + activesupport (>= 3.0, < 5.2) + delayed_job_active_record (4.1.2) + activerecord (>= 3.0, < 5.2) delayed_job (>= 3.0, < 5) devise (4.2.0) bcrypt (~> 3.0) @@ -160,14 +158,14 @@ GEM warden (~> 1.2.3) devise_ichain_authenticatable (0.3.1) devise (>= 2.2) - diff-lcs (1.2.5) + diff-lcs (1.3) docile (1.1.5) domain_name (0.5.20160310) unf (>= 0.0.5, < 1.0.0) - dotenv (2.1.1) - dotenv-rails (2.1.1) - dotenv (= 2.1.1) - railties (>= 4.0, < 5.1) + dotenv (2.2.1) + dotenv-rails (2.2.1) + dotenv (= 2.2.1) + railties (>= 3.2, < 5.2) erubis (2.7.0) execjs (2.6.0) factory_girl (4.5.0) @@ -181,27 +179,32 @@ GEM multipart-post (>= 1.2, < 3) fastimage (2.0.0) addressable (~> 2) - ffi (1.9.3) - font-awesome-rails (4.1.0.0) - railties (>= 3.2, < 5.0) - formatador (0.2.4) + ffi (1.9.18) + font-awesome-rails (4.7.0.2) + railties (>= 3.2, < 5.2) + formatador (0.2.5) formtastic (3.1.3) actionpack (>= 3.2.13) formtastic-bootstrap (3.1.1) formtastic (>= 3.0) geocoder (1.2.2) - globalid (0.3.6) - activesupport (>= 4.1.0) + globalid (0.4.0) + activesupport (>= 4.2.0) gravtastic (3.2.6) - guard (2.6.0) + guard (2.14.1) formatador (>= 0.2.4) - listen (~> 2.7) + listen (>= 2.7, < 4.0) lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) pry (>= 0.9.12) + shellany (~> 0.0) thor (>= 0.18.1) - guard-rspec (4.2.8) + guard-compat (1.2.1) + guard-rspec (4.7.3) guard (~> 2.1) - rspec (>= 2.14, < 4.0) + guard-compat (~> 1.1) + rspec (>= 2.99.0, < 4.0) haml (4.0.5) tilt haml-rails (0.5.3) @@ -215,8 +218,8 @@ GEM rake (>= 10, < 13) rubocop (>= 0.47.0) sysexits (~> 1.1) - hashie (2.1.1) - hike (1.2.3) + hashdiff (0.3.4) + hashie (3.5.5) hoptoad_notifier (2.4.11) activesupport builder @@ -225,14 +228,15 @@ GEM domain_name (~> 0.5) i18n (0.7.0) i18n_data (0.7.0) - inversion (0.12.3) - loggability (~> 0.4) + inversion (1.0.0) + loggability (~> 0.12) iso-639 (0.2.5) jquery-datatables-rails (2.2.3) jquery-rails sass-rails - jquery-rails (3.1.4) - railties (>= 3.0, < 5.0) + jquery-rails (4.3.1) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) thor (>= 0.14, < 2.0) jquery-ui-rails (4.2.1) railties (>= 3.2.16) @@ -249,25 +253,25 @@ GEM actionmailer (>= 3.2) letter_opener (~> 1.0) railties (>= 3.2) - listen (2.7.2) - celluloid (>= 0.15.2) - celluloid-io (>= 0.15.0) - rb-fsevent (>= 0.9.3) - rb-inotify (>= 0.9) - loggability (0.11.0) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) + loggability (0.14.0) loofah (2.0.3) nokogiri (>= 1.5.9) - lumberjack (1.0.5) - mail (2.6.3) - mime-types (>= 1.16, < 3) + lumberjack (1.0.12) + mail (2.6.6) + mime-types (>= 1.16, < 4) method_source (0.8.2) - mime-types (2.99.1) + mime-types (2.99.3) + 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.10.1) + mini_portile2 (2.3.0) + minitest (5.10.2) momentjs-rails (2.8.1) railties (>= 3.1) monetize (1.4.0) @@ -280,23 +284,26 @@ GEM monetize (~> 1.4.0) money (~> 6.7) railties (>= 3.0) - multi_json (1.11.2) + multi_json (1.12.1) multi_xml (0.5.5) multipart-post (2.0.0) - mysql2 (0.4.2) + mysql2 (0.4.9) + nenv (0.3.0) netrc (0.11.0) - nio4r (1.2.1) - nokogiri (1.7.1) - mini_portile2 (~> 2.1.0) - oauth2 (0.9.4) - faraday (>= 0.8, < 0.10) + nokogiri (1.8.1) + mini_portile2 (~> 2.3.0) + notiffany (0.1.1) + nenv (~> 0.1) + shellany (~> 0.0) + oauth2 (1.3.1) + faraday (>= 0.8, < 0.12) jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) - rack (~> 1.2) - omniauth (1.2.1) - hashie (>= 1.2, < 3) - rack (~> 1.0) + rack (>= 1.2, < 3) + omniauth (1.6.1) + hashie (>= 3.4.6, < 3.6.0) + rack (>= 1.6.2, < 3) omniauth-facebook (1.6.0) omniauth-oauth2 (~> 1.1) omniauth-github (1.1.2) @@ -305,20 +312,19 @@ GEM omniauth-google-oauth2 (0.2.4) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-oauth2 (1.1.2) - faraday (>= 0.8, < 0.10) - multi_json (~> 1.3) - oauth2 (~> 0.9.3) + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) omniauth (~> 1.2) omniauth-openid (1.0.1) omniauth (~> 1.0) 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) + parallel (1.11.2) parser (2.4.0.0) ast (~> 2.2) pdf-core (0.2.5) @@ -336,29 +342,33 @@ 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) - pry (0.9.12.6) - coderay (~> 1.0) - method_source (~> 0.8) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) slop (~> 3.4) - rack (1.6.4) + public_suffix (2.0.5) + rack (1.6.8) rack-openid (1.3.1) rack (>= 1.1.0) ruby-openid (>= 2.1.8) rack-test (0.6.3) rack (>= 1.0) - rails (4.2.7.1) - actionmailer (= 4.2.7.1) - actionpack (= 4.2.7.1) - actionview (= 4.2.7.1) - activejob (= 4.2.7.1) - activemodel (= 4.2.7.1) - activerecord (= 4.2.7.1) - activesupport (= 4.2.7.1) + rails (4.2.9) + actionmailer (= 4.2.9) + actionpack (= 4.2.9) + actionview (= 4.2.9) + activejob (= 4.2.9) + activemodel (= 4.2.9) + activerecord (= 4.2.9) + activesupport (= 4.2.9) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.7.1) + railties (= 4.2.9) sprockets-rails rails-assets-bootstrap (3.3.6) rails-assets-jquery (>= 1.9.1, < 3) @@ -388,65 +398,68 @@ GEM 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 rails_serve_static_assets (0.0.4) rails_stdout_logging (0.0.3) - railties (4.2.7.1) - actionpack (= 4.2.7.1) - activesupport (= 4.2.7.1) + railties (4.2.9) + actionpack (= 4.2.9) + activesupport (= 4.2.9) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rainbow (2.2.1) - rake (10.5.0) - rb-fsevent (0.9.4) - rb-inotify (0.9.4) - ffi (>= 0.5.0) - rdoc (4.1.1) - json (~> 1.4) - rdoc-generator-fivefish (0.1.0) - inversion (~> 0.12) - loggability (~> 0.6) - rdoc (~> 4.0) - yajl-ruby (~> 1.1) + rainbow (2.2.2) + rake + rake (12.0.0) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + rdoc (5.1.0) + rdoc-generator-fivefish (0.3.0) + inversion (~> 1.0) + loggability (~> 0.12) + rdoc (~> 5.0) + yajl-ruby (~> 1.3) redcarpet (3.2.3) referer-parser (0.2.1) request_store (1.1.0) - responders (2.1.1) - railties (>= 4.2.0, < 5.1) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) rest-client (1.8.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) rolify (5.1.0) - rspec (3.0.0) - rspec-core (~> 3.0.0) - rspec-expectations (~> 3.0.0) - rspec-mocks (~> 3.0.0) + rqrcode (0.10.1) + chunky_png (~> 1.0) + rspec (3.6.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) rspec-activemodel-mocks (1.0.1) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.0.2) - rspec-support (~> 3.0.0) - rspec-expectations (3.0.2) + rspec-core (3.6.0) + rspec-support (~> 3.6.0) + rspec-expectations (3.6.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.0.0) - rspec-mocks (3.0.2) - rspec-support (~> 3.0.0) - rspec-rails (3.0.0) + rspec-support (~> 3.6.0) + rspec-mocks (3.6.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.6.0) + rspec-rails (3.6.0) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.0.0) - rspec-expectations (~> 3.0.0) - rspec-mocks (~> 3.0.0) - rspec-support (~> 3.0.0) - rspec-support (3.0.2) - rubocop (0.48.1) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-support (~> 3.6.0) + rspec-support (3.6.0) + rubocop (0.49.1) + parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) @@ -455,16 +468,19 @@ GEM ruby-oembed (0.8.14) ruby-openid (2.5.0) ruby-progressbar (1.8.1) + ruby_dep (1.5.0) rubyzip (1.2.1) safe_yaml (1.0.4) sass (3.2.19) - sass-rails (4.0.4) - railties (>= 4.0.0, < 5.0) - sass (~> 3.2.2) - sprockets (~> 2.8, < 2.12) - sprockets-rails (~> 2.0) + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) selectize-rails (0.12.4) - shoulda-matchers (2.6.1) + shellany (0.0.1) + shoulda-matchers (2.8.0) activesupport (>= 3.0.0) simplecov (0.11.2) docile (~> 1.1.0) @@ -478,11 +494,9 @@ GEM spring (1.6.3) spring-commands-rspec (1.0.4) spring (>= 0.9.1) - sprockets (2.11.3) - hike (~> 1.2) - multi_json (~> 1.0) - rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) + sprockets (3.7.1) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) sprockets-rails (2.3.3) actionpack (>= 3.0) activesupport (>= 3.0) @@ -501,7 +515,6 @@ GEM thread_safe (0.3.6) tilt (1.4.1) timecop (0.7.1) - timers (1.1.0) tins (1.6.0) transitions (0.1.12) ttfunk (1.1.1) @@ -514,7 +527,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicode-display_width (1.2.1) + unicode-display_width (1.3.0) unicode_utils (1.4.0) unobtrusive_flash (3.1.0) railties @@ -527,9 +540,10 @@ GEM binding_of_caller (>= 0.7.2) railties (>= 4.0) sprockets-rails (>= 2.0, < 4.0) - webmock (1.20.4) + webmock (3.0.1) addressable (>= 2.3.6) crack (>= 0.3.2) + hashdiff websocket-driver (0.6.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) @@ -550,7 +564,8 @@ DEPENDENCIES acts_as_list ahoy_matey autoprefixer-rails - awesome_nested_set (~> 3.0.0.rc.5) + awesome_nested_set (~> 3.1.3) + axlsx! axlsx_rails bootstrap-sass (~> 3.3.4.1) bootstrap-switch-rails (~> 3.0.0) @@ -578,7 +593,7 @@ DEPENDENCIES formtastic (~> 3.1.1) formtastic-bootstrap gravtastic - guard-rspec (~> 4.2.8) + guard-rspec haml-rails haml_lint (~> 0.24.0) hoptoad_notifier (~> 2.3) @@ -594,6 +609,7 @@ DEPENDENCIES mini_magick money-rails mysql2 + nokogiri (>= 1.8.1) omniauth omniauth-facebook omniauth-github @@ -603,8 +619,9 @@ DEPENDENCIES phantomjs piwik_analytics (~> 1.0.1) poltergeist + prawn-qrcode (~> 0.2.2.1) prawn_rails - rails (~> 4.2) + rails (~> 4.2.8) rails-assets-bootstrap-markdown! rails-assets-date.format! rails-assets-holderjs! @@ -617,15 +634,15 @@ 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 (~> 0.48.1) + rspec-rails (~> 3.5, >= 3.5.2) + rubocop (~> 0.49.0) ruby-oembed sass-rails (>= 4.0.2) selectize-rails @@ -635,6 +652,7 @@ DEPENDENCIES sqlite3 stripe stripe-ruby-mock + thor (= 0.19.1) timecop transitions turbolinks @@ -645,4 +663,4 @@ DEPENDENCIES whenever BUNDLED WITH - 1.14.5 + 1.15.4 diff --git a/INSTALL.md b/INSTALL.md index 9cbfafcc..29dc5cec 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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 Deploy +### 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. diff --git a/TRANSLATION.md b/TRANSLATION.md index 5014159f..3923584c 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -1,5 +1,5 @@ # Translation -We are using [Transifex] (https://www.transifex.com/opensuse-community/osem/) to manage our translations. +We are using [Transifex](https://www.transifex.com/opensuse-community/osem/) to manage our translations. We are also using the _Live_ feature of Transifex. That means that all strings from OSEM instances are automatically collected and are available for translating. >Automated collection only works if you use the API key (See at the end of this file) @@ -7,7 +7,7 @@ We are also using the _Live_ feature of Transifex. That means that all strings f ## 1. Translate * Start translating: - 1. Navigate to the [project's page] (https://www.transifex.com/opensuse-community/osem) + 1. Navigate to the [project's page](https://www.transifex.com/opensuse-community/osem) 2. Click **translation** button 3. Select **language** 4. Select **resource** (events.opensuse.org) @@ -27,7 +27,7 @@ Eg. Assuming you have signed up yourself as a translator for OSEM, to translate * How to request to publish translated strings - After you make sure that you have properly reviewed the newly translated strings (they need to be marked as **reviewed** otherwise they won't go live), you can open a [new issue] (https://github.com/openSUSE/osem/issues/new) with the following information: + After you make sure that you have properly reviewed the newly translated strings (they need to be marked as **reviewed** otherwise they won't go live), you can open a [new issue](https://github.com/openSUSE/osem/issues/new) with the following information: Title: [Transifex] Publish translation for EN diff --git a/Vagrantfile b/Vagrantfile index e2faad9d..bf3622e4 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -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 diff --git a/app/assets/javascripts/osem-dashboard.js b/app/assets/javascripts/osem-dashboard.js index c8d5b853..83d2b468 100644 --- a/app/assets/javascripts/osem-dashboard.js +++ b/app/assets/javascripts/osem-dashboard.js @@ -55,7 +55,6 @@ $(function() { } function draw_line_chart(animation, $canvas){ - var options = get_animation({}, animation); var chart_data = create_dataset($canvas); var weeks = $canvas.parent().data('weeks'); var data = { @@ -63,10 +62,27 @@ $(function() { datasets : chart_data } + var options = get_animation(wholeNumberAxisFix(data), animation); var ctx = $canvas.get(0).getContext("2d"); new Chart(ctx).Line(data, options); } + function wholeNumberAxisFix(data){ + var maxValue = false; + for(datasetIndex = 0; datasetIndex < data.datasets.length; ++datasetIndex){ + var setMax = Math.max.apply(null, data.datasets[datasetIndex].data); + if (maxValue === false || setMax > maxValue) maxValue = setMax; + } + + var steps = maxValue; + var stepWidth = 1; + if (maxValue > 10) { + stepWidth = Math.floor(maxValue / 10); + steps = Math.ceil(maxValue / stepWidth); + } + return { scaleOverride: true, scaleSteps: steps, scaleStepWidth: stepWidth, scaleStartValue: 0 }; + } + function create_dataset($canvas){ var selected = getSelectedConferences($canvas); var chart_data = $canvas.parent().data('chart'); diff --git a/app/assets/javascripts/osem-datatables.js b/app/assets/javascripts/osem-datatables.js index f950159e..a6f85c5d 100644 --- a/app/assets/javascripts/osem-datatables.js +++ b/app/assets/javascripts/osem-datatables.js @@ -1,18 +1,14 @@ $(function () { - $(document).ready(function() { - $('.datatable').DataTable({ - // ajax: ..., - stateSave: true, - autoWidth: false, - pagingType: 'full_numbers', - "lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]] - }); + $('.datatable').DataTable({ + // ajax: ..., + stateSave: true, + autoWidth: false, + pagingType: 'full_numbers', + "lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]], + }); - $('#versionstable').DataTable({ - pagingType: 'full_numbers', - order: [[ 0, 'desc' ]] - }); + $('#versionstable').DataTable({ + pagingType: 'full_numbers', + order: [[ 0, 'desc' ]] }); }); - - diff --git a/app/assets/javascripts/osem-schedule.js b/app/assets/javascripts/osem-schedule.js index baae1933..39e33922 100644 --- a/app/assets/javascripts/osem-schedule.js +++ b/app/assets/javascripts/osem-schedule.js @@ -78,11 +78,12 @@ var Schedule = { }; $(document).ready( function() { - // hide the remove button for unscheduled events + // hide the remove button for unscheduled and non schedulable events $('.unscheduled-events .schedule-event-delete-button').hide(); + $('.non_schedulable .schedule-event-delete-button').hide(); // set events as draggable - $('.schedule-event').draggable({ + $('.schedule-event').not('.non_schedulable').draggable({ snap: '.schedule-room-slot', revertDuration: 200, revert: function (event, ui) { @@ -99,7 +100,7 @@ $(document).ready( function() { }); // set room cells as droppable - $('.schedule-room-slot').droppable({ + $('.schedule-room-slot').not('.non_schedulable .schedule-room-slot').droppable({ accept: '.schedule-event', tolerance: "pointer", drop: function(event, ui) { diff --git a/app/assets/javascripts/osem-tickets.js b/app/assets/javascripts/osem-tickets.js index ea99a349..3f559c38 100644 --- a/app/assets/javascripts/osem-tickets.js +++ b/app/assets/javascripts/osem-tickets.js @@ -4,14 +4,14 @@ function update_price($this){ // Calculate price for row var value = $this.val(); var price = $('#price_' + id).text(); - $('#total_row_' + id).text(value * price); + $('#total_row_' + id).text((value * price).toFixed(2)); // Calculate total price var total = 0; $('.total_row').each(function( index ) { - total += parseInt($(this).text()); + total += parseFloat($(this).text()); }); - $('#total_price').text(total); + $('#total_price').text(total.toFixed(2)); } $( document ).ready(function() { diff --git a/app/assets/stylesheets/osem-navbar.css.scss b/app/assets/stylesheets/osem-navbar.css.scss index 385cf93d..bfaac9ac 100644 --- a/app/assets/stylesheets/osem-navbar.css.scss +++ b/app/assets/stylesheets/osem-navbar.css.scss @@ -20,14 +20,6 @@ color: black; background-color: #eeeeee; } - input[type=text], - input[type=password] { - text-align: center; - padding: 5px; - display: block; - margin: auto; - width: 255px; - } .btn-group { width: 100%; text-align: center; @@ -37,7 +29,7 @@ } } .dropdown-menu { - padding: 17px 17px 0px 17px; + padding: 17px; min-width: 225px; } } diff --git a/app/assets/stylesheets/osem-schedule.css.scss b/app/assets/stylesheets/osem-schedule.css.scss index 21fda6f3..75a71767 100644 --- a/app/assets/stylesheets/osem-schedule.css.scss +++ b/app/assets/stylesheets/osem-schedule.css.scss @@ -267,6 +267,10 @@ td.no-padding{ font-size: 7px; } +.non_schedulable{ + opacity: 0.5; +} + /* Small devices (tablets, 768px and up) */ @media (min-width: 768px) { .room, .event-title{ diff --git a/app/assets/stylesheets/osem.css.scss b/app/assets/stylesheets/osem.css.scss index bc8b6388..79768c5d 100644 --- a/app/assets/stylesheets/osem.css.scss +++ b/app/assets/stylesheets/osem.css.scss @@ -89,4 +89,18 @@ p.comment-body { .changeset{ display: none; -} \ No newline at end of file +} + +.box{ + height: 230px; +} + +/* sidebar hamburger btn */ +.side-nav-btn{ + margin-left: 10px; + float: left; + } + +.qr-image{ + margin-left: 120px; +} diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 638d2ab6..631dd4d8 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -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 diff --git a/app/controllers/admin/booths_controller.rb b/app/controllers/admin/booths_controller.rb new file mode 100644 index 00000000..102eb10b --- /dev/null +++ b/app/controllers/admin/booths_controller.rb @@ -0,0 +1,116 @@ +module Admin + class BoothsController < Admin::BaseController + load_and_authorize_resource :conference, find_by: :short_title + load_and_authorize_resource through: :conference + + def index; end + + def show; end + + def new + @url = admin_conference_booths_path(@conference.short_title) + end + + def create + @url = admin_conference_booths_path(@conference.short_title) + + @booth = @conference.booths.new(booth_params) + + @booth.submitter = current_user + + if @booth.save + redirect_to admin_conference_booths_path, + notice: 'Booth successfully created.' + else + flash.now[:error] = "Creating booth failed. #{@booth.errors.full_messages.to_sentence}." + render :new + end + end + + def edit + @url = admin_conference_booth_path(@conference.short_title, @booth.id) + end + + def update + @url = admin_conference_booth_path(@conference.short_title, @booth.id) + + @booth.update_attributes(booth_params) + + if @booth.save + redirect_to admin_conference_booths_path, + notice: "Successfully updated booth for #{@booth.title}." + else + flash.now[:error] = "An error prohibited the Booth for #{@booth.title} "\ + "#{@booth.errors.full_messages.join('. ')}." + render :edit + end + end + + def accept + @booth.accept! + + if @booth.save + if @conference.email_settings.send_on_booths_acceptance + Mailbot.conference_booths_acceptance_mail(@booth).deliver + end + redirect_to admin_conference_booths_path(conference_id: @conference.short_title), + notice: 'Booth successfully accepted!' + else + redirect_to admin_conference_booths_path(conference_id: @conference.short_title) + flash[:error] = "Booth could not be accepted. #{@booth.errors.full_messages.to_sentence}." + end + end + + def to_accept + update_state(:to_accept, 'Booth to accept') + end + + def to_reject + update_state(:to_reject, 'Booth to reject') + end + + def reject + @booth.reject! + + if @booth.save + Mailbot.conference_booths_rejection_mail(@booth).deliver + redirect_to admin_conference_booths_path(conference_id: @conference.short_title), + notice: 'Booth successfully rejected.' + else + redirect_to admin_conference_booths_path(conference_id: @conference.short_title) + flash[:error] = "Booth could not be rejected. #{@booth.errors.full_messages.to_sentence}." + end + end + + def restart + update_state(:restart, 'Booth is submitted') + end + + def cancel + update_state(:cancel, 'Booth is canceled') + end + + def confirm + update_state(:confirm, 'Booth successfully confirmed') + end + + private + + def update_state(transition, notice) + alert = @booth.update_state(transition, notice) + + if alert.blank? + flash[:notice] = notice + redirect_back_or_to(admin_conference_booths_path(conference_id: @conference.short_title)) && return + else + flash[:error] = alert + return redirect_back_or_to(admin_conference_booths_path(conference_id: @conference.short_title)) && return + end + end + + def booth_params + params.require(:booth).permit(:title, :description, :reasoning, :state, :picture, :conference_id, + :created_at, :updated_at, :submitter_relationship, :website_url, responsible_ids: []) + end + end +end diff --git a/app/controllers/admin/cfps_controller.rb b/app/controllers/admin/cfps_controller.rb index 42d10218..da2723b7 100644 --- a/app/controllers/admin/cfps_controller.rb +++ b/app/controllers/admin/cfps_controller.rb @@ -2,23 +2,25 @@ 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.now[:error] = "Creating the call for papers failed. #{@cfp.errors.full_messages.join('. ')}." @@ -27,14 +29,13 @@ module Admin 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.now[:error] = "Updating call for papers failed. #{@cfp.errors.to_a.join('. ')}." @@ -44,9 +45,9 @@ module Admin 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 diff --git a/app/controllers/admin/conferences_controller.rb b/app/controllers/admin/conferences_controller.rb index 50360a64..d8c101d1 100644 --- a/app/controllers/admin/conferences_controller.rb +++ b/app/controllers/admin/conferences_controller.rb @@ -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,12 +62,17 @@ 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 def new @conference = Conference.new + @organizations = Organization.accessible_by(current_ability, :update).pluck(:name, :id) end def create @@ -133,6 +145,20 @@ 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) @@ -186,7 +212,7 @@ module Admin :vpositions_attributes, :use_volunteers, :color, :sponsorship_levels_attributes, :sponsors_attributes, :targets, :targets_attributes, - :campaigns, :campaigns_attributes, :registration_limit) + :campaigns, :campaigns_attributes, :registration_limit, :organization_id, :ticket_layout, :booth_limit) end end end diff --git a/app/controllers/admin/emails_controller.rb b/app/controllers/admin/emails_controller.rb index adff714a..a4d6fe04 100644 --- a/app/controllers/admin/emails_controller.rb +++ b/app/controllers/admin/emails_controller.rb @@ -30,7 +30,9 @@ module Admin :send_on_conference_registration_dates_updated, :conference_registration_dates_updated_subject, :conference_registration_dates_updated_body, :send_on_venue_updated, :venue_updated_subject, :venue_updated_body, :send_on_cfp_dates_updated, :cfp_dates_updated_subject, :cfp_dates_updated_body, - :send_on_program_schedule_public, :program_schedule_public_subject, :program_schedule_public_body) + :send_on_program_schedule_public, :program_schedule_public_subject, :program_schedule_public_body, + :send_on_booths_acceptance, :booths_acceptance_subject, :booths_acceptance_body, + :send_on_booths_rejection, :booths_rejection_subject, :booths_rejection_body) end end end diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb index 125d1ef1..accfb281 100644 --- a/app/controllers/admin/events_controller.rb +++ b/app/controllers/admin/events_controller.rb @@ -4,8 +4,10 @@ module Admin load_and_authorize_resource :program, through: :conference, singleton: true load_and_authorize_resource :event, through: :program load_and_authorize_resource :events_registration, only: :toggle_attendance + # For some reason this doesn't work, so a workaround is used + # load_and_authorize_resource :track, through: :program, only: [:index, :show, :edit] - before_action :get_event, except: [:index, :create, :new] + before_action :get_tracks, only: [:index, :show, :edit] # FIXME: The timezome should only be applied on output, otherwise # you get lost in timezone conversions... @@ -16,8 +18,6 @@ module Admin end def index - @events = @program.events - @tracks = @program.tracks @difficulty_levels = @program.difficulty_levels @event_types = @program.event_types @tracks_distribution_confirmed = @conference.tracks_distribution(:confirmed) @@ -43,7 +43,6 @@ module Admin end def show - @tracks = @program.tracks @event_types = @program.event_types @comments = @event.root_comments @comment_count = @event.comment_threads.count @@ -58,7 +57,6 @@ module Admin def edit @event_types = @program.event_types - @tracks = Track.all @comments = @event.root_comments @comment_count = @event.comment_threads.count @user = @event.submitter @@ -104,7 +102,7 @@ module Admin 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(', ')}" + flash.now[:error] = "Could not submit proposal: #{@event.errors.full_messages.join(', ')}" render action: 'new' end end @@ -187,16 +185,6 @@ module Admin params.require(:comment).permit(:commentable, :body, :user_id) end - def get_event - @event = @conference.program.events.find(params[:id]) - unless @event - redirect_to admin_conference_program_events_path(conference_id: @conference.short_title), - error: 'Error! Could not find event!' - return - end - @event - end - def update_state(transition, notice, mail = false, subject = false, send_mail = false) alert = @event.update_state(transition, mail, subject, send_mail, params[:send_mail].blank?) @@ -208,5 +196,9 @@ module Admin return redirect_back_or_to(admin_conference_program_events_path(conference_id: @conference.short_title)) && return end end + + def get_tracks + @tracks = Track.accessible_by(current_ability).where(program: @program).confirmed + end end end diff --git a/app/controllers/admin/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb new file mode 100644 index 00000000..d125c5cf --- /dev/null +++ b/app/controllers/admin/organizations_controller.rb @@ -0,0 +1,94 @@ +module Admin + class OrganizationsController < Admin::BaseController + load_and_authorize_resource :organization + before_action :verify_user, only: [:assign_org_admins, :unassign_org_admins] + + 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 + + def assign_org_admins + if @user.has_role? 'organization_admin', @organization + flash[:error] = "User #{@user.email} already has the role organization admin" + elsif @user.add_role 'organization_admin', @organization + flash[:notice] = "Successfully added role organization admin to user #{@user.email}" + else + flash[:error] = "Coud not add role organization admin to #{@user.email}" + end + + redirect_to admins_admin_organization_path(@organization) + end + + def unassign_org_admins + if @user.remove_role 'organization_admin', @organization + flash[:notice] = "Successfully removed role organization admin from user #{@user.email}" + else + flash[:error] = "Could not remove role organization admin from user #{@user.email}" + end + + redirect_to admins_admin_organization_path(@organization) + end + + def admins + @role = @organization.roles.first + @users = @role.users + render 'show_org_admins' + end + + private + + def user_params + params.require(:user).permit(:email) + end + + def verify_user + @user = User.find_by(email: user_params[:email]) + unless @user + redirect_to admins_admin_organization_path(@organization), + error: 'Could not find user. Please provide a valid email!' + return + end + end + + def organization_params + params.require(:organization).permit(:name, :description, :picture) + end + end +end diff --git a/app/controllers/admin/physical_tickets_controller.rb b/app/controllers/admin/physical_tickets_controller.rb new file mode 100644 index 00000000..0fb26ad8 --- /dev/null +++ b/app/controllers/admin/physical_tickets_controller.rb @@ -0,0 +1,14 @@ +module Admin + class PhysicalTicketsController < 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 diff --git a/app/controllers/admin/programs_controller.rb b/app/controllers/admin/programs_controller.rb index fe401078..3d9f66d8 100644 --- a/app/controllers/admin/programs_controller.rb +++ b/app/controllers/admin/programs_controller.rb @@ -30,7 +30,7 @@ module Admin 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 } + format.js { render json: { errors: "The selected schedule couldn't be updated #{@program.errors.to_a.join('. ')}" }, status: 422 } end end end diff --git a/app/controllers/admin/registration_periods_controller.rb b/app/controllers/admin/registration_periods_controller.rb index d955ddd0..4e48c013 100644 --- a/app/controllers/admin/registration_periods_controller.rb +++ b/app/controllers/admin/registration_periods_controller.rb @@ -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 diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index c6d4003c..b01a4bdc 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -2,9 +2,11 @@ module Admin class ReportsController < Admin::BaseController load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :program, through: :conference, singleton: true + # For some reason this doesn't work, so a workaround is used + # load_and_authorize_resource :event, through: :program def index - @events = @program.events + @events = Event.accessible_by(current_ability).where(program: @program) @events_commercials = Commercial.where(commercialable_type: 'Event', commercialable_id: @events.pluck(:id)) @events_missing_commercial = @events.where.not(id: @events_commercials.pluck(:commercialable_id)) @events_with_requirements = @events.where.not(description: ['', nil]) diff --git a/app/controllers/admin/roles_controller.rb b/app/controllers/admin/roles_controller.rb index 3bc37bc3..7ddb7953 100644 --- a/app/controllers/admin/roles_controller.rb +++ b/app/controllers/admin/roles_controller.rb @@ -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_admin_conference_program_track_role_path(@conference.short_title, @track, @role.name) + else + toggle_user_admin_conference_role_path(@conference.short_title, @role.name) + end @users = @role.users end def edit + @url = if @track + admin_conference_program_track_role_path(@conference.short_title, @track, @role.name) + else + admin_conference_role_path(@conference.short_title, @role.name) + end @users = @role.users end @@ -23,7 +35,13 @@ 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 + admin_conference_program_track_role_path(@conference.short_title, @track, @role.name) + 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 @@ -36,8 +54,14 @@ module Admin user = User.find_by(email: user_params[:email]) state = user_params[:state] + url = if @track + admin_conference_program_track_role_path(@conference.short_title, @track, @role.name) + 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_id]) + @role = Role.find_by(name: @selection, resource: @track) + else + @role = Role.find_by(name: @selection, resource: @conference) + end end def role_params diff --git a/app/controllers/admin/schedules_controller.rb b/app/controllers/admin/schedules_controller.rb index d3c7cea3..9f6c222b 100644 --- a/app/controllers/admin/schedules_controller.rb +++ b/app/controllers/admin/schedules_controller.rb @@ -4,14 +4,21 @@ module Admin # the schedule of a conference, which should not be accessed in the first place load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :program, through: :conference, singleton: true - load_and_authorize_resource :schedule, through: :program + load_and_authorize_resource :schedule, through: :program, except: [:new, :create] load_resource :event_schedules, through: :schedule load_resource :selected_schedule, through: :program, singleton: true load_resource :venue, through: :conference, singleton: true def index; end + def new + @schedule = @program.schedules.build(track: @program.tracks.new) + authorize! :new, @schedule + end + def create + @schedule = @program.schedules.new(schedule_params) + authorize! :create, @schedule if @schedule.save redirect_to admin_conference_schedule_path(@conference.short_title, @schedule.id), notice: 'Schedule was successfully created.' @@ -23,9 +30,24 @@ module Admin def show @event_schedules = @schedule.event_schedules - @unscheduled_events = @program.events.confirmed - @schedule.events - @dates = @conference.start_date..@conference.end_date - @rooms = @conference.venue.rooms if @conference.venue + + if @schedule.track + track = @schedule.track + @unscheduled_events = track.events.confirmed - @schedule.events + @dates = track.start_date..track.end_date + @rooms = [track.room] + else + @program.tracks.self_organized.confirmed.each do |t| + @event_schedules += t.selected_schedule.event_schedules if t.selected_schedule + end + self_organized_tracks_events = @program.tracks.self_organized.confirmed.map do |t| + t.events.confirmed + end + self_organized_tracks_events.flatten.compact! + @unscheduled_events = @program.events.confirmed - @schedule.events - self_organized_tracks_events + @dates = @conference.start_date..@conference.end_date + @rooms = @conference.venue.rooms if @conference.venue + end end def destroy @@ -37,5 +59,11 @@ module Admin error: "Schedule couldn't be deleted. #{@schedule.errors.full_messages.join('. ')}." end end + + private + + def schedule_params + params.require(:schedule).permit(:track_id) if params[:schedule] + end end end diff --git a/app/controllers/admin/splashpages_controller.rb b/app/controllers/admin/splashpages_controller.rb index 2745cc8b..5f04a67b 100644 --- a/app/controllers/admin/splashpages_controller.rb +++ b/app/controllers/admin/splashpages_controller.rb @@ -47,7 +47,8 @@ module Admin :include_tracks, :include_program, :include_cfp, :include_venue, :include_registrations, :include_tickets, :include_lodgings, - :include_sponsors, :include_social_media) + :include_sponsors, :include_social_media, + :include_booths) end end end diff --git a/app/controllers/admin/ticket_scannings_controller.rb b/app/controllers/admin/ticket_scannings_controller.rb new file mode 100644 index 00000000..488f28a1 --- /dev/null +++ b/app/controllers/admin/ticket_scannings_controller.rb @@ -0,0 +1,16 @@ +module Admin + class TicketScanningsController < Admin::BaseController + before_action :authenticate_user! + load_resource :physical_ticket, find_by: :token + # We authorize manually in these actions + skip_authorize_resource only: [:create] + + def create + @ticket_scanning = TicketScanning.new(physical_ticket: @physical_ticket) + authorize! :create, @ticket_scanning + @ticket_scanning.save + redirect_to conferences_path, + notice: "Ticket with token #{@physical_ticket.token} successfully scanned." + end + end +end diff --git a/app/controllers/admin/tickets_controller.rb b/app/controllers/admin/tickets_controller.rb index 9042297b..cf6e0f80 100644 --- a/app/controllers/admin/tickets_controller.rb +++ b/app/controllers/admin/tickets_controller.rb @@ -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 @@ -48,7 +50,7 @@ module Admin private def ticket_params - params.require(:ticket).permit(:conference, :title, :url, :description, :conference_id, :price_cents, :price_currency, :price) + params.require(:ticket).permit(:conference, :title, :url, :description, :conference_id, :price_cents, :price_currency, :price, :registration_ticket) end end end diff --git a/app/controllers/admin/tracks_controller.rb b/app/controllers/admin/tracks_controller.rb index 2df7db67..7352ee35 100644 --- a/app/controllers/admin/tracks_controller.rb +++ b/app/controllers/admin/tracks_controller.rb @@ -2,7 +2,10 @@ 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 + + # Show flash message with ajax calls + after_action :prepare_unobtrusive_flash, only: :toggle_cfp_inclusion def index; end @@ -19,6 +22,8 @@ module Admin def create @track = @program.tracks.new(track_params) + @track.state = 'confirmed' + @track.cfp_active = true if @track.save redirect_to admin_conference_program_tracks_path(conference_id: @conference.short_title), notice: 'Track successfully created.' @@ -50,10 +55,80 @@ module Admin end end + def toggle_cfp_inclusion + @track.cfp_active = !@track.cfp_active + if @track.save + flash[:notice] = "Successfully changed cfp inclusion of #{@track.name} to #{@track.cfp_active}" + else + flash[:error] = "Failed to toggle cfp inclusion of #{@track.name} to #{@track.cfp_active}" + end + + respond_to do |format| + format.js + end + end + + def restart + update_state(:restart, "Review for #{@track.name} started!") + end + + def to_accept + update_state(:to_accept, "Track #{@track.name} marked as a possible acceptance!") + end + + def accept + if @track.room && @track.start_date && @track.end_date + update_state(:accept, "Track #{@track.name} accepted!") + else + flash[:alert] = 'Please make sure that the track has a room and start/end dates before accepting it' + redirect_to edit_admin_conference_program_track_path(@conference.short_title, @track) + end + end + + def confirm + update_state(:confirm, "Track #{@track.name} confirmed!") + end + + def to_reject + update_state(:to_reject, "Track #{@track.name} marked as a possible rejection!") + end + + def reject + update_state(:reject, "Track #{@track.name} rejected!") + end + + def cancel + update_state(:cancel, "Track #{@track.name} canceled!") + end + + def update_selected_schedule + if @track.update_attributes(params.require(:track).permit(:selected_schedule_id)) + respond_to do |format| + format.js { render json: {} } + end + else + respond_to do |format| + format.js { render json: { errors: "The selected schedule couldn't be updated #{@track.errors.to_a.join('. ')}" }, status: 422 } + end + end + end + private def track_params - params.require(:track).permit(:name, :description, :color) + params.require(:track).permit(:name, :description, :color, :short_name, :cfp_active, :start_date, :end_date, :room_id) + end + + def update_state(transition, notice) + errors = @track.update_state(transition) + + if errors.blank? + flash[:notice] = notice + else + flash[:error] = errors + end + + redirect_back_or_to(admin_conference_program_tracks_path(conference_id: @conference.short_title)) end end end diff --git a/app/controllers/admin/versions_controller.rb b/app/controllers/admin/versions_controller.rb index 07108d38..5056f3ff 100644 --- a/app/controllers/admin/versions_controller.rb +++ b/app/controllers/admin/versions_controller.rb @@ -1,17 +1,21 @@ module Admin class VersionsController < Admin::BaseController - skip_authorization_check + load_resource :conference, find_by: :short_title, only: :index + load_and_authorize_resource class: PaperTrail::Version def index - authorize! :index, PaperTrail::Version.new(item_type: 'User') - conf_ids_for_organizer = current_user.is_admin? ? Conference.pluck(:id) : Conference.with_role(:organizer, current_user).pluck(:id) - @versions = PaperTrail::Version.where(["conference_id IN (?) OR item_type = 'User'", conf_ids_for_organizer]) + @conferences_with_role = current_user.is_admin? ? Conference.pluck(:short_title) : Conference.with_role([:organizer, :cfp, :info_desk], current_user).pluck(:short_title) + + if current_user.has_role? :organization_admin, :any + @conferences_with_role = Organization.with_role('organization_admin', current_user).map { |org| org.conferences.pluck :short_title }.flatten + end + @conferences_with_role.uniq! + + return if @conference.blank? + @versions = PaperTrail::Version.where(conference_id: @conference.id).accessible_by(current_ability) end def revert_attribute - @version = PaperTrail::Version.find(params[:id]) - authorize! :revert_attribute, @version - if params[:attribute] && @version.changeset.reject{ |_, values| values[0].blank? && values[1].blank? }.keys.include?(params[:attribute]) if @version.item[params[:attribute]] == @version.changeset[params[:attribute]][0] flash[:error] = 'The item is already in the state that you are trying to revert it back to' @@ -33,9 +37,6 @@ module Admin end def revert_object - @version = PaperTrail::Version.find(params[:id]) - authorize! :revert_object, @version - if @version.event != 'create' if @version.reify.save flash[:notice] = 'The selected change was successfully reverted' diff --git a/app/controllers/admin/volunteers_controller.rb b/app/controllers/admin/volunteers_controller.rb index e8676bae..aefa682a 100644 --- a/app/controllers/admin/volunteers_controller.rb +++ b/app/controllers/admin/volunteers_controller.rb @@ -13,11 +13,11 @@ module Admin 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 + @volunteers = if @conference.use_vpositions + @conference.registrations.joins(:vchoices).uniq + else + @conference.registrations.where(volunteer: true) + end else authorize! :index, :volunteer end diff --git a/app/controllers/booths_controller.rb b/app/controllers/booths_controller.rb new file mode 100644 index 00000000..f0457b88 --- /dev/null +++ b/app/controllers/booths_controller.rb @@ -0,0 +1,97 @@ +class BoothsController < ApplicationController + before_action :authenticate_user! + load_resource :conference, find_by: :short_title + load_and_authorize_resource through: :conference + skip_authorize_resource only: [:withdraw, :confirm, :restart] + + def index + @booths = current_user.booths.where(conference_id: @conference.id).uniq + end + + def show; end + + def new + @url = conference_booths_path(@conference.short_title) + end + + def create + @url = conference_booths_path(@conference.short_title) + + @booth.submitter = current_user + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully created.' + else + flash.now[:error] = "Creating booth failed. #{@booth.errors.full_messages.to_sentence}." + render :new + end + end + + def edit + @url = conference_booth_path(@conference.short_title, @booth.id) + end + + def update + @url = conference_booth_path(@conference.short_title, @booth.id) + @booth.update_attributes(booth_params) + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully updated!' + else + flash.now[:error] = "Booth could not be updated. #{@booth.errors.full_messages.to_sentence}." + end + end + + def destroy; end + + def withdraw + authorize! :update, @booth + @url = conference_booth_path(@conference.short_title, @booth.id) + + @booth.withdraw! + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully withdrawn' + else + flash.now[:error] = "Booth could not be withdrawn. #{@booth.errors.full_messages.to_sentence}." + end + end + + def confirm + authorize! :update, @booth + @url = conference_booth_path(@conference.short_title, @booth.id) + + @booth.confirm! + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully confirmed' + else + flash.now[:error] = "Booth could not be confirmed. #{@booth.errors.full_messages.to_sentence}." + end + end + + def restart + authorize! :update, @booth + @url = conference_booth_path(@conference.short_title, @booth.id) + + @booth.restart! + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully re-submitted' + else + flash.now[:error] = "Booth could not be re-submitted. #{@booth.errors.full_messages.to_sentence}." + end + end + + private + + def booth_params + params.require(:booth).permit(:title, :description, :reasoning, :state, :picture, :conference_id, + :created_at, :updated_at, :submitter_relationship, :website_url, responsible_ids: []) + end +end diff --git a/app/controllers/conference_registrations_controller.rb b/app/controllers/conference_registrations_controller.rb index c229440e..e8809b28 100644 --- a/app/controllers/conference_registrations_controller.rb +++ b/app/controllers/conference_registrations_controller.rb @@ -27,8 +27,9 @@ class ConferenceRegistrationsController < ApplicationController end def show - @total_price = Ticket.total_price(@conference, current_user, paid: true) + @total_price = Ticket.total_price_user(@conference, current_user, paid: true) @tickets = current_user.ticket_purchases.by_conference(@conference).paid + @total_price_per_ticket = @tickets.group(:ticket_id).sum('amount_paid * quantity') @ticket_payments = @tickets.group_by(&:ticket_id) @total_quantity = @tickets.group(:ticket_id).sum(:quantity) end @@ -38,12 +39,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 diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index 53e5bd6d..f141529d 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -1,18 +1,29 @@ class ConferencesController < ApplicationController protect_from_forgery with: :null_session before_action :respond_to_options - load_and_authorize_resource find_by: :short_title - load_resource :program, through: :conference, singleton: true, except: :index + load_and_authorize_resource find_by: :short_title, except: :show def index @current = Conference.where('end_date >= ?', Date.current).reorder(start_date: :asc) @antiquated = @conferences - @current end - def show; end + def show + @conference = if params[:id] + Conference.find_by_short_title(params[:id]) + else + load_conference_by_domain + end + authorize! :show, @conference + @program = @conference.program + end private + def load_conference_by_domain + Conference.find_by(custom_domain: request.domain) + end + def respond_to_options respond_to do |format| format.html { head :ok } diff --git a/app/controllers/openids_controller.rb b/app/controllers/openids_controller.rb new file mode 100644 index 00000000..35bc0bae --- /dev/null +++ b/app/controllers/openids_controller.rb @@ -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 diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb new file mode 100644 index 00000000..3d549dfa --- /dev/null +++ b/app/controllers/organizations_controller.rb @@ -0,0 +1,7 @@ +class OrganizationsController < ApplicationController + load_and_authorize_resource :organization + + def index + @organizations = Organization.all + end +end diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb index e8766548..322ed397 100644 --- a/app/controllers/payments_controller.rb +++ b/app/controllers/payments_controller.rb @@ -10,6 +10,9 @@ class PaymentsController < ApplicationController def new @total_amount_to_pay = Ticket.total_price(@conference, current_user, paid: false) + if @total_amount_to_pay.zero? + raise CanCan::AccessDenied.new('Nothing to pay for!', :new, Payment) + end @unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference) end @@ -18,7 +21,7 @@ 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_tickets_path, notice: 'Thanks! Your ticket is booked successfully.' else @total_amount_to_pay = Ticket.total_price(@conference, current_user, paid: false) @@ -38,6 +41,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 diff --git a/app/controllers/physical_tickets_controller.rb b/app/controllers/physical_tickets_controller.rb new file mode 100644 index 00000000..2f6f18ec --- /dev/null +++ b/app/controllers/physical_tickets_controller.rb @@ -0,0 +1,28 @@ +class PhysicalTicketsController < ApplicationController + before_action :authenticate_user! + load_resource :conference, find_by: :short_title + load_and_authorize_resource find_by: :token + 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 + @qrcode_image = RQRCode::QRCode.new(@physical_ticket.token).as_png(size: 180, border_modules: 0) + respond_to do |format| + format.html + format.pdf do + pdf = TicketPdf.new(@conference, @user, @physical_ticket, @ticket_layout, @file_name) + send_data pdf.render, + filename: @file_name, + type: 'application/pdf', + disposition: 'attachment' + end + end + end +end diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb index baf86f3c..66052310 100644 --- a/app/controllers/proposals_controller.rb +++ b/app/controllers/proposals_controller.rb @@ -49,6 +49,14 @@ class ProposalsController < ApplicationController # by default. @event.speakers = [current_user] @event.submitter = current_user + + track = Track.find_by(id: params[:event][:track_id]) + if track && !track.cfp_active + flash.now[:error] = 'You have selected a track that doesn\'t accept proposals' + render action: 'new' + return + end + if @event.save ahoy.track 'Event submission', title: 'New submission' redirect_to conference_program_proposals_path(@conference.short_title), notice: 'Proposal was successfully submitted.' @@ -61,6 +69,13 @@ class ProposalsController < ApplicationController def update @url = conference_program_proposal_path(@conference.short_title, params[:id]) + track = Track.find_by(id: params[:event][:track_id]) + if track && !track.cfp_active + flash.now[:error] = 'You have selected a track that doesn\'t accept proposals' + render action: 'edit' + return + end + if @event.update(event_params) redirect_to conference_program_proposals_path(conference_id: @conference.short_title), notice: 'Proposal was successfully updated.' diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index 5fdbeecd..eb145730 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -21,9 +21,16 @@ class SchedulesController < ApplicationController # the schedule takes you to today if it is a date of the schedule @current_day = @conference.current_conference_day @day = @current_day.present? ? @current_day : @dates.first - return unless @current_day - # the schedule takes you to the current time if it is beetween the start and the end time. - @hour_column = @conference.hours_from_start_time(@conf_start, @conference.end_hour) + unless @current_day + # the schedule takes you to the current time if it is beetween the start and the end time. + @hour_column = @conference.hours_from_start_time(@conf_start, @conference.end_hour) + end + # Ids of the schedules of confrmed self_organized tracks along with the selected_schedule_id + @selected_schedules_ids = [@conference.program.selected_schedule_id] + @conference.program.tracks.self_organized.confirmed.each do |track| + @selected_schedules_ids << track.selected_schedule_id + end + @selected_schedules_ids.compact! end def events @@ -32,11 +39,7 @@ class SchedulesController < ApplicationController @events_schedules = @program.selected_event_schedules @events_schedules = [] unless @events_schedules - @unscheduled_events = if @program.selected_schedule - @program.events.confirmed - @program.selected_schedule.events - else - @program.events.confirmed - end + @unscheduled_events = @program.events.confirmed - @events_schedules.map(&:event) day = @conference.current_conference_day @tag = day.strftime('%Y-%m-%d') if day diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 351f9e9d..76d5c74a 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -5,17 +5,19 @@ class SubscriptionsController < ApplicationController def create @subscription = current_user.subscriptions.build(conference_id: @conference.id) - if @subscription.save! - redirect_to root_path, notice: "You have been subscribed to receive email notifications for #{@conference.short_title}." + if @subscription.save + redirect_to root_path, notice: "You have subscribed to receive email notifications for #{@conference.title}." else - redirect_to root_path, error: subscription.errors.full_messages.to_sentence + redirect_to root_path, error: @subscription.errors.full_messages.to_sentence end end def destroy @subscription = current_user.subscriptions.find_by(conference_id: @conference.id) + + redirect_to(root_path, error: "You are not subscribed to #{@conference.title}.") && return unless @subscription if @subscription.destroy - redirect_to root_path, notice: "You have been unsubscribed and now you will not be receiving email notifications for #{@conference.short_title}." + redirect_to root_path, notice: "You have unsubscribed and you will not be receiving email notifications for #{@conference.title}." else redirect_to root_path, error: @subscription.errors.full_messages.to_sentence end diff --git a/app/controllers/ticket_purchases_controller.rb b/app/controllers/ticket_purchases_controller.rb index 1c672d69..3eb8b68d 100644 --- a/app/controllers/ticket_purchases_controller.rb +++ b/app/controllers/ticket_purchases_controller.rb @@ -2,6 +2,7 @@ class TicketPurchasesController < ApplicationController before_filter :authenticate_user! load_resource :conference, find_by: :short_title authorize_resource :conference_registrations, class: Registration + authorize_resource def create current_user.ticket_purchases.by_conference(@conference).unpaid.destroy_all @@ -11,18 +12,22 @@ 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_tickets_path, notice: 'You have free tickets for the conference.' else redirect_to conference_tickets_path(@conference.short_title), error: 'Please get at least one ticket to continue.' end else - redirect_to conference_conference_registration_path(@conference.short_title), + redirect_to conference_tickets_path(@conference.short_title), error: "Oops, something went wrong with your purchase! #{message}" end end + def index + @unpaid_ticket_purchases = current_user.ticket_purchases.by_conference(@conference).unpaid + end + private def ticket_purchase_params diff --git a/app/controllers/tracks_controller.rb b/app/controllers/tracks_controller.rb new file mode 100644 index 00000000..6b584d22 --- /dev/null +++ b/app/controllers/tracks_controller.rb @@ -0,0 +1,70 @@ +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 = @tracks.where(submitter: current_user) + 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.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 + + def restart + update_state(:restart, "Track #{@track.name} re-submitted.") + end + + def confirm + update_state(:confirm, "Track #{@track.name} confirmed.") + end + + def withdraw + update_state(:withdraw, "Track #{@track.name} withdrawn.") + end + + private + + def track_params + params.require(:track).permit(:name, :description, :color, :short_name, :start_date, :end_date, :relevance) + end + + def update_state(transition, notice) + errors = @track.update_state(transition) + + if errors.blank? + flash[:notice] = notice + else + flash[:error] = errors + end + + redirect_back_or_to(conference_program_tracks_path(conference_id: @conference.short_title)) + end +end diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index cf6eb6a8..ed7ce011 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -11,13 +11,13 @@ module Users def handle(provider) auth_hash = request.env['omniauth.auth'] - uid = auth_hash[:uid] + username = auth_hash.info.email.split('@')[0] openid = Openid.find_for_oauth(auth_hash) # Get or create openid # If openid exists and is associated with a user, sign in with associated user, # even if the email of the associated user and the email of the provided openid are different unless (user = openid.user) user = User.find_for_auth(auth_hash, current_user) # Get or create users - user.username = "#{uid}@#{provider}" if user.username.blank? + user.username = "#{username}@#{provider}" if user.username.blank? end begin diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index b3081f9b..6174500e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -33,6 +33,11 @@ module ApplicationHelper result end + # 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) def resource_name :user @@ -51,7 +56,7 @@ module ApplicationHelper end def tracks(conference) - all = conference.program.tracks.map {|t| t.name} + all = conference.program.tracks.confirmed.cfp_active.pluck(:name) first = all[0...-1] last = all[-1] ts = '' @@ -93,25 +98,6 @@ module ApplicationHelper .reverse.sub(',', ' dna ').reverse end - # Recieves a model_name and id - # Returns nil if model_name is invalid - # Returns object in its current state if its alive - # Otherwise Returns object state just before deletion - def current_or_last_object_state(model_name, id) - return nil unless id.present? && model_name.present? - begin - object = model_name.constantize.find_by(id: id) - rescue NameError - return nil - end - - if object.nil? - object_last_version = PaperTrail::Version.where(item_type: model_name, item_id: id).last - object = object_last_version.reify if object_last_version - end - object - end - def normalize_array_length(hashmap, length) hashmap.each do |_, value| if value.length < length @@ -131,7 +117,7 @@ module ApplicationHelper 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) + event_schedule = event.program.selected_event_schedules.find { |es| es.event == event } other_event_schedules = event.program.selected_event_schedules.reject { |other_event_schedule| other_event_schedule == event_schedule } concurrent_events = [] @@ -147,7 +133,7 @@ module ApplicationHelper end def speaker_links(event) - event.speakers.map{ |speaker| link_to speaker.name, admin_user_path(speaker) }.join(', ').html_safe + safe_join(event.speakers.map{ |speaker| link_to speaker.name, admin_user_path(speaker) }, ',') end def speaker_selector_input(form) @@ -157,6 +143,14 @@ module ApplicationHelper include_blank: false, label: 'Speakers', input_html: { class: 'select-help-toggle', multiple: 'true' } end + def responsibles_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 :responsibles, as: :select, + collection: options_for_select(users.map {|user| ["#{user[1]} (#{user[2]}) #{user[3]}", user[0]]}, @booth.responsibles.map(&:id)), + include_blank: false, label: 'Responsibles', input_html: { class: 'select-help-toggle', multiple: 'true' }, + hint: 'The people responsible for the booth. You can only select existing users.' + end + def event_types(conference) conference.program.event_types.map { |et| et.title.pluralize }.to_sentence end @@ -168,4 +162,13 @@ module ApplicationHelper 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 diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 012bd930..8f620e62 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -39,4 +39,12 @@ module EventsHelper content_tag :span, 'REPLACEMENT', class: (['label', 'label-info'] + label_classes) end end + + def track_selector_input(form) + if @program.tracks.confirmed.cfp_active.any? + form.input :track_id, as: :select, + collection: @program.tracks.confirmed.cfp_active.pluck(:name, :id), + include_blank: '(Please select)' + end + end end diff --git a/app/helpers/format_helper.rb b/app/helpers/format_helper.rb index bd64e3ac..65578ba6 100644 --- a/app/helpers/format_helper.rb +++ b/app/helpers/format_helper.rb @@ -15,6 +15,19 @@ module FormatHelper end end + def booth_status_icon(booth) + case booth.state + when 'new', 'to_reject', 'to_accept' + 'fa-eye' + when 'accepted' + '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 @@ -102,13 +115,12 @@ module FormatHelper end end - # rubocop:disable Lint/EndAlignment def word_pluralize(count, singular, plural = nil) word = if (count == 1 || count =~ /^1(\.0+)?$/) - singular - else - plural || singular.pluralize - end + singular + else + plural || singular.pluralize + end "#{word}" end @@ -176,7 +188,7 @@ module FormatHelper (schedule == @selected_schedule) ? 'Yes' : 'No' end - def markdown(text) + def markdown(text, escape_html=true) return '' if text.nil? options = { @@ -184,12 +196,12 @@ module FormatHelper space_after_headers: true, no_intra_emphasis: true } - markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(escape_html: true), options) + 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") + 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) diff --git a/app/helpers/paths_helper.rb b/app/helpers/paths_helper.rb index 97de8bca..df01ab2f 100644 --- a/app/helpers/paths_helper.rb +++ b/app/helpers/paths_helper.rb @@ -2,14 +2,6 @@ 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) diff --git a/app/helpers/versions_helper.rb b/app/helpers/versions_helper.rb index e919eb79..5788fe9d 100644 --- a/app/helpers/versions_helper.rb +++ b/app/helpers/versions_helper.rb @@ -2,35 +2,88 @@ 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 + def link_if_alive(version, link_text, link_url, conference) + version.item && conference ? link_to(link_text, link_url) : "#{link_text} with ID #{version.item_id}" + end + + def link_to_organization(organization_id) + return 'deleted organization' unless organization_id + + org = Organization.find_by(id: organization_id) + return current_or_last_object_state('Organization', organization_id).try(:name) unless org + org.name.to_s + end + + def link_to_conference(conference_id) + return 'deleted conference' if conference_id.nil? + + conference = Conference.find_by(id: conference_id) + if conference + link_to conference.short_title, + edit_admin_conference_path(conference.short_title) + else + short_title = current_or_last_object_state('Conference', conference_id).try(:short_title) || '' + " #{short_title} with ID #{conference_id}" + end + end + + def link_to_user(user_id) + return 'Someone (probably via the console)' unless user_id + + user = User.find_by(id: user_id) + if user + link_to user.name, admin_user_path(id: user_id) + else + name = current_or_last_object_state('User', user_id).try(:name) || PaperTrail::Version.where(item_type: 'User', item_id: user_id).last.changeset['name'].second if PaperTrail::Version.where(item_type: 'User', item_id: user_id).any? + "#{name ? name : 'Unknown user'} with ID #{user_id}" + end + end + + # Receives a model_name and id + # Returns nil if model_name is invalid + # Returns object in its current state if its alive + # Otherwise Returns object state just before deletion + def current_or_last_object_state(model_name, id) + return nil unless id.present? && model_name.present? + begin + object = model_name.constantize.find_by(id: id) + rescue NameError + return nil + end + + if object.nil? + object_last_version = PaperTrail::Version.where(item_type: model_name, item_id: id).last + object = object_last_version.reify if object_last_version + end + object 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 + user_id = current_or_last_object_state(version.item_type, version.item_id).user_id + user_name = User.find_by(id: user_id).try(:name) || current_or_last_object_state('User', user_id).try(:name) || PaperTrail::Version.where(item_type: 'User', item_id: user_id).last.changeset[:name].second 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 + user_id = current_or_last_object_state(version.item_type, version.item_id).user_id 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 + user_id = current_or_last_object_state('Registration', registration_id).user_id end + user_name = User.find_by(id: user_id).try(:name) || current_or_last_object_state('User', user_id).try(:name) || PaperTrail::Version.where(item_type: 'User', item_id: user_id).last.changeset[:name].second - if user.id.to_s == version.whodunnit + 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' + 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" + 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 diff --git a/app/mailers/mailbot.rb b/app/mailers/mailbot.rb index 3d70f18d..63f95a40 100644 --- a/app/mailers/mailbot.rb +++ b/app/mailers/mailbot.rb @@ -8,6 +8,22 @@ class Mailbot < ActionMailer::Base conference.email_settings.registration_body)) end + def ticket_confirmation_mail(ticket_purchase) + @ticket_purchase = ticket_purchase + @conference = ticket_purchase.conference + @user = ticket_purchase.user + + PhysicalTicket.last(ticket_purchase.quantity).each do |physical_ticket| + pdf = TicketPdf.new(@conference, @user, physical_ticket, @conference.ticket_layout.to_sym, "ticket_for_#{@conference.short_title}_#{physical_ticket.id}") + attachments["ticket_for_#{@conference.short_title}_#{physical_ticket.id}.pdf"] = pdf.render + end + + mail(to: @user.email, + from: @conference.contact.email, + template_name: 'ticket_confirmation_template', + subject: "#{@conference.title} | Ticket Confirmation and PDF!") + end + def acceptance_mail(event) conference = event.program.conference @@ -81,6 +97,24 @@ class Mailbot < ActionMailer::Base conference.email_settings.cfp_dates_updated_body)) end + def conference_booths_acceptance_mail(booth) + conference = booth.conference + + mail(to: booth.submitter.email, + from: conference.contact.email, + subject: conference.email_settings.booths_acceptance_subject, + body: conference.email_settings.generate_booth_mail(booth, conference.email_settings.booths_acceptance_body)) + end + + def conference_booths_rejection_mail(booth) + conference = booth.conference + + mail(to: booth.submitter.email, + from: conference.contact.email, + subject: conference.email_settings.booths_rejection_subject, + body: conference.email_settings.generate_booth_mail(booth, conference.email_settings.booths_rejection_body)) + end + def event_comment_mail(comment, user) @comment = comment @event = @comment.commentable diff --git a/app/models/ability.rb b/app/models/ability.rb index ff1628a1..18f9d4c4 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -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 @@ -73,7 +60,6 @@ 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 @@ -83,9 +69,19 @@ class Ability 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 [:new, :create], Booth do |booth| + booth.new_record? && booth.conference.program.cfps.for_booths.try(:open?) + end + + can [:edit, :update, :index, :show], Booth do |booth| + booth.users.include?(user) + end can [:create, :destroy], Subscription, user_id: user.id @@ -93,181 +89,58 @@ class Ability event.program.cfp_open? && event.new_record? end - can [:update, :show, :delete, :index], Event do |event| + can [:update, :show, :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 [:new, :create], Track do |track| + track.new_record? && track.program.cfps.for_tracks.try(:open?) + end + + can [:index, :show, :restart, :confirm, :withdraw], Track, submitter_id: user.id + + can [:edit, :update], Track do |track| + user == track.submitter && !(track.accepted? || track.confirmed?) + end 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? - # 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 - - 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, 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 - - # 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 diff --git a/app/models/admin_ability.rb b/app/models/admin_ability.rb new file mode 100644 index 00000000..cc318da2 --- /dev/null +++ b/app/models/admin_ability.rb @@ -0,0 +1,324 @@ +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, 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, :admins], 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 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 + # Can't accept a booth when booth_limit is reached + cannot :accept, Booth do |booth| + conference = booth.conference + conference.maximum_accepted_booths? + 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, :assign_org_admins, :unassign_org_admins, :admins], Organization, id: org_ids_for_organization_admin + can :new, Conference + can :manage, Conference, organization_id: org_ids_for_organization_admin + can [:index, :show], Role + + 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 do |registration_period| + conference = registration_period.conference + conf_ids.include?(conference.id) && conference.tickets.for_registration.any? + end + can :manage, Booth, 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 :create, TicketScanning do |ticket_scanning| + conf_id = ticket_scanning.physical_ticket.ticket_purchase.conference_id + conf_ids.include? conf_id + end + 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, item_type: 'User' + can [:index, :revert_object, :revert_attribute], PaperTrail::Version, conference_id: conf_ids + 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, Booth, 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: %w[Event EventType Track DifficultyLevel EmailSettings Room Cfp Program Comment], conference_id: conf_ids_for_cfp + can [:index, :revert_object, :revert_attribute], PaperTrail::Version, + ["item_type = 'Commercial' AND conference_id IN (?) AND (object LIKE '%Event%' OR object_changes LIKE '%Event%')", conf_ids_for_cfp] 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 + can :create, TicketScanning do |ticket_scanning| + conf_id = ticket_scanning.physical_ticket.ticket_purchase.conference_id + conf_ids_for_info_desk.include? conf_id + 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 + can [:index, :revert_object, :revert_attribute], PaperTrail::Version, item_type: 'Registration', conference_id: conf_ids_for_info_desk + 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 + + cannot [:edit, :update], Track do |track| + track.self_organized_and_accepted_or_confirmed? + end + + # 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 + + # Show Events in the admin sidebar + can :update, Event do |event| + event.new_record? && conf_ids_for_track_organizer.include?(event.program.conference_id) + end + + can :manage, Event, track_id: track_ids_for_track_organizer + can :manage, Commercial, commercialable_type: 'Event', + commercialable_id: Event.where(track_id: track_ids_for_track_organizer).pluck(:id) + + # Show Scheduless in the admin sidebar + can :update, Schedule do |schedule| + schedule.new_record? && conf_ids_for_track_organizer.include?(schedule.program.conference_id) + end + + # Show new track schedule button + can :new, Schedule do |schedule| + schedule.new_record? && conf_ids_for_track_organizer.include?(schedule.program.conference_id) && schedule.track.try(:new_record?) + end + + can :manage, Schedule, track_id: track_ids_for_track_organizer + can :manage, EventSchedule, schedule: { track_id: track_ids_for_track_organizer } + end +end diff --git a/app/models/booth.rb b/app/models/booth.rb new file mode 100644 index 00000000..4e42d839 --- /dev/null +++ b/app/models/booth.rb @@ -0,0 +1,83 @@ +class Booth < ActiveRecord::Base + include ActiveRecord::Transitions + has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id } + + belongs_to :conference + has_many :booth_requests, dependent: :destroy + has_many :users, through: :booth_requests + + has_one :submitter_booth_user, -> { where(role: 'submitter') }, class_name: 'BoothRequest' + has_one :submitter, through: :submitter_booth_user, source: :user + + has_many :responsibles_booth_user, -> { where(role: 'responsible') }, class_name: 'BoothRequest' + has_many :responsibles, through: :responsibles_booth_user, source: :user + + validates :title, + uniqueness: { case_sensitive: false }, + presence: true + + validates :description, + :reasoning, + :state, + :responsibles, + :conference_id, + :website_url, + :submitter_relationship, + presence: true + + scope :accepted, -> { where(state: 'accepted') } + scope :confirmed, -> { where(state: 'confirmed') } + + mount_uploader :picture, PictureUploader, mount_on: :logo_link + + state_machine initial: :new do + state :new + state :withdrawn + state :to_accept + state :accepted + state :to_reject + state :rejected + state :canceled + state :confirmed + + event :restart do + transitions to: :new, from: [:withdrawn, :rejected, :canceled] + end + event :withdraw do + transitions to: :withdrawn, from: [:new, :to_accept, :accepted, :to_reject, :rejected, :confirmed] + end + event :confirm do + transitions to: :confirmed, from: [:accepted] + end + event :to_accept do + transitions to: :to_accept, from: [:new, :to_reject] + end + event :to_reject do + transitions to: :to_reject, from: [:new, :to_accept] + end + event :accept do + transitions to: :accepted, from: [:new, :to_accept] + end + event :reject do + transitions to: :rejected, from: [:new, :to_reject] + end + event :cancel do + transitions to: :canceled, from: [:accepted, :rejected, :to_accept, :to_reject, :confirmed] + end + end + + def transition_possible?(transition) + self.class.state_machine.events_for(current_state).include?(transition) + end + + def update_state(transition, _notice) + alert = '' + begin + send(transition) + save + rescue Transitions::InvalidTransition => e + alert = "Update state failed. #{e.message}" + end + alert + end +end diff --git a/app/models/booth_request.rb b/app/models/booth_request.rb new file mode 100644 index 00000000..438b4167 --- /dev/null +++ b/app/models/booth_request.rb @@ -0,0 +1,9 @@ +class BoothRequest < ActiveRecord::Base + belongs_to :booth + belongs_to :user + + validates :role, + presence: true + + ROLES = %w[submitter responsible].freeze +end diff --git a/app/models/cfp.rb b/app/models/cfp.rb index affcc408..17b37809 100644 --- a/app/models/cfp.rb +++ b/app/models/cfp.rb @@ -1,6 +1,8 @@ # cannot delete program if there are events submitted class Cfp < ActiveRecord::Base + TYPES = %w(events booths tracks).freeze + has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id } belongs_to :program @@ -8,6 +10,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 @@ -55,15 +66,52 @@ class Cfp < ActiveRecord::Base 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 + + ## + # Finds the cfp for events if it exists + # + # ====Returns + # * +Cfp+ -> The cfp with type 'events' + def self.for_events + find_by(cfp_type: 'events') + end + + ## + # Finds the cfp for tracks if it exists + # + # ====Returns + # * +Cfp+ -> The cfp with type 'tracks' + def self.for_tracks + find_by(cfp_type: 'tracks') + end + + ## + # Finds the cfp for booths if it exists + # + # ====Returns + # * +Cfp+ -> The cfp with type 'booths' + def self.for_booths + find_by(cfp_type: 'booths') + end + private def before_end_of_conference - 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 - 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 diff --git a/app/models/comment.rb b/app/models/comment.rb index 947b2f54..4bb90719 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -8,7 +8,7 @@ class Comment < ActiveRecord::Base # 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 diff --git a/app/models/concerns/revision_count.rb b/app/models/concerns/revision_count.rb new file mode 100644 index 00000000..71789f34 --- /dev/null +++ b/app/models/concerns/revision_count.rb @@ -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 diff --git a/app/models/conference.rb b/app/models/conference.rb index 1c4ccc73..c56161f2 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -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,6 +7,10 @@ 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) }) + + belongs_to :organization has_paper_trail ignore: %i(updated_at guid revision events_per_week), meta: { conference_id: :id } @@ -17,11 +22,17 @@ 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 - has_many :tickets, dependent: :destroy + has_many :tickets, dependent: :destroy do + def for_registration + where(registration_ticket: true) + end + end has_many :resources, dependent: :destroy + has_many :booths, dependent: :destroy has_many :lodgings, dependent: :destroy has_many :registrations, dependent: :destroy @@ -53,7 +64,9 @@ class Conference < ActiveRecord::Base :start_date, :end_date, :start_hour, - :end_hour, presence: true + :end_hour, + :ticket_layout, + :organization, presence: true validates :short_title, uniqueness: true validates :short_title, format: { with: /\A[a-zA-Z0-9_-]*\z/ } @@ -69,6 +82,8 @@ class Conference < ActiveRecord::Base after_create :create_free_ticket after_update :delete_event_schedules + enum ticket_layout: [:portrait, :landscape] + ## # Checks if the user is registered to the conference # @@ -128,7 +143,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) @@ -176,7 +191,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) @@ -184,6 +199,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. # @@ -281,8 +349,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 @@ -292,10 +360,10 @@ class Conference < ActiveRecord::Base # ====Returns # * +hash+ -> user: submissions def get_top_submitter(limit = 5) - submitter = EventUser.joins(:event) + submitter = EventUser.joins(:event).select(:user_id) .where('event_role = ? and program_id = ?', 'submitter', Conference.find(id).program.id) .limit(limit).group(:user_id) - counter = submitter.order('count_all desc').count + counter = submitter.order('count_all desc').count(:all) Conference.calculate_user_submission_hash(submitter, counter) end @@ -396,6 +464,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_total(ticket.id)).delete(',').to_i, + 'color' => "\##{Digest::MD5.hexdigest(ticket.title)[0..5]}" + } + end + end + result + end + ## # Calculates the overall program minutes # @@ -460,11 +568,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) @@ -547,7 +655,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 @@ -634,6 +742,28 @@ class Conference < ActiveRecord::Base (start_hour..(end_hour - 1)).cover?(current_hour) ? current_hour - start_hour : 0 end + ## + # + # ====Returns + # * +True+ -> if accepted booths are equal to the booth limit + # * +False+ -> Accepted booths have not reached the booth limit + def maximum_accepted_booths? + booth_limit > 0 && booths.accepted.count + booths.confirmed.count >= booth_limit + end + + ## + # Return the current conference object to be used in RevisionCount + # + # ====Returns + # * +ActiveRecord+ + def conference + self + end + + def to_param + short_title + end + private # Returns a different html colour for every i and consecutive colors are @@ -803,7 +933,7 @@ class Conference < ActiveRecord::Base hash[key] = 0 end end - Hash[ hash.sort ] + Hash[hash.sort] end ## @@ -889,11 +1019,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) @@ -1004,7 +1134,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 diff --git a/app/models/email_settings.rb b/app/models/email_settings.rb index 6d6c4478..6210cf9d 100644 --- a/app/models/email_settings.rb +++ b/app/models/email_settings.rb @@ -3,7 +3,7 @@ class EmailSettings < ActiveRecord::Base has_paper_trail on: [:update], ignore: [:updated_at], meta: { conference_id: :conference_id } - def get_values(conference, user, event = nil) + def get_values(conference, user, event = nil, booth = nil) h = { 'email' => user.email, 'name' => user.name, @@ -45,6 +45,10 @@ class EmailSettings < ActiveRecord::Base h['proposalslink'] = Rails.application.routes.url_helpers.conference_program_proposals_url( conference.short_title, host: (ENV['OSEM_HOSTNAME'] || 'localhost:3000')) end + + if booth + h['booth_title'] = booth.title + end h end @@ -58,6 +62,11 @@ class EmailSettings < ActiveRecord::Base parse_template(conf_update_template, values) end + def generate_booth_mail(booth, booth_template) + values = get_values(booth.conference, booth.submitter, nil, booth) + parse_template(booth_template, values) + end + private def parse_template(text, values) diff --git a/app/models/event.rb b/app/models/event.rb index 0c786066..c6de2d45 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -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 @@ -44,6 +45,7 @@ class Event < ActiveRecord::Base validates :max_attendees, numericality: { only_integer: true, greater_than_or_equal_to: 1, allow_nil: true } validate :max_attendees_no_more_than_room_size + validate :valid_track scope :confirmed, -> { where(state: 'confirmed') } scope :canceled, -> { where(state: 'canceled') } @@ -83,7 +85,7 @@ class Event < ActiveRecord::Base # ====Returns # * +true+ or +false+ def scheduled? - event_schedules.find_by(schedule_id: program.selected_schedule_id).present? + event_schedules.find_by(schedule_id: selected_schedule_id).present? end def registration_possible? @@ -115,7 +117,7 @@ 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 @@ -241,14 +243,22 @@ class Event < ActiveRecord::Base def room # We use try(:selected_schedule_id) because this function is used for # validations so program could not be present there - event_schedules.find_by(schedule_id: program.try(:selected_schedule_id)).try(:room) + if track.try(:self_organized?) + track.room + else + event_schedules.find_by(schedule_id: program.try(:selected_schedule_id)).try(:room) + end end ## # Returns the start time at which this event is scheduled # def time - event_schedules.find_by(schedule_id: program.selected_schedule_id).try(:start_time) + event_schedules.find_by(schedule_id: selected_schedule_id).try(:start_time) + end + + def conference + program.conference end private @@ -298,4 +308,25 @@ class Event < ActiveRecord::Base def conference_id program.conference_id end + + ## + # Allow only confirmed tracks that belong to the same program as the event + # + def valid_track + return unless track && track.program && program + errors.add(:track, 'is invalid') unless track.confirmed? && track.program == program + end + + ## + # Return the id of the selected schedule + # + # ====Returns + # * +Integer+ -> selected_schedule_id of self-organized track or program + def selected_schedule_id + if track.try(:self_organized?) + track.selected_schedule_id + else + program.selected_schedule_id + end + end end diff --git a/app/models/event_schedule.rb b/app/models/event_schedule.rb index 82f1e55a..57edbec0 100644 --- a/app/models/event_schedule.rb +++ b/app/models/event_schedule.rb @@ -12,6 +12,9 @@ class EventSchedule < ActiveRecord::Base validates :event, uniqueness: { scope: :schedule } validate :start_after_end_hour validate :start_before_start_hour + validate :same_room_as_track + validate :during_track + validate :valid_schedule scope :confirmed, -> { joins(:event).where('state = ?', 'confirmed') } scope :canceled, -> { joins(:event).where('state = ?', 'canceled') } @@ -52,4 +55,35 @@ class EventSchedule < ActiveRecord::Base def conference_id schedule.program.conference_id end + + ## + # Validates that the event is scheduled in the same room as its track + # + def same_room_as_track + return unless event.try(:track).try(:room) + errors.add(:room, "must be the same as the track's room (#{event.track.room.name})") unless event.track.room == room + end + + ## + # Validates that the event is scheduled within its track's time slot + # + def during_track + return unless event.try(:track) && start_time + + if event.track.try(:start_date) && event.track.start_date > start_time + errors.add(:start_time, "can't be before the track's start date (#{event.track.start_date})") + end + + if event.track.try(:end_date) && event.track.end_date + 1.day < end_time + errors.add(:end_time, "can't be after the track's end date (#{event.track.end_date})") + end + end + + ## + # Validates that the event is scheduled in its self-organized tracks's schedules + # + def valid_schedule + return unless event.try(:track).try(:self_organized?) && schedule + errors.add(:schedule, "must be one of #{event.track.name} track's schedules") unless event.track.schedules.include?(schedule) + end end diff --git a/app/models/event_user.rb b/app/models/event_user.rb index 64952a28..70d9ca91 100644 --- a/app/models/event_user.rb +++ b/app/models/event_user.rb @@ -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 diff --git a/app/models/organization.rb b/app/models/organization.rb new file mode 100644 index 00000000..0354df5b --- /dev/null +++ b/app/models/organization.rb @@ -0,0 +1,19 @@ +class Organization < ActiveRecord::Base + resourcify :roles, dependent: :delete_all + + has_paper_trail + + 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 diff --git a/app/models/physical_ticket.rb b/app/models/physical_ticket.rb new file mode 100644 index 00000000..689d118a --- /dev/null +++ b/app/models/physical_ticket.rb @@ -0,0 +1,22 @@ +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 + + before_create :set_token + + private + + def set_token + self.token = generate_token + end + + def generate_token + loop do + token = SecureRandom.hex(10) + break token unless PhysicalTicket.exists?(token: token) + end + end +end diff --git a/app/models/program.rb b/app/models/program.rb index e36d65bf..612d8154 100644 --- a/app/models/program.rb +++ b/app/models/program.rb @@ -5,7 +5,7 @@ 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 @@ -71,7 +71,11 @@ class Program < ActiveRecord::Base # Returns all event_schedules for the selected schedule ordered by start_time def selected_event_schedules - selected_schedule.event_schedules.order(start_time: :asc) if selected_schedule + event_schedules = selected_schedule.event_schedules.order(start_time: :asc) if selected_schedule + tracks.self_organized.confirmed.order(start_date: :asc).each do |track| + event_schedules += track.selected_schedule.event_schedules.order(start_time: :asc) if track.selected_schedule + end + event_schedules.sort_by(&:start_time) if event_schedules end ## @@ -163,6 +167,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 ## diff --git a/app/models/registration.rb b/app/models/registration.rb index 5d2555f2..7003dfd7 100644 --- a/app/models/registration.rb +++ b/app/models/registration.rb @@ -2,7 +2,6 @@ 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 diff --git a/app/models/revision_observer.rb b/app/models/revision_observer.rb deleted file mode 100644 index 4d2784f4..00000000 --- a/app/models/revision_observer.rb +++ /dev/null @@ -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 diff --git a/app/models/room.rb b/app/models/room.rb index 1477a2c6..8a50551d 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -1,6 +1,8 @@ class Room < ActiveRecord::Base + include RevisionCount belongs_to :venue has_many :event_schedules, dependent: :destroy + has_many :tracks has_paper_trail ignore: [:guid], meta: { conference_id: :conference_id } @@ -10,6 +12,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 diff --git a/app/models/schedule.rb b/app/models/schedule.rb index b14a978c..082d0af0 100644 --- a/app/models/schedule.rb +++ b/app/models/schedule.rb @@ -1,5 +1,6 @@ class Schedule < ActiveRecord::Base belongs_to :program + belongs_to :track has_many :event_schedules, dependent: :destroy has_many :events, through: :event_schedules diff --git a/app/models/subscription.rb b/app/models/subscription.rb index aa688ae8..984c46f3 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -1,5 +1,4 @@ class Subscription < ActiveRecord::Base - validates :user_id, uniqueness: { scope: [:conference_id] } belongs_to :conference belongs_to :user diff --git a/app/models/ticket.rb b/app/models/ticket.rb index 7bac9462..13646c9f 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -55,12 +55,18 @@ class Ticket < ActiveRecord::Base result ? result : Money.new(0, 'USD') end - def tickets_sold - ticket_purchases.sum(:quantity) + def self.total_price_user(conference, user, paid: false) + tickets = TicketPurchase.where(conference: conference, user: user, paid: paid) + tickets.inject(0){ |sum, ticket| sum + (ticket.amount_paid * ticket.quantity) } end - def tickets_turnover - tickets_sold * price + def tickets_turnover_total(id) + tickets = TicketPurchase.where(ticket_id: id) + tickets.inject(0){ |sum, ticket| sum + (ticket.amount_paid * ticket.quantity) } + end + + def tickets_sold + ticket_purchases.paid.sum(:quantity) end private diff --git a/app/models/ticket_purchase.rb b/app/models/ticket_purchase.rb index 07fb7e16..71b44d18 100644 --- a/app/models/ticket_purchase.rb +++ b/app/models/ticket_purchase.rb @@ -2,9 +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 - + validate :one_registration_ticket_per_user + validate :registration_ticket_already_purchased, on: :create validates :quantity, numericality: { greater_than: 0 } delegate :title, to: :ticket @@ -13,25 +15,32 @@ 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) } + after_create :set_week + def self.purchase(conference, user, purchases) errors = [] - ActiveRecord::Base.transaction do - 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 - - if purchase && !purchase.save - errors.push(purchase.errors.full_messages) + if count_purchased_registration_tickets(conference, purchases) > 1 + errors.push('You cannot buy more than one registration tickets.') + else + ActiveRecord::Base.transaction do + 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 + 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) + end end end end @@ -44,7 +53,8 @@ class TicketPurchase < ActiveRecord::Base conference_id: conference.id, user_id: user.id, quantity: quantity, - paid: ticket.price_cents.zero?) + amount_paid: ticket.price) + purchase.pay(nil) if ticket.price_cents.zero? end purchase end @@ -58,4 +68,37 @@ 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 + Mailbot.ticket_confirmation_mail(self).deliver_later + end + + def one_registration_ticket_per_user + if ticket.try(:registration_ticket?) && quantity != 1 + errors.add(:quantity, 'cannot be greater than one for registration tickets.') + end + end + + def registration_ticket_already_purchased + if ticket.try(:registration_ticket?) && user.tickets.for_registration(conference).present? + errors.add(:quantity, 'cannot be greater than one for registration tickets.') + end + end +end + +private + +def set_week + self.week = created_at.strftime('%W') + save! +end + +def count_purchased_registration_tickets(conference, purchases) + conference.tickets.for_registration.inject(0) do |sum, registration_ticket| + sum + purchases[registration_ticket.id.to_s].to_i + end end diff --git a/app/models/ticket_scanning.rb b/app/models/ticket_scanning.rb new file mode 100644 index 00000000..350d4f9b --- /dev/null +++ b/app/models/ticket_scanning.rb @@ -0,0 +1,13 @@ +class TicketScanning < ActiveRecord::Base + belongs_to :physical_ticket + + before_create :mark_user_present + + private + + def mark_user_present + if physical_ticket.ticket.registration_ticket? + physical_ticket.user.mark_attendance_for_conference(physical_ticket.conference) + end + end +end diff --git a/app/models/track.rb b/app/models/track.rb index 1d94f7d5..02d49f8c 100644 --- a/app/models/track.rb +++ b/app/models/track.rb @@ -1,15 +1,176 @@ class Track < ActiveRecord::Base - belongs_to :program - has_many :events, dependent: :nullify + include ActiveRecord::Transitions + include RevisionCount - has_paper_trail only: [:name, :description, :color], meta: { conference_id: :conference_id } + resourcify :roles, dependent: :delete_all + + belongs_to :program + belongs_to :submitter, class_name: 'User' + belongs_to :room + belongs_to :selected_schedule, class_name: 'Schedule' + has_many :events, dependent: :nullify + has_many :schedules + + has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id } 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, + inclusion: { in: %w(new to_accept accepted confirmed to_reject rejected canceled withdrawn) } + validates :cfp_active, inclusion: { in: [true, false] } + validates :start_date, presence: true, if: :self_organized_and_accepted_or_confirmed? + validates :end_date, presence: true, if: :self_organized_and_accepted_or_confirmed? + validates :room, presence: true, if: :self_organized_and_accepted_or_confirmed? + validates :relevance, presence: true, if: :self_organized? + validates :description, presence: true, if: :self_organized? + validate :dates_within_conference_dates + validate :start_date_before_end_date + validate :valid_room + validate :overlapping before_validation :capitalize_color + scope :accepted, -> { where(state: 'accepted') } + scope :confirmed, -> { where(state: 'confirmed') } + scope :cfp_active, -> { where(cfp_active: true) } + scope :self_organized, -> { where.not(submitter: nil) } + + state_machine initial: :pending do + state :new + state :to_accept + state :accepted + state :confirmed + state :to_reject + state :rejected + state :canceled + state :withdrawn + + event :restart do + transitions to: :new, from: [:rejected, :withdrawn, :canceled] + end + event :to_accept do + transitions to: :to_accept, from: [:new, :to_reject] + end + event :accept do + transitions to: :accepted, from: [:new, :to_accept], on_transition: :create_organizer_role + end + event :confirm do + transitions to: :confirmed, from: [:accepted], on_transition: :assign_role_to_submitter + end + event :to_reject do + transitions to: :to_reject, from: [:new, :to_accept] + end + event :reject do + transitions to: :rejected, from: [:new, :to_reject] + end + event :cancel do + transitions to: :canceled, from: [:to_accept, :to_reject, :accepted, :confirmed], on_transition: :revoke_role_and_cleanup + end + event :withdraw do + transitions to: :withdrawn, from: [:new, :to_accept, :to_reject, :accepted, :confirmed], on_transition: :revoke_role_and_cleanup + end + end + + 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 + + def transition_possible?(transition) + self.class.state_machine.events_for(current_state).include?(transition) + end + + # Gives the role of the track_organizer to the submitter + def assign_role_to_submitter + submitter.add_role 'track_organizer', self + end + + ## + # Revokes the track organizer role, destroys the track's schedule, removes the + # track from events that have it set and reverts their state to new + # + def revoke_role_and_cleanup + role = Role.find_by(name: 'track_organizer', resource: self) + + if role + role.users.each do |user| + user.remove_role 'track_organizer', self + end + end + + self.selected_schedule_id = nil + save! + + schedules.each(&:destroy!) + + events.each do |event| + event.track = nil + event.state = 'new' + event.save! + end + end + + ## + # Checks if the track is accepted + # ====Returns + # * +true+ -> If the track's state is 'accepted' + # * +false+ -> If the track's state isn't 'accepted' + def accepted? + state == 'accepted' + end + + ## + # Checks if the track is confirmed + # ====Returns + # * +true+ -> If the track's state is 'confirmed' + # * +false+ -> If the track's state isn't 'confirmed' + def confirmed? + state == 'confirmed' + end + + ## + # Checks if a self-organized track is accepted or confirmed + # ====Returns + # * +true+ -> If the track's state is 'accepted' or 'confirmed' + # * +false+ -> If the track's state is neither 'accepted' nor 'confirmed' + def self_organized_and_accepted_or_confirmed? + self_organized? && (accepted? || confirmed?) + end + + def update_state(transition) + error = '' + + begin + send(transition) + rescue Transitions::InvalidTransition => e + error += "State update failed. #{e.message} " + end + + error += errors.full_messages.join(', ') unless save + error + end + private def generate_guid @@ -27,4 +188,51 @@ 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 + + ## + # Verify that the track's dates are between the conference's dates + # + def dates_within_conference_dates + return unless start_date && end_date && program.try(:conference).try(:start_date) && program.try(:conference).try(:end_date) + errors.add(:start_date, "can't be outside of the conference's dates (#{program.conference.start_date}-#{program.conference.end_date})") unless (program.conference.start_date..program.conference.end_date).cover?(start_date) + errors.add(:end_date, "can't be outside of the conference's dates (#{program.conference.start_date}-#{program.conference.end_date})") unless (program.conference.start_date..program.conference.end_date).cover?(end_date) + end + + ## + # Verify that the start date isn't after the end date + # + def start_date_before_end_date + return unless start_date && end_date + errors.add(:start_date, 'can\'t be after the end date') if start_date > end_date + end + + ## + # Verify that the room is a room of the conference + # + def valid_room + return unless room.try(:venue).try(:conference) && program.try(:conference) + errors.add(:room, "must be a room of #{program.conference.venue.name}") unless room.venue.conference == program.conference + end + + ## + # Check that there is no other track in the same room with overlapping dates + # + def overlapping + return unless start_date && end_date && room && program.try(:tracks) + (program.tracks.accepted + program.tracks.confirmed - [self]).each do |existing_track| + next unless existing_track.room == room && existing_track.start_date && existing_track.end_date + if start_date >= existing_track.start_date && start_date <= existing_track.end_date || + end_date >= existing_track.start_date && end_date <= existing_track.end_date || + start_date <= existing_track.start_date && end_date >= existing_track.end_date + errors.add(:track, 'has overlapping dates with a confirmed or accepted track in the same room') + break + end + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index a63d721e..231e1c11 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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) @@ -42,14 +47,26 @@ 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 :registrations, dependent: :destroy do + def for_conference conference + where(conference: conference).first + end + end has_many :events_registrations, through: :registrations has_many :ticket_purchases, dependent: :destroy has_many :payments, dependent: :destroy - has_many :tickets, through: :ticket_purchases, source: :ticket + has_many :tickets, through: :ticket_purchases, source: :ticket do + def for_registration conference + where(conference: conference, registration_ticket: true).first + end + end 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' + has_many :booth_requests + has_many :booth_requests, dependent: :destroy + has_many :booths, through: :booth_requests accepts_nested_attributes_for :roles scope :admin, -> { where(is_admin: true) } @@ -80,6 +97,12 @@ class User < ActiveRecord::Base event_registration.attended end + def mark_attendance_for_conference conference + registration = registrations.for_conference(conference) + registration.attended = true + registration.save + end + def name self[:name].blank? ? username : self[:name] end @@ -156,7 +179,11 @@ class User < ActiveRecord::Base def get_roles result = {} roles.each do |role| - resource = Conference.find(role.resource_id).short_title + resource = if role.resource_type == 'Conference' + Conference.find(role.resource_id).short_title + elsif role.resource_type == 'Track' + Track.find(role.resource_id).name + end if result[role.name].nil? result[role.name] = [resource] else diff --git a/app/pdfs/ticket_pdf.rb b/app/pdfs/ticket_pdf.rb new file mode 100644 index 00000000..9a09f9fe --- /dev/null +++ b/app/pdfs/ticket_pdf.rb @@ -0,0 +1,92 @@ +class TicketPdf < Prawn::Document + def initialize(conference, user, physical_ticket, ticket_layout, file_name) + super(page_layout: ticket_layout, page_size: 'A4', filename: file_name) + @user = user + @physical_ticket = physical_ticket + @conference = conference + + @left = bounds.left + @right = bounds.right + @mid_vertical = (bounds.top - bounds.bottom) / 2 + @mid_horizontal = (bounds.right - bounds.left) / 2 + @x = 0 + + draw_first_square + draw_second_square + draw_third_square + draw_fourth_square + end + + def draw_first_square + move_down @mid_vertical + dash(2, space: 1) + stroke_horizontal_rule + stroke_vertical_line bounds.top, bounds.bottom, at: @mid_horizontal + move_up @mid_vertical + draw_text 'TICKET HOLDER', at: [@x, cursor - 30], size: 17 + dash(2, space: 0) + bounding_box [@x, cursor - 50], width: 230, height: 150 do + pad(15) do + text_box 'NAME', at: [@x + 10, cursor], size: 13 + fill_color '808080' + text_box @user.name.to_s, at: [@x + 10, cursor - 20], size: 18 + fill_color '000000' + text_box 'EMAIL', at: [@x + 10, cursor - 60], size: 13 + fill_color '808080' + text_box @user.email.to_s, at: [@x + 10, cursor - 80], size: 18, overflow: :shrink_to_fit + fill_color '000000' + end + stroke_bounds + end + end + + def draw_second_square + move_up 150 + if @conference.picture? + if 7 * @conference.picture.image[:width] > 12 * @conference.picture.image[:height] + image "#{Rails.root}/public#{@conference.picture_url}", at: [@mid_horizontal + 30, cursor], width: 120 + else + image "#{Rails.root}/public#{@conference.picture_url}", at: [@mid_horizontal + 30, cursor], height: 70 + end + else + image "#{Rails.root}/public/img/osem-logo.png", at: [@mid_horizontal + 30, cursor], height: 70 + end + move_down 70 + draw_text @conference.title.to_s, at: [@mid_horizontal + 30, cursor - 30], size: 12 + draw_text @conference.organization.name.to_s, at: [@mid_horizontal + 30, cursor - 50], size: 12 + if @conference.venue + draw_text @conference.venue.name, at: [@mid_horizontal + 30, cursor - 70] + draw_text @conference.venue.street, at: [@mid_horizontal + 30, cursor - 90] + draw_text @conference.venue.city, at: [@mid_horizontal + 30, cursor - 110] + end + move_up 130 + move_down @mid_vertical + end + + def draw_third_square + draw_text 'EVENT', at: [@x, cursor - 40], size: 15 + fill_color '808080' + draw_text @conference.title.to_s, at: [@x, cursor - 60], size: 12 + draw_text @conference.start_date.strftime('%B %d, %Y').to_s, at: [@x, cursor - 80], size: 12 + move_down 80 + fill_color '000000' + draw_text 'TICKET', at: [@x, cursor - 30], size: 15 + fill_color '808080' + draw_text @physical_ticket.ticket.title.to_s, at: [@x, cursor - 50], size: 12 + move_down 50 + fill_color '000000' + draw_text 'TICKET REF.', at: [@x, cursor - 30], size: 15 + fill_color '808080' + draw_text @physical_ticket.ticket_purchase.id.to_s, at: [@x, cursor - 50], size: 12 + move_down 50 + fill_color '000000' + draw_text 'Powered By OSEM', at: [(@mid_horizontal - @left - 100) / 2, cursor - 100], size: 11 + move_up 180 + end + + def draw_fourth_square + x = @mid_horizontal + (@right - @mid_horizontal - 180) / 2 + y = cursor - (bounds.top - @mid_vertical - 180) / 2 + print_qr_code(@physical_ticket.token, pos: [x, y], extent: 180, stroke: false) + end +end diff --git a/app/serializers/conference_serializer.rb b/app/serializers/conference_serializer.rb index a7e0c1da..7bb96131 100644 --- a/app/serializers/conference_serializer.rb +++ b/app/serializers/conference_serializer.rb @@ -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 diff --git a/app/uploaders/picture_uploader.rb b/app/uploaders/picture_uploader.rb index af3c2227..b17f0d76 100644 --- a/app/uploaders/picture_uploader.rb +++ b/app/uploaders/picture_uploader.rb @@ -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] diff --git a/app/views/admin/booths/_change_state_dropdown.html.haml b/app/views/admin/booths/_change_state_dropdown.html.haml new file mode 100644 index 00000000..338e43f7 --- /dev/null +++ b/app/views/admin/booths/_change_state_dropdown.html.haml @@ -0,0 +1,49 @@ + +- if booth.transition_possible? :accept + - if can? :accept, booth + - if @conference.booth_limit > 0 + - confirm_message = ' You are able to accept '+ pluralize(@conference.booth_limit - (@conference.booths.accepted.count + @conference.booths.confirmed.count), 'more booth') + " (booth limit set to #{@conference.booth_limit}). Are you sure you want to accept this one?" + - if @conference.email_settings.send_on_booths_acceptance + - link = 'Accept with email' + - confirm_message = "By accepting this booth, an email will be sent informing the submitter for the acceptance. You may change the state to \'To accept\' until you are completely sure." + confirm_message + - else + - link = 'Accept booth' + %li= link_to link, + accept_admin_conference_booth_path(@conference.short_title, booth), + method: :patch ,id: "accept_booth_#{booth.id}", + data: (confirm_message ? { confirm: confirm_message } : nil) + +- if booth.transition_possible? :reject + - if @conference.email_settings.send_on_booths_rejection + - link = 'Reject with email' + - else + - link = 'Reject' + %li= link_to link, + reject_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "reject_booth_#{booth.id}", + data: (@conference.email_settings.send_on_booths_rejection ? { confirm: 'By rejecting this booth, an email will be sent informing the submitter about the rejection. You may change the state to \'To reject\' until you are completely sure.'} : nil) + +- if booth.transition_possible? :to_reject + %li= link_to 'To reject booth', + to_reject_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "to_reject_booth_#{booth.id}" + +- if booth.transition_possible? :restart + %li= link_to 'Start review', + restart_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "restart_booth_#{booth.id}" + +- if booth.transition_possible? :to_accept + %li= link_to 'To accept booth', + to_accept_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "to_accept_booth_#{booth.id}" + +- if booth.transition_possible? :cancel + %li= link_to 'Cancel booth', + cancel_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "cancel_booth_#{booth.id}" + +- if booth.transition_possible? :confirm + %li= link_to 'Confirm booth', + confirm_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "confirm_booth_#{booth.id}" diff --git a/app/views/admin/booths/edit.html.haml b/app/views/admin/booths/edit.html.haml new file mode 100644 index 00000000..0cac17e7 --- /dev/null +++ b/app/views/admin/booths/edit.html.haml @@ -0,0 +1,5 @@ +%h1 + Editing + = @booth.title + += render 'booths/form' diff --git a/app/views/admin/booths/index.html.haml b/app/views/admin/booths/index.html.haml new file mode 100644 index 00000000..428895e5 --- /dev/null +++ b/app/views/admin/booths/index.html.haml @@ -0,0 +1,78 @@ +.row + .col-md-12 + .page-header + %h1 + Booths + = "(#{@booths.length})" if @booths.any? + %p.text-muted + All the booth requests + +.row + .col-md-12 + %h4 + - if @conference.booth_limit == 0 + %p + Set the + = link_to 'Booth limit', edit_admin_conference_path(@conference.short_title) + to make sure you are not accepting more booths than you can accommodate. + - elsif !@conference.maximum_accepted_booths? + %p + You cannot accept more than + %b + = pluralize(@conference.booth_limit, 'booth') + ( + = pluralize(@conference.booths.accepted.count + @conference.booths.confirmed.count, 'accepted booth') + so far) + - else + %p + You have reached the maximum number of accepted booths. + ( + = link_to "#{@conference.booth_limit} booths", edit_admin_conference_path(@conference.short_title) + ) + - if @booths.any? + %table.table.table-striped.table-bordered.table-hover.datatable + %thead + %th + %b ID + %th + %b Logo + %th + %b Title + %th + %b Submitter + %th + %b Responsibles + %th + %b State + %th + %b Actions + - @booths.each do |booth| + %tr + %td + = booth.id + %td + - if booth.logo_link + = image_tag(booth.picture.thumb.url, width: '20%') + %td + = link_to booth.title, admin_conference_booth_path(@conference.short_title, booth) + %td + = link_to booth.submitter.name, admin_user_path(booth.submitter) if booth.submitter + %td + .responsibles + - booth.responsibles.each_with_index do |responsible, i| + = link_to responsible.name, admin_user_path(responsible) + = ", " unless i == booth.responsibles.length - 1 + %td + .btn-group + %button{ type: 'button', class: 'btn btn-link dropdown-toggle', 'data-toggle' => 'dropdown' } + = booth.state.humanize + %span.caret + %ul.dropdown-menu{ role: 'menu' } + = render 'change_state_dropdown', booth: booth + %td + = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, booth.id), + class: 'btn btn-primary' +.row + .col-md-12.text-right + - if can? :create, Booth + = link_to 'New Booth', new_admin_conference_booth_path(@conference.short_title), class: 'button btn btn-primary' diff --git a/app/views/admin/booths/new.html.haml b/app/views/admin/booths/new.html.haml new file mode 100644 index 00000000..99ec9b20 --- /dev/null +++ b/app/views/admin/booths/new.html.haml @@ -0,0 +1,3 @@ +%h1 New booth + += render 'booths/form' diff --git a/app/views/admin/booths/show.html.haml b/app/views/admin/booths/show.html.haml new file mode 100644 index 00000000..6ed823e5 --- /dev/null +++ b/app/views/admin/booths/show.html.haml @@ -0,0 +1,58 @@ +.row + .col-md-12 + %h3 + = image_tag(@booth.picture.thumb.url, size: '20%', alt: '') + = @booth.title + .btn-group.pull-right + = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, @booth), class: 'btn btn-mini btn-primary' + +.row + .col-md-12 + %table.table + %tr + %td.col-md-2 + %b Description + %td + = markdown(@booth.description) + %tr + %td.col-md-2 + %b Reasoning + %td + = markdown(@booth.reasoning) + %tr + %td.col-md-2 + %b Website + %td + - if @booth.website_url.present? + = link_to @booth.website_url, @booth.website_url + %tr + %td.col-md-2 + %b Submitter + %td + = link_to @booth.submitter.name, admin_user_path(@booth.submitter) + %tr + %td.col-md-2 + %b Submitter's relationship + %td + = @booth.submitter_relationship + %tr + %td.col-md-2 + %b Responsibles + %td + - @booth.responsibles.each_with_index do |responsibles, i| + .responsibles + = link_to responsibles.name, admin_user_path(responsibles) + ( + = responsibles.email + ) + = " , " unless i == @booth.responsibles.length - 1 + %tr + %td.col-md-2 + %b Submitted on + %td + = @booth.created_at + %tr + %td.col-md-2 + %b Last updated on + %td + = @booth.updated_at diff --git a/app/views/admin/cfps/_booths_cfp.html.haml b/app/views/admin/cfps/_booths_cfp.html.haml new file mode 100644 index 00000000..7cdc6db4 --- /dev/null +++ b/app/views/admin/cfps/_booths_cfp.html.haml @@ -0,0 +1,16 @@ +%dt + Type: +%dd + = @cfp.cfp_type.capitalize +%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') diff --git a/app/views/admin/cfps/_events_cfp.html.haml b/app/views/admin/cfps/_events_cfp.html.haml new file mode 100644 index 00000000..f06aa6ca --- /dev/null +++ b/app/views/admin/cfps/_events_cfp.html.haml @@ -0,0 +1,42 @@ +%dt + Type: +%dd + = @cfp.cfp_type.capitalize +%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 diff --git a/app/views/admin/cfps/_form.html.haml b/app/views/admin/cfps/_form.html.haml index e8110406..a02f3a02 100644 --- a/app/views/admin/cfps/_form.html.haml +++ b/app/views/admin/cfps/_form.html.haml @@ -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' } diff --git a/app/views/admin/cfps/_tracks_cfp.html.haml b/app/views/admin/cfps/_tracks_cfp.html.haml new file mode 100644 index 00000000..bcc2df43 --- /dev/null +++ b/app/views/admin/cfps/_tracks_cfp.html.haml @@ -0,0 +1,16 @@ +%dt + Type: +%dd + = @cfp.cfp_type.capitalize +%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') diff --git a/app/views/admin/cfps/index.html.haml b/app/views/admin/cfps/index.html.haml new file mode 100644 index 00000000..0067229c --- /dev/null +++ b/app/views/admin/cfps/index.html.haml @@ -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' diff --git a/app/views/admin/cfps/show.html.haml b/app/views/admin/cfps/show.html.haml index 09441508..e2a043ea 100644 --- a/app/views/admin/cfps/show.html.haml +++ b/app/views/admin/cfps/show.html.haml @@ -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 diff --git a/app/views/admin/conferences/_doughnut_chart.html.haml b/app/views/admin/conferences/_doughnut_chart.html.haml index c8ef55fc..f4a8f0ba 100644 --- a/app/views/admin/conferences/_doughnut_chart.html.haml +++ b/app/views/admin/conferences/_doughnut_chart.html.haml @@ -1,6 +1,23 @@ .text-center %h4 #{title} - %canvas.doughnut_chart{ 'data-chart' => data.to_json } + %canvas.doughnut_chart{ id: "dough_#{title}", 'data-chart' => data.to_json } - if data - data.each do |key, value| %span{ 'style' => "border-bottom: 3px solid #{value['color']}" } #{key}: #{value['value']} + +:javascript + $(document).ready( function(){ + + var d = $("#dough_#{title}"); + var dt = d.get(0).getContext('2d'); + + $(window).resize( respondCanvas ); + + function respondCanvas(){ + dt.canvas.width = 150; + dt.canvas.height = 150; + } + + respondCanvas(); + + }); diff --git a/app/views/admin/conferences/_line_chart.html.haml b/app/views/admin/conferences/_line_chart.html.haml index cd49df10..12d825e7 100644 --- a/app/views/admin/conferences/_line_chart.html.haml +++ b/app/views/admin/conferences/_line_chart.html.haml @@ -27,3 +27,21 @@ %span{ 'style' => "border-bottom: 3px solid #{conference[:color]};", 'data-chart' => "#{name}" } %input{ 'type' => 'checkbox', 'name' => "#{conference[:short_title]}" } #{conference[:short_title]} + +:javascript + $(document).ready( function(){ + + var c = $("#line_chart_#{name}"); + var ct = c.get(0).getContext('2d'); + var container = $(c).parent(); + + $(window).resize( respondCanvas ); + + function respondCanvas(){ + c.attr('width', $(container).width() ); + c.attr('height', $(container).height() ); + } + + respondCanvas(); + + }); diff --git a/app/views/admin/conferences/_todo_list.html.haml b/app/views/admin/conferences/_todo_list.html.haml index cd98391d..88bb7c36 100644 --- a/app/views/admin/conferences/_todo_list.html.haml +++ b/app/views/admin/conferences/_todo_list.html.haml @@ -1,12 +1,12 @@ .list-group - %li.list-group-item + %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)}" } %h4 Conference progress .progress .progress-bar{ 'role' => 'progressbar', 'aria-valuenow' => "#{conference_progress['process']}", 'aria-valuemin' => '0', 'aria-valuemax' => '100', 'style' => "width: #{conference_progress['process']}%;" } = conference_progress['process'] + '%' - %li{ 'class' => "list-group-item #{class_for_todo(conference_progress['registration'])}" } + %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['registration'])}" } %span{ 'class' => icon_for_todo(conference_progress['registration']) } - if can? :update, @conference - if conference.registration_period @@ -15,13 +15,13 @@ = link_to 'Set up registration period', new_admin_conference_registration_period_path(conference_progress['short_title']) - else Set up registration period - %li{ 'class' => "list-group-item #{class_for_todo(conference_progress['cfp'])}" } + %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['cfp'])}" } %span{ 'class' => icon_for_todo(conference_progress['cfp']) } - if can? :update, Cfp.new(program_id: @program.id) - = link_to 'Set up call for papers', admin_conference_program_cfp_path(conference_progress['short_title']) + = link_to 'Set up call for papers', admin_conference_program_cfps_path(conference_progress['short_title']) - else Set up call for papers - %li{'class'=>"list-group-item #{class_for_todo(conference_progress['venue'])}"} + %li{'class'=>"list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['venue'])}"} %span{'class'=>icon_for_todo(conference_progress['venue'])} - if can? :update, Venue.new(conference: @conference) - @conference.reload @@ -32,31 +32,31 @@ - else - @conference.reload Add venue - %li{ 'class' => "list-group-item #{class_for_todo(conference_progress['rooms'])}" } + %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['rooms'])}" } %span{ 'class' => icon_for_todo(conference_progress['rooms']) } - if @conference.venue && (can? :update, @conference.venue.rooms.build) = link_to 'Add rooms', admin_conference_venue_rooms_path(conference_progress['short_title']) - else Add rooms - %li{ 'class' => "list-group-item #{class_for_todo(conference_progress['tracks'])}" } + %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['tracks'])}" } %span{ 'class' => icon_for_todo(conference_progress['tracks']) } - if can? :update, @conference.program.tracks.build = link_to 'Add tracks', admin_conference_program_tracks_path(conference_progress['short_title']) - else Add tracks - %li{ 'class' => "list-group-item #{class_for_todo(conference_progress['event_types'])}" } + %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['event_types'])}" } %span{ 'class' => icon_for_todo(conference_progress['event_types']) } - if can? :update, @conference.program.event_types.build = link_to 'Add event types', admin_conference_program_event_types_path(conference_progress['short_title']) - else Add event types - %li{ 'class' => "list-group-item #{class_for_todo(conference_progress['difficulty_levels'])}" } + %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['difficulty_levels'])}" } %span{ 'class' => icon_for_todo(conference_progress['difficulty_levels']) } - if can? :update, @conference.program.difficulty_levels.build = link_to 'Add difficulty levels', admin_conference_program_difficulty_levels_path(conference_progress['short_title']) - else Add difficulty levels - %li{ class: "list-group-item #{class_for_todo(conference_progress['splashpage'])}" } + %li{ class: "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['splashpage'])}" } %span{ 'class' => icon_for_todo(conference_progress['splashpage']) } - if can? :update, @conference = link_to 'Set up a Splashpage', admin_conference_splashpage_path(conference_progress['short_title']) diff --git a/app/views/admin/conferences/edit.html.haml b/app/views/admin/conferences/edit.html.haml index 5aa717ea..26ae198f 100644 --- a/app/views/admin/conferences/edit.html.haml +++ b/app/views/admin/conferences/edit.html.haml @@ -17,6 +17,7 @@ = image_tag @conference.picture.thumb.url = f.input :picture, label: false, hint: 'This will be displayed on the front page.' = f.hidden_field :picture_cache + = f.input :ticket_layout, as: :select, collection: Conference.ticket_layouts.keys, hint: "Layout type for tickets of the conference." = f.inputs name: 'Scheduling' do = f.input :timezone, as: :time_zone, hint: 'The conference time zone' = f.input :start_date, as: :string, input_html: { id: 'conference-start-datepicker', readonly: 'readonly' } @@ -25,4 +26,7 @@ = f.input :end_hour, input_html: {size: 2, type: 'number', min: 1, max: 24} = f.inputs name: 'Registrations' do = f.input :registration_limit, as: :number, in: 0..9999, hint: 'Limit the number of registrations to the conference (0 no limit). Please note that the registration limit doesn\'t apply to speakers of confirmed events (they will still be able to register even if it has been reached). You currently have ' + pluralize(@conference.registrations.count, 'registration') + = f.inputs name: 'Booths' do + = f.input :booth_limit, as: :number, in: 0..9999, + hint: 'Booth limit is the maximum number of booths that you can accept for this conference. By setting this number (0 no limit) you can be sure that you are not going to accept more booths than the conference can accommodate. You currently have ' + pluralize(@conference.booths.accepted.count, 'accepted booth') +'.' = f.action :submit, as: :button, button_html: {class: 'btn btn-primary'} diff --git a/app/views/admin/conferences/index.html.haml b/app/views/admin/conferences/index.html.haml index 24c2d0f2..02208aeb 100644 --- a/app/views/admin/conferences/index.html.haml +++ b/app/views/admin/conferences/index.html.haml @@ -60,6 +60,17 @@ unit: 'weeks' } .col-md-4 = render partial: 'doughnut_chart', locals: { title: 'User', data: @user_distribution } +.row#tickets + .col-md-8 + = render partial: 'line_chart', locals: { title: 'Tickets sold over time', + name: 'tickets', + conferences: @conferences, + active_conferences: @active_conferences, + deactive_conferences: @deactive_conferences, + y: @tickets, + x: @ticket_weeks, + unit: 'weeks' } +%br .row .col-md-8 %ul.nav.nav-tabs#recentTable diff --git a/app/views/admin/conferences/new.html.haml b/app/views/admin/conferences/new.html.haml index 014758de..9e115457 100644 --- a/app/views/admin/conferences/new.html.haml +++ b/app/views/admin/conferences/new.html.haml @@ -2,6 +2,7 @@ .col-md-8 = semantic_form_for(@conference, url: admin_conferences_path) do |f| = f.inputs 'Basic Information' do + = f.input :organization, as: :select, collection: @organizations = f.input :title, hint: "The name of your conference as it shall appear throughout the site. Example: 'OpenSUSE Conference 2013'", input_html: { required: 'required' } = f.input :short_title, hint: "A short and unique handle for your conference, using only letters, numbers, underscores, and dashes. This will be used to identify your conference in URLs etc. Example: 'froscon2011'", diff --git a/app/views/admin/conferences/show.html.haml b/app/views/admin/conferences/show.html.haml index dec58da8..cddae4c2 100644 --- a/app/views/admin/conferences/show.html.haml +++ b/app/views/admin/conferences/show.html.haml @@ -69,6 +69,17 @@ .col-md-4 = render partial: 'todo_list', locals: { conference_progress: @conference_progress, conference: @conference } + .row#tickets + .col-md-8 + = render partial: 'line_chart', locals: { title: 'Tickets sold over time', + name: 'tickets', + conferences: @tickets, + active_conferences: @tickets, + deactive_conferences: [], + y: @tickets_data, + x: @ticket_weeks, + unit: 'weeks' } +%br .row .col-md-12#doughnut %ul.nav.nav-tabs#doughnut_tabs @@ -101,11 +112,11 @@ .row .col-md-8 %ul.nav.nav-tabs#recentTable - %li.active + %li{ 'class' => "active #{hidden_if_conference_over(@conference)}" } %a{ href: '#recent_reg', 'data-toggle' => 'tab' } %span.fa.fa-user Recent Registrations - %li + %li{ 'class' => "#{hidden_if_conference_over(@conference)}" } %a{ href: '#recent_submissions', 'data-toggle' => 'tab' } %span.fa.fa-file-text Recent Submissions diff --git a/app/views/admin/emails/_help.html.haml b/app/views/admin/emails/_help.html.haml index 535cd097..371bac7b 100644 --- a/app/views/admin/emails/_help.html.haml +++ b/app/views/admin/emails/_help.html.haml @@ -55,4 +55,10 @@ %tr %td {conference_splash_link} %td The link to conference splash page - + - if @conference.booths + %tr + %td {submitter_name} + %td Submitter's name + %tr + %td {booth_title} + %td Booth's title diff --git a/app/views/admin/emails/index.html.haml b/app/views/admin/emails/index.html.haml index 4b380b86..83ebe210 100644 --- a/app/views/admin/emails/index.html.haml +++ b/app/views/admin/emails/index.html.haml @@ -14,6 +14,8 @@ %a{ 'aria-controls' => 'notifications', 'data-toggle' => 'tab', href: '#notifications', role: 'tab' } Update Notifications %li{ role: 'presentation' } %a{ 'aria-controls' => 'cfp', 'data-toggle' => 'tab', href: '#cfp', role: 'tab' } Call for Papers + %li{ role: 'presentation' } + %a{ 'aria-controls' => 'booths', 'data-toggle' => 'tab', href: '#booth', role: 'tab' } Booth / Tab panes .tab-content #onboarding.tab-pane.active{ role: 'tabpanel' } @@ -100,6 +102,26 @@ 'data-body-text' => "Dear {name},\n\nThe Conference Call for Papers Details of {conference} has changed.\nNew Dates : {cfp_start_date} - {cfp_end_date}.\n Link to Schedule {schedule_link} \n\nBest wishes\n\n{conference} Team" } Load Template %a.btn.btn-link.control_label.template_help_link{ 'data-name' => 'updated_cfp_help' } Show Help = render partial: 'help', locals: {id: 'updated_cfp_help', show_event_variables: false} + #booth.tab-pane{ role: 'tabpanel' } + = f.input :send_on_booths_acceptance + = f.input :booths_acceptance_subject + = f.input :booths_acceptance_body, input_html: { rows:10, cols: 20 } + %a.btn.btn-link.control_label.load_template{ 'data-subject-input-id' => 'email_settings_booths_acceptance_subject', + 'data-subject-text' => 'Your booth has been accepted!', + 'data-body-input-id' => 'email_settings_booths_acceptance_body', + 'data-body-text' => "Dear {name},\n\nWe are pleased to inform you that your booth request {booth_title} has been accepted for the conference {conference}.\nPlease click the confirm button to let us know you can make it as soon as possible!\n\nFeel free to contact us with any questions or concerns.\n\nWe are looking forward to seeing you there.\n\nBest wishes\n\n{conference} Team"} Load Template + %a.btn.btn-link.control_label.template_help_link{ 'data-name' => 'booth_acceptance_help' } Show help + = render partial: 'help', locals: {id: 'booth_acceptance_help', show_event_variables: false} + = f.input :send_on_booths_rejection + = f.input :booths_rejection_subject + = f.input :booths_rejection_body, input_html: { rows:10, cols:20 } + %a.btn.btn-link.control_label.load_template{ 'data-subject-input-id' => 'email_settings_booths_rejection_subject', + 'data-subject-text' => 'Your booth request has been rejected', + 'data-body-input-id' => 'email_settings_booths_rejection_body', + 'data-body-text' => "Dear {name},\n\nThank you for your booth request {booth_title} for the conference {conference}.\n\nUnfortunately, we are sorry to inform you that your request has been rejected.\n\n\nBest wishes\n\n{conference} Team" } Load Template + %a.btn.btn-link.control_label.template_help_link{ 'data-name' => 'booth_rejection_help' } Show help + = render partial: 'help', locals: {id: 'booth_rejection_help', show_event_variables: false} + .row .col-md-12 = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/events/_nested_comments.html.haml b/app/views/admin/events/_nested_comments.html.haml index 8ab3cabb..0bfb440c 100644 --- a/app/views/admin/events/_nested_comments.html.haml +++ b/app/views/admin/events/_nested_comments.html.haml @@ -6,9 +6,9 @@ %div %a.pull-right.comment-reply-link{ href: '#' } Reply .comment-reply - = semantic_form_for :comment, url: '#{comment_admin_conference_program_event_path(@conference.short_title, comment.commentable_id)}', method: :post do |f| + = semantic_form_for :comment, url: comment_admin_conference_program_event_path(@conference.short_title, comment.commentable_id), method: :post do |f| = f.input :body - %input{ name: 'parent', type: 'hidden', value: '#{comment.id}' } + %input{ name: 'parent', type: 'hidden', value: comment.id } %input{ name: 'authenticity_token', type: 'hidden', value: '#{form_authenticity_token}' } %button.btn.btn-primary.pull-right{ name: 'button', type: 'submit' } Add Reply - comment.children.each do |child| diff --git a/app/views/admin/events/index.html.haml b/app/views/admin/events/index.html.haml index 0e204c87..f8b58b63 100644 --- a/app/views/admin/events/index.html.haml +++ b/app/views/admin/events/index.html.haml @@ -9,13 +9,14 @@ =link_to 'Add Event', new_admin_conference_program_event_path(@conference.short_title), class: 'button btn btn-default btn-info' - if can? :read, Event .btn-group - %button.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown', type: 'button', class: 'btn btn-success' } - Export PDF - %span.caret - %ul.dropdown-menu{ role: 'menu' } - %li= link_to 'All Events', admin_conference_program_events_path(@conference.short_title, format: :pdf, event_export_option: 'all') - %li= link_to 'Confirmed Events', admin_conference_program_events_path(@conference.short_title, format: :pdf, event_export_option: 'confirmed') - %li= link_to 'All Events with Comments', admin_conference_program_events_path(@conference.short_title, format: :pdf, event_export_option: 'all_with_comments') + .btn-group + %button.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown', type: 'button', class: 'btn btn-success' } + Export PDF + %span.caret + %ul.dropdown-menu{ role: 'menu' } + %li= link_to 'All Events', admin_conference_program_events_path(@conference.short_title, format: :pdf, event_export_option: 'all') + %li= link_to 'Confirmed Events', admin_conference_program_events_path(@conference.short_title, format: :pdf, event_export_option: 'confirmed') + %li= link_to 'All Events with Comments', admin_conference_program_events_path(@conference.short_title, format: :pdf, event_export_option: 'all_with_comments') .btn-group %button.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown', type: 'button', class: 'btn btn-success' } Export CSV @@ -184,4 +185,4 @@ %ul.dropdown-menu{ role: 'menu' } = render 'change_state_dropdown', event: event %td.text-center - = link_to "#{event.comment_threads.count}", admin_conference_program_event_path(@conference.short_title, event), anchor: 'comments-div' + = link_to "#{event.comments_count}", admin_conference_program_event_path(@conference.short_title, event), anchor: 'comments-div' diff --git a/app/views/admin/organizations/_form.html.haml b/app/views/admin/organizations/_form.html.haml new file mode 100644 index 00000000..c83facbb --- /dev/null +++ b/app/views/admin/organizations/_form.html.haml @@ -0,0 +1,13 @@ += semantic_form_for(@organization, url: (@organization.new_record? ? admin_organizations_path : admin_organization_path(@organization))) do |f| + = f.inputs name: 'Organization details' do + = f.input :name, as: :string, required: true + = f.input :description, as: :text, input_html: { rows: 10 }, placeholder: 'Decribe about your organization..' + = image_tag f.object.picture.thumb.url if f.object.picture? + - if @organization.picture + = image_tag(@organization.picture.thumb.url, width: '20%') + = f.input :picture + %p.text-right + - if @organization.new_record? + = f.submit 'Create Organization', class: 'btn btn-success' + - else + = f.submit 'Update Organization', class: 'btn btn-success' diff --git a/app/views/admin/organizations/_users_with_org_admin_role.haml b/app/views/admin/organizations/_users_with_org_admin_role.haml new file mode 100644 index 00000000..e107868c --- /dev/null +++ b/app/views/admin/organizations/_users_with_org_admin_role.haml @@ -0,0 +1,25 @@ +.page-header + %h3 Users (#{users.length}) +- if users.present? + %table.table.table-striped.table-bordered.table-hover.datatable#users + %thead + %th Name + %th Email + - if ( can? :unassign_org_admins, organization ) + %th + Actions + %tbody + - users.each do |user| + %tr + %td= user.name + %td= user.email + - if ( can? :unassign_org_admins, organization ) + %td + = link_to 'Remove from organization admin', + unassign_org_admins_admin_organization_path(organization.id, + role.name, + user: {email: user.email}), + method: :delete, + class: 'btn btn-danger' +- else + %h5 No users found! diff --git a/app/views/admin/organizations/index.html.haml b/app/views/admin/organizations/index.html.haml new file mode 100644 index 00000000..baf9bc80 --- /dev/null +++ b/app/views/admin/organizations/index.html.haml @@ -0,0 +1,33 @@ +.row + .col-md-12 + .page-header + %h1 Organizations + .btn-group.pull-right + = link_to 'Create Organization', new_admin_organization_path, class: 'btn btn-success pull-right' + %p.text-muted + Manage organizations in OSEM +.row + .col-md-12 + %table.table.table-hover.datatable + %thead + %th Name + %th Upcoming Conferences + %th Past Conferences + %th Actions + %tbody + - @organizations.each do |organization| + %tr + %td + = organization.name + %td + = organization.conferences.upcoming.count + %td + = organization.conferences.past.count + %td + .btn-group + = link_to 'Admins', admins_admin_organization_path(organization), + method: :get, class: 'btn btn-success' + = link_to 'Edit', edit_admin_organization_path(organization), + method: :get, class: 'btn btn-primary' + = link_to 'Delete', admin_organization_path(organization), + method: :delete, class: 'btn btn-danger', data: { confirm: "Warning: This will delete #{organization.name} and all its data which includes data for all conferences within #{organization.name}. Do you really want to continue?" } diff --git a/app/views/admin/organizations/show_org_admins.haml b/app/views/admin/organizations/show_org_admins.haml new file mode 100644 index 00000000..7fe1f943 --- /dev/null +++ b/app/views/admin/organizations/show_org_admins.haml @@ -0,0 +1,24 @@ +.row + .col-md-12 + .page-header + %h2 + Organization admins for #{@organization.name} + .text-muted + = @role.description + +.row.col-md-3 + - if ( can? :assign_org_admins, @organization ) + = semantic_form_for :user, + url: assign_org_admins_admin_organization_path(@organization, + @role.name), method: :post do |u| + + = u.label 'Add user by email: ' + .input-group + = u.input :email, label: false, placeholder: "User's email" + .input-group-btn + = u.submit 'Add', id: 'user-add', class: 'btn btn-primary' + +.row + .col-md-12 + = render partial: 'users_with_org_admin_role', + locals: { users: @users, organization: @organization, role: @role } diff --git a/app/views/admin/physical_tickets/_physical_ticket.html.haml b/app/views/admin/physical_tickets/_physical_ticket.html.haml new file mode 100644 index 00000000..ff4dd185 --- /dev/null +++ b/app/views/admin/physical_tickets/_physical_ticket.html.haml @@ -0,0 +1,16 @@ +%tr + %td= physical_ticket.id + %td= physical_ticket.ticket.title + %td= physical_ticket.user.email + %td= humanized_money_with_symbol physical_ticket.ticket_purchase.amount_paid + %td + .btn-group + = link_to 'Show', + conference_physical_ticket_path(conference.short_title, + physical_ticket.token), + class: 'btn btn-primary' + = link_to 'Generate PDF', + conference_physical_ticket_path(conference.short_title, + physical_ticket.token, + format: :pdf), + class: 'button btn btn-default btn-info' diff --git a/app/views/admin/physical_tickets/index.html.haml b/app/views/admin/physical_tickets/index.html.haml new file mode 100644 index 00000000..8b26a965 --- /dev/null +++ b/app/views/admin/physical_tickets/index.html.haml @@ -0,0 +1,31 @@ +.container + .row + .col-md-12.page-header + %h2 + Tickets Sold + .text-muted + Tickets sold for the conference + .row + .col-md-4 + = render partial: 'admin/conferences/doughnut_chart', + locals: { title: 'Tickets sold', data: @tickets_sold_distribution } + .col-md-4 + = render partial: 'admin/conferences/doughnut_chart', + locals: {title: 'Tickets turnover', data: @tickets_turnover_distribution} + %br + - if @physical_tickets.any? + .row + .col-md-12 + %table.table.table-hover.datatable#tickets + %thead + %th ID + %th Type + %th User + %th Paid + %th Actions + %tbody + - @physical_tickets.each do |physical_ticket| + = render "physical_ticket", physical_ticket: physical_ticket, + conference: @conference + - else + %h5 No Tickets sold! diff --git a/app/views/admin/registration_periods/show.html.haml b/app/views/admin/registration_periods/show.html.haml index 9ef02e37..e923d089 100644 --- a/app/views/admin/registration_periods/show.html.haml +++ b/app/views/admin/registration_periods/show.html.haml @@ -25,5 +25,12 @@ = link_to 'Delete', admin_conference_registration_period_path, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger' - else - - if can? :create, @conference - = link_to 'New Registration Period', new_admin_conference_registration_period_path, class: 'btn btn-primary' + - unless @conference.tickets.for_registration.empty? + - if can? :create, @conference.build_registration_period + = link_to 'New Registration Period', new_admin_conference_registration_period_path, class: 'btn btn-primary' + - else + .h3.text-left + No Registration Tickets! + %small + = link_to 'Create registration tickets', new_admin_conference_ticket_path + before creating the registration period. diff --git a/app/views/admin/registrations/index.html.haml b/app/views/admin/registrations/index.html.haml index 0e7035c0..a51d41fc 100644 --- a/app/views/admin/registrations/index.html.haml +++ b/app/views/admin/registrations/index.html.haml @@ -56,7 +56,7 @@ -if @conference.questions.any? %td = link_to 'Questions','#', class: 'btn btn-success question-btn', 'data-id' => index, 'data-name' => registration.name - %td + %td{ 'data-order' => registration.attended.to_s } = check_box_tag "#{@conference.short_title}_#{registration.id}", registration.id, registration.attended, class: 'switch-checkbox', method: :patch, url: toggle_attendance_admin_conference_registration_path(@conference.short_title, id: registration.id)+"?attended=", diff --git a/app/views/admin/reports/_missing_speakers.html.haml b/app/views/admin/reports/_missing_speakers.html.haml index b269eef6..bc1769ae 100644 --- a/app/views/admin/reports/_missing_speakers.html.haml +++ b/app/views/admin/reports/_missing_speakers.html.haml @@ -3,7 +3,7 @@ .page-header %h1 Missing Speakers - = "(#{@missing_event_speakers.group(:user_id).length})" + = "(#{@missing_event_speakers.distinct(:user_id).length})" %p.text-muted All event speakers who haven't checked in .col-md-12 diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index 704ffdaa..6bbb8886 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -17,7 +17,7 @@ %a{href: '#missing-speakers', 'data-toggle' => 'tab'} Missing Speakers %span.label.label-danger{style: 'border-radius: 1em;'} - = @missing_event_speakers.group(:user_id).length + = @missing_event_speakers.distinct(:user_id).length .tab-content #all.tab-pane.active diff --git a/app/views/admin/roles/_form.html.haml b/app/views/admin/roles/_form.html.haml index eb10b23f..2204ef89 100644 --- a/app/views/admin/roles/_form.html.haml +++ b/app/views/admin/roles/_form.html.haml @@ -7,7 +7,7 @@ .text-muted = @role.description -= semantic_form_for @role, url: admin_conference_role_path(@conference.short_title, @role.name) do |f| += semantic_form_for @role, url: @url do |f| .row .col-md-5 = f.input :description diff --git a/app/views/admin/roles/_users.html.haml b/app/views/admin/roles/_users.html.haml index 9d73421b..f32c9a99 100644 --- a/app/views/admin/roles/_users.html.haml +++ b/app/views/admin/roles/_users.html.haml @@ -14,7 +14,7 @@ - if ( can? :toggle_user, @role ) %td.text-right = hidden_field_tag "role[user_ids][]", nil - = check_box_tag @conference.short_title, @role.id, (@role.user_ids.include? user.id), method: :post, url: "/admin/conferences/#{@conference.short_title}/roles/#{@role.name}/toggle_user?user[email]=#{user.email}&user[state]=", class: 'switch-checkbox', data: { size: 'small', off_color: 'warning', on_text: 'Yes', off_text: 'No' } + = check_box_tag @conference.short_title, @role.id, (@role.user_ids.include? user.id), method: :post, url: "#{@url}?user[email]=#{user.email}&user[state]=", class: 'switch-checkbox', data: { size: 'small', off_color: 'warning', on_text: 'Yes', off_text: 'No' } %td= user.id %td= user.name %td= user.email diff --git a/app/views/admin/roles/index.html.haml b/app/views/admin/roles/index.html.haml index c6e4c773..4990673e 100644 --- a/app/views/admin/roles/index.html.haml +++ b/app/views/admin/roles/index.html.haml @@ -19,13 +19,26 @@ %tr %td= role.id %td= role.name.titleize - %td= role.description + %td + = role.description + - if role.resource_type == 'Track' + = link_to role.resource.name, admin_conference_program_track_path(@conference.short_title, role.resource) %td = role.users.pluck(:name).first(5).join ', ' - if role.users.count > 5 - = link_to '...', admin_conference_role_path(@conference.short_title, role.name) + - if role.resource_type == 'Track' + = link_to '...', admin_conference_program_track_role_path(@conference.short_title, role.resource, role.name) + - else + = link_to '...', admin_conference_role_path(@conference.short_title, role.name) %td .btn-group - = link_to 'Users', admin_conference_role_path(@conference.short_title, role.name), class: 'btn btn-success' - - if can? :edit, role - = link_to 'Edit', edit_admin_conference_role_path(@conference.short_title, role.name), class: 'btn btn-primary' + - if role.resource_type == 'Track' + = link_to 'Users', admin_conference_program_track_role_path(@conference.short_title, role.resource, role.name), + class: 'btn btn-success' + - if can? :edit, role + = link_to 'Edit', edit_admin_conference_program_track_role_path(@conference.short_title, role.resource, role.name), + class: 'btn btn-primary' + - else + = link_to 'Users', admin_conference_role_path(@conference.short_title, role.name), class: 'btn btn-success' + - if can? :edit, role + = link_to 'Edit', edit_admin_conference_role_path(@conference.short_title, role.name), class: 'btn btn-primary' diff --git a/app/views/admin/roles/show.html.haml b/app/views/admin/roles/show.html.haml index 69d02f6b..b264bc5f 100644 --- a/app/views/admin/roles/show.html.haml +++ b/app/views/admin/roles/show.html.haml @@ -6,14 +6,21 @@ Role = @role.name.titleize - if can? :edit, @role - = link_to 'Edit', edit_admin_conference_role_path(@conference.short_title, @role.name), class: 'btn btn-primary pull-right' + - if @track + = link_to 'Edit', edit_admin_conference_program_track_role_path(@conference.short_title, @track, @role.name), + class: 'btn btn-primary pull-right' + - else + = link_to 'Edit', edit_admin_conference_role_path(@conference.short_title, @role.name), class: 'btn btn-primary pull-right' .text-muted = @role.description + - if @track + = link_to @track.name, admin_conference_program_track_path(@conference.short_title, @track) + .row.col-md-3 - if ( can? :toggle_user, @role ) && !@role.new_record? - = semantic_form_for :user, url: toggle_user_admin_conference_role_path(@conference.short_title, @role.name), method: :post do |u| + = semantic_form_for :user, url: @url, method: :post do |u| = u.label 'Add user by email: ' .input-group diff --git a/app/views/admin/rooms/_form.html.haml b/app/views/admin/rooms/_form.html.haml index 8e515290..eec272cd 100644 --- a/app/views/admin/rooms/_form.html.haml +++ b/app/views/admin/rooms/_form.html.haml @@ -10,6 +10,6 @@ .col-md-8 = semantic_form_for(@room, url: (@room.new_record? ? admin_conference_venue_rooms_path : admin_conference_venue_room_path(@conference.short_title, @room))) do |f| = f.input :name, input_html: { autofocus: true} - = f.input :size, input_html: {size: 5} + = f.input :size, label: 'Capacity', input_html: {size: 5} %p.text-right = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/rooms/index.html.haml b/app/views/admin/rooms/index.html.haml index 8bf682c8..2d1545f6 100644 --- a/app/views/admin/rooms/index.html.haml +++ b/app/views/admin/rooms/index.html.haml @@ -11,7 +11,7 @@ %table.table.table-hover#rooms %thead %th Name - %th Size + %th Capacity %th Actions %tbody - @rooms.each_with_index do |room, index| diff --git a/app/views/admin/schedules/_day_tab.html.haml b/app/views/admin/schedules/_day_tab.html.haml index 7bfe3ec6..8a1dff60 100644 --- a/app/views/admin/schedules/_day_tab.html.haml +++ b/app/views/admin/schedules/_day_tab.html.haml @@ -5,7 +5,9 @@ - date_event_schedules = @event_schedules.select{ |e| e.start_time.to_date.eql? date } .row - @rooms.each do |room| - .col-md-2.col-xs-6 + - non_schedulable = room.tracks.self_organized.confirmed.any? do |track| + - !track.schedules.include?(@schedule) && (track.start_date..track.end_date).include?(date) + .col-md-2.col-xs-6{ class: ('non_schedulable' if non_schedulable) } .room-name - room_date_event_schedules = date_event_schedules.select{ |e| e.room == room } = room.name diff --git a/app/views/admin/schedules/_event.html.haml b/app/views/admin/schedules/_event.html.haml index 3a13323b..ff6fd0c3 100644 --- a/app/views/admin/schedules/_event.html.haml +++ b/app/views/admin/schedules/_event.html.haml @@ -7,12 +7,13 @@ / subtracting the padding before calculate the number of lines - lines = (height - 7) / 23 - color = event.track.try(:color).present? ? event.track.try(:color) : 'FFFFFF' +- non_schedulable = event_schedule_id && (EventSchedule.find(event_schedule_id).schedule != @schedule) .schedule-event{ style: "height: #{height}px; background-color: #{color}; color: #{contrast_color(color)}", | id: "event-#{event.id}", | event_id: event.id, | length: cells_length, | event_schedule_id: event_schedule_id, | - class: ('compact' if compact_grid) } + class: "#{'compact' if compact_grid} #{'non_schedulable' if non_schedulable}" } .schedule-event-text{ style: "-webkit-line-clamp: #{lines}; height: #{lines * 23}px;"} %span.schedule-event-delete-button{ onclick: "Schedule.remove(\'event-#{event.id}\');" } X = event.title diff --git a/app/views/admin/schedules/_form.html.haml b/app/views/admin/schedules/_form.html.haml new file mode 100644 index 00000000..8c6c091d --- /dev/null +++ b/app/views/admin/schedules/_form.html.haml @@ -0,0 +1,10 @@ +.row + .col-md-12 + .page-header + %h1 + New Track Schedule +.row + .col-md-12 + = semantic_form_for @schedule, url: admin_conference_schedules_path(@conference.short_title) do |f| + = f.input :track, collection: Track.accessible_by(current_ability).where(program: @program).self_organized.confirmed.pluck(:name, :id), include_blank: false + = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/schedules/index.html.haml b/app/views/admin/schedules/index.html.haml index acfc79a8..29c1910e 100644 --- a/app/views/admin/schedules/index.html.haml +++ b/app/views/admin/schedules/index.html.haml @@ -4,42 +4,85 @@ %h1 Schedules %p.text-muted The schedules for your conference -.row - .col-md-12 - %table.table.table-hover#event_types - %thead - %th Schedule - %th Selected - %th Actions - %tbody - - @schedules.each do |schedule| - %tr - %td - Schedule - = schedule.id - %td - = selected_scheduled?(schedule) - %td - .btn-group{role: "group"} - = link_to 'Show', admin_conference_schedule_path(@conference.short_title, schedule.id), - method: :get, class: 'btn btn-primary' - = link_to 'Delete', admin_conference_schedule_path(@conference.short_title, schedule.id), - method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete Schedule #{schedule.id}?" } -.row - .col-md-12 - - if @venue.try(:rooms).present? - .text-right - = link_to 'Add Schedule', admin_conference_schedules_path(@conference.short_title), - method: :post, class: 'btn btn-primary' - - elsif @venue - .h3 - No Rooms! - %small - = link_to 'Create rooms', admin_conference_venue_rooms_path - before creating the schedule. - - else - .h3 - No Venue! - %small - = link_to 'Create a venue with rooms', new_admin_conference_venue_path - before creating the schedule. +.tabbable + %ul.nav.nav-tabs + %li.active + = link_to 'Conference schedules', '#conference', 'data-toggle' => 'tab' + %li + = link_to 'Track schedules', '#tracks', 'data-toggle' => 'tab' + .tab-content + .tab-pane.active#conference + .row + .col-md-12 + %table.table.table-hover + %thead + %th Schedule + %th Selected + %th Actions + %tbody + - @schedules.where(track: nil).each do |schedule| + %tr + %td + Schedule + = schedule.id + %td + = selected_scheduled?(schedule) + %td + .btn-group{role: "group"} + - if can? :show, schedule + = link_to 'Show', admin_conference_schedule_path(@conference.short_title, schedule), + method: :get, class: 'btn btn-primary' + - if can? :destroy, schedule + = link_to 'Delete', admin_conference_schedule_path(@conference.short_title, schedule), + method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete Schedule #{schedule.id}?" } + .row + .col-md-12 + - if @venue.try(:rooms).present? + .text-right + - if can? :create, @program.schedules.new + = link_to 'Add Schedule', admin_conference_schedules_path(@conference.short_title), + method: :post, class: 'btn btn-primary' + - elsif @venue + .h3 + No Rooms! + %small + = link_to 'Create rooms', admin_conference_venue_rooms_path + before creating the schedule. + - else + .h3 + No Venue! + %small + = link_to 'Create a venue with rooms', new_admin_conference_venue_path + before creating the schedule. + .tab-pane#tracks + .row + .col-md-12 + %table.table.table-hover + %thead + %th Schedule + %th Track + %th Selected + %th Actions + %tbody + - @schedules.where.not(track: nil).each do |schedule| + %tr + %td + Schedule + = schedule.id + %td + - track = schedule.track + = link_to track.name, admin_conference_program_track_path(@conference.short_title, track) + %td + = schedule == schedule.track.selected_schedule ? 'Yes' : 'No' + %td + .btn-group{role: "group"} + - if can? :show, schedule + = link_to 'Show', admin_conference_schedule_path(@conference.short_title, schedule), class: 'btn btn-primary' + - if can? :destroy, schedule + = link_to 'Delete', admin_conference_schedule_path(@conference.short_title, schedule), + method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete Schedule #{schedule.id}?" } + .row + .col-md-12 + .text-right + - if can? :new, @program.schedules.build(track: @program.tracks.new) + = link_to 'Add Track Schedule', new_admin_conference_schedule_path(@conference.short_title), class: 'btn btn-primary' diff --git a/app/views/admin/schedules/show.html.haml b/app/views/admin/schedules/show.html.haml index 9b2d3571..258b4d87 100644 --- a/app/views/admin/schedules/show.html.haml +++ b/app/views/admin/schedules/show.html.haml @@ -11,8 +11,15 @@ .row .col-md-2 Selected schedule - = check_box_tag @conference.short_title, @schedule.id, (@schedule.id == @selected_schedule.try(:id)), - method: :patch, url: (admin_conference_program_path(@conference.short_title) + '?[program][selected_schedule_id]='), + :ruby + if @schedule.track + value = @schedule == @schedule.track.selected_schedule + url = update_selected_schedule_admin_conference_program_track_path(@conference.short_title, @schedule.track) + '?[track][selected_schedule_id]=' + else + value = @schedule.id == @selected_schedule.try(:id) + url = admin_conference_program_path(@conference.short_title) + '?[program][selected_schedule_id]=' + end + = check_box_tag @conference.short_title, @schedule.id, value, method: :patch, url: url, class: 'switch-checkbox-schedule', data: { size: 'small', off_color: 'warning', on_text: 'Yes', @@ -33,11 +40,23 @@ .tab-pane{ class: "#{ (@dates.first == date) ? 'active' : '' }", id: "#{date}" } = render partial: 'day_tab', locals: { date: date } - else - .h3 - No Rooms! - %small - = link_to 'Create rooms', admin_conference_venue_rooms_path - before creating the schedule. + - if @venue.try(:rooms).present? + .text-right + - if can? :create, @program.schedules.new + = link_to 'Add Schedule', admin_conference_schedules_path(@conference.short_title), + method: :post, class: 'btn btn-primary' + - elsif @venue + .h3 + No Rooms! + %small + = link_to 'Create rooms', admin_conference_venue_rooms_path + before creating the schedule. + - else + .h3 + No Venue! + %small + = link_to 'Create a venue with rooms', new_admin_conference_venue_path + before creating the schedule. :javascript $(document).ready( function() { diff --git a/app/views/admin/splashpages/_form.html.haml b/app/views/admin/splashpages/_form.html.haml index f945b2c9..c1897745 100644 --- a/app/views/admin/splashpages/_form.html.haml +++ b/app/views/admin/splashpages/_form.html.haml @@ -14,6 +14,7 @@ = f.input :include_tickets, label: 'Display tickets', input_html: { checked: params[:action] == 'new' || @splashpage.try(:include_tickets) } = f.input :include_lodgings, label: 'Display the lodgings', input_html: { checked: params[:action] == 'new' || @splashpage.try(:include_lodgings) } = f.input :include_sponsors, label: 'Display sponsors', input_html: { checked: params[:action] == 'new' || @splashpage.try(:include_sponsors) } + = f.input :include_booths, label: 'Display confirmed booths', input_html: { checked: params[:action] == 'new' || @splashpage.try(:include_booths) } = f.input :include_social_media, label: 'Display social media', input_html: { checked: params[:action] == 'new' || @splashpage.try(:include_social_media) } = f.inputs name: 'Access' do = f.input :public, label: 'Make splash page public?' diff --git a/app/views/admin/splashpages/show.html.haml b/app/views/admin/splashpages/show.html.haml index 0c6be8a8..c185b119 100644 --- a/app/views/admin/splashpages/show.html.haml +++ b/app/views/admin/splashpages/show.html.haml @@ -66,6 +66,13 @@ Yes - else No + %dt + Include Booths + %dd + - if @splashpage.include_booths + Yes + - else + No %dt Include Social Media: %dd @@ -90,5 +97,5 @@ - else .row .col-md-12.text-right - - if can? :create, @conference + - if can? :create, @conference.build_splashpage = link_to 'Create Splashpage', new_admin_conference_splashpage_path, class: 'btn btn-primary' diff --git a/app/views/admin/targets/_form.html.haml b/app/views/admin/targets/_form.html.haml index b0462249..1e81989b 100644 --- a/app/views/admin/targets/_form.html.haml +++ b/app/views/admin/targets/_form.html.haml @@ -10,6 +10,6 @@ = semantic_form_for(@target, url: (@target.new_record? ? admin_conference_targets_path : admin_conference_target_path(@conference.short_title, @target))) do |f| = f.input :due_date, as: :string, input_html: { class: 'target-due-date-datepicker'}, label: 'Until when do you want to have ' = f.input :target_count, label: 'this amount of ' - = f.input :unit, as: :select, label: 'Unit', class: 'form-control', collection: Target.units.values, include_blank: false, label: 'units ' + = f.input :unit, as: :select, label: 'Unit', class: 'form-control', collection: Target.units.values, include_blank: false %p.text-right = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/tickets/_form.html.haml b/app/views/admin/tickets/_form.html.haml index 94a54692..319f7996 100644 --- a/app/views/admin/tickets/_form.html.haml +++ b/app/views/admin/tickets/_form.html.haml @@ -12,6 +12,7 @@ = f.input :title = f.input :description, input_html: { rows: 5, data: { provide: "markdown-editable" } } = f.input :price - = f.input :price_currency, as: :select, class: 'form-control', collection: ['USD', 'EUR', 'GBP', 'INR', 'CNY'], include_blank: false + = f.input :price_currency, as: :select, class: 'form-control', collection: ['USD', 'EUR', 'GBP', 'INR', 'CNY', 'CHF'], include_blank: false + = f.input :registration_ticket, hint: 'A registration ticket is with which user register for the conference.' %p.text-right = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/tickets/index.html.haml b/app/views/admin/tickets/index.html.haml index 7fa3e37c..71698bb2 100644 --- a/app/views/admin/tickets/index.html.haml +++ b/app/views/admin/tickets/index.html.haml @@ -4,6 +4,7 @@ %h1 Tickets %p.text-muted Tickets to get during registration +%br - if @conference.tickets.any? .row .col-md-12 @@ -13,6 +14,7 @@ %th Price %th Sold %th Turnover + %th Registration Ticket %th Actions %tbody - @conference.tickets.each do |ticket| @@ -25,7 +27,9 @@ %td = ticket.tickets_sold %td - = humanized_money_with_symbol ticket.tickets_turnover + = humanized_money_with_symbol ticket.tickets_turnover_total(ticket.id) + %td + = ticket.registration_ticket? ? 'Yes' : 'No' %td .btn-group = link_to 'Edit', edit_admin_conference_ticket_path(@conference.short_title, ticket.id), @@ -36,4 +40,4 @@ .row .col-md-12 = link_to 'Add Ticket', new_admin_conference_ticket_path, class: 'btn btn-success pull-right' - + = link_to 'Tickets Sold', admin_conference_physical_tickets_path, class: 'button btn btn-default btn-info pull-right' diff --git a/app/views/admin/tracks/_change_state_dropdown.html.haml b/app/views/admin/tracks/_change_state_dropdown.html.haml new file mode 100644 index 00000000..0bbbcd7f --- /dev/null +++ b/app/views/admin/tracks/_change_state_dropdown.html.haml @@ -0,0 +1,34 @@ +- if track.transition_possible? :restart + %li= link_to 'Start review', + restart_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "restart_track_#{track.id}" + +- if track.transition_possible? :to_accept + %li= link_to 'Mark as possible acceptance', + to_accept_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "to_accept_track_#{track.id}" + +- if track.transition_possible? :accept + %li= link_to 'Accept track request', + accept_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "accept_track_#{track.id}" + +- if track.transition_possible? :confirm + %li= link_to 'Confirm track', + confirm_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "confirm_track_#{track.id}" + +- if track.transition_possible? :to_reject + %li= link_to 'Mark as possible rejection', + to_reject_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "to_reject_track_#{track.id}" + +- if track.transition_possible? :reject + %li= link_to 'Reject track request', + reject_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, confirm: 'Are you sure?', id: "reject_track_#{track.id}" + +- if track.transition_possible? :cancel + %li= link_to 'Cancel track request', + cancel_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "cancel_track_#{track.id}" diff --git a/app/views/admin/tracks/_form.html.haml b/app/views/admin/tracks/_form.html.haml index 579d263d..9109e4f5 100644 --- a/app/views/admin/tracks/_form.html.haml +++ b/app/views/admin/tracks/_form.html.haml @@ -8,8 +8,21 @@ Track .row .col-md-12 - = semantic_form_for(@track, url: (@track.new_record? ? admin_conference_program_tracks_path : admin_conference_program_track_path(@conference.short_title, @track))) do |f| + = semantic_form_for(@track, url: (@track.new_record? ? admin_conference_program_tracks_path(@conference.short_title) : admin_conference_program_track_path(@conference.short_title, @track))) do |f| = f.input :name + = f.input :short_name, hint: "A short and unique handle for the track, using only letters, numbers, underscores, and dashes. This will be used to identify the track in URLs etc. Example: 'my_awesome_track'", input_html: { required: 'required', pattern: '[a-zA-Z0-9_-]+', title: 'Only letters, numbers, underscores, and dashes.' } = f.input :color, input_html: {size: 6, type: 'color'}, required: true + = 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', required: @track.self_organized_and_accepted_or_confirmed? } + = f.input :end_date, as: :string, input_html: { id: 'registration-period-end-datepicker', readonly: 'readonly', required: @track.self_organized_and_accepted_or_confirmed? } + - if @conference.venue.try(:rooms) + = f.input :room, as: :select, collection: (@conference.venue.rooms).map {|room| ["#{room.name}", room.id]}, include_blank: true, label: 'Room', input_html: { class: 'select-help-toggle', required: @track.self_organized_and_accepted_or_confirmed? } + - else + %b + Please add a + = link_to 'venue', admin_conference_venue_path(@conference.short_title) + with + = link_to 'rooms', admin_conference_venue_rooms_path(@conference.short_title) + , if you want to select a room for the track. = f.input :description, input_html: {rows: 2, data: { provide: 'markdown-editable' } }, hint: markdown_hint + = f.input :cfp_active, label: 'Allow event submitters to select this track for their proposal' = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/tracks/index.html.haml b/app/views/admin/tracks/index.html.haml index f2de8a38..b814ac8e 100644 --- a/app/views/admin/tracks/index.html.haml +++ b/app/views/admin/tracks/index.html.haml @@ -1,3 +1,4 @@ +.unobtrusive-flash-container .row .col-md-12 .page-header @@ -6,31 +7,76 @@ Categorize events in your conference .row .col-md-12 - %table.table.table-hover#tracks - %thead - %th Name - %th Description - %th Color - %th Actions - %tbody - - @tracks.each do |track| - %tr - %td - = link_to(admin_conference_program_track_path(@conference.short_title, track)) do - = track.name - %td - %p - = truncate(track.description) - %td - %span.label{style: "background-color: #{track.color}; color: #{ contrast_color(track.color) }"} - = track.color - %td - .btn-group{role: "group"} - = link_to 'Edit', edit_admin_conference_program_track_path(@conference.short_title, track.id), - method: :get, class: 'btn btn-primary' - = link_to 'Delete', admin_conference_program_track_path(@conference.short_title, track.id), - method: :delete, class: 'btn btn-danger', - data: { confirm: "Do you really want to delete #{track.name}? Attention: This track will be removed from all Events that have it set" } + - if @tracks.any? + %table.table.table-hover.table-striped.table-bordered.datatable#tracks + %thead + %th ID + %th Name + %th Description + %th Room + %th Start Date + %th End Date + %th Submitter + %th Included in Cfp + %th State + %th Actions + %tbody + - @tracks.each do |track| + %tr + %td + = track.id + %td{ style: 'padding: 15px 0px 0px 10px;' } + = link_to admin_conference_program_track_path(@conference.short_title, track), class: 'btn' do + %span.label{style: "background-color: #{track.color}; color: #{ contrast_color(track.color) }"} + = track.name + %td + %p + = markdown(truncate(track.description)) + %td + = track.room.try(:name) + %td + = track.start_date.strftime('%A, %B %-d. %Y') if track.start_date + %td + = track.end_date.strftime('%A, %B %-d. %Y') if track.end_date + %td + = link_to track.submitter.name, admin_user_path(track.submitter) if track.self_organized? + %td.text-center{ 'id' => "cfp_switch_#{track.id}", 'data-order' => track.cfp_active.to_s } + = check_box_tag "#{@conference.short_title}_#{track.short_name}", track.id, track.cfp_active, + class: 'switch-checkbox', method: :patch, + url: toggle_cfp_inclusion_admin_conference_program_track_path(@conference.short_title, id: track.short_name)+"?included=", + data: { size: 'small', + on_color: 'success', + off_color: 'warning', + on_text: 'Yes', + off_text: 'No' } + + %td.text-center + - if track.self_organized? + .btn-group + %button{ type: 'button', class: 'btn btn-link dropdown-toggle', 'data-toggle' => 'dropdown' } + = track.state.humanize + %span.caret + %ul.dropdown-menu{ role: 'menu' } + = render 'change_state_dropdown', track: track + - else + = track.state.humanize + %td + .btn-group{ role: 'group' } + - if can? :edit, track + = link_to 'Edit', edit_admin_conference_program_track_path(@conference.short_title, track), class: 'btn btn-primary' + - special_style = true + - if can? :destroy, track + = link_to 'Delete', admin_conference_program_track_path(@conference.short_title, track), method: :delete, class: 'btn btn-danger', + data: { confirm: "Do you really want to delete #{track.name}? Attention: This track will be removed from all Events that have it set" } + - if track.self_organized? + - if track.selected_schedule + - if can? :show, track.selected_schedule + = link_to 'Show Schedule', admin_conference_schedule_path(@conference.short_title, track.selected_schedule), + class: 'btn btn-default' + - elsif can? :create, @program.schedules.build(track: track) + = button_to 'Create Schedule', admin_conference_schedules_path(@conference.short_title), + form: { class: 'btn', style: 'padding: 0px 0px; margin-top: -1px;' }, class: 'btn btn-default', + style: ('border-top-left-radius: 0; border-bottom-left-radius: 0;' if special_style), params: { 'schedule[track_id]' => track.id } .row .col-md-12.text-right - = link_to 'New Track', new_admin_conference_program_track_path(@conference.short_title), class: 'btn btn-success' + = link_to 'New Track', new_admin_conference_program_track_path(@conference.short_title), class: 'btn btn-primary' diff --git a/app/views/admin/tracks/show.html.haml b/app/views/admin/tracks/show.html.haml index 3acb8471..b8c9773e 100644 --- a/app/views/admin/tracks/show.html.haml +++ b/app/views/admin/tracks/show.html.haml @@ -1,30 +1,134 @@ +.unobtrusive-flash-container .row .col-md-12 .page-header %h1 = @track.name Track - %p.text-muted - Events in this track -.row - .col-md-12 - %table.table.table-hover.datatable - %thead - %th Title - %th Type - %th Submitter - %th State - %th Time - %tbody - - @track.events.each_with_index do |event| - %tr - %td - =link_to event.title, admin_conference_program_event_path(@conference.short_title, event) - %td - = event.event_type.title - %td - =link_to event.submitter.name, admin_user_path(event.submitter) - %td - = event.state - %td - = event.time + +.tabbable + %ul.nav.nav-tabs + %li.active + = link_to 'Details', '#details', 'data-toggle' => 'tab' + %li + = link_to 'Events', '#events', 'data-toggle' => 'tab' + + .tab-content + .tab-pane.active#details + .row + .col-md-12 + .btn-group.pull-right + - if can? :edit, @track + = link_to 'Edit', edit_admin_conference_program_track_path(@conference.short_title, @track), + method: :get, class: 'btn btn-primary' + - special_style = true + - if can? :destroy, @track + = link_to 'Delete', admin_conference_program_track_path(@conference.short_title, @track), + method: :delete, class: 'btn btn-danger', + data: { confirm: "Do you really want to delete #{@track.name}? Attention: This track will be removed from all Events that have it set" } + - if @track.self_organized? + - if @track.selected_schedule + - if can? :show, @track.selected_schedule + = link_to 'Show Schedule', admin_conference_schedule_path(@conference.short_title, @track.selected_schedule), + class: 'btn btn-default' + - elsif can? :create, @program.schedules.build(track: @track) + = button_to 'Create Schedule', admin_conference_schedules_path(@conference.short_title), + form: { class: 'btn', style: 'padding: 0px 0px; margin-top: -1px;' }, class: 'btn btn-default', + style: ('border-top-left-radius: 0; border-bottom-left-radius: 0;' if special_style), params: { 'schedule[track_id]' => @track.id } + .row + .col-md-12 + %table.table + %tr + %td.col-md-2 + %b Color + %td + %span.label{ style: "background-color: #{@track.color}; color: #{ contrast_color(@track.color) }" } + = @track.color + %tr + %td + %b Room + %td + = @track.room.try(:name) + %tr + %td + %b Start date + %td + = @track.start_date.strftime('%A, %B %-d. %Y') if @track.start_date + %tr + %td + %b End date + %td + = @track.end_date.strftime('%A, %B %-d. %Y') if @track.end_date + - if @track.self_organized? + %tr + %td + %b Submitter + %td + = link_to @track.submitter.name, admin_user_path(@track.submitter) + - if @track.confirmed? + %tr + %td + %b Organizers + %td + - Role.find_by(name: 'track_organizer', resource: @track).users.each do |organizer| + %div + = link_to organizer.name, admin_user_path(organizer) + %tr + %td + %b Included in the Cfp? + %td + = check_box_tag "#{@conference.short_title}_#{@track.short_name}", @track.id, @track.cfp_active, + class: 'switch-checkbox', method: :patch, + url: toggle_cfp_inclusion_admin_conference_program_track_path(@conference.short_title, id: @track.short_name)+"?included=", + data: { size: 'small', + on_color: 'success', + off_color: 'warning', + on_text: 'Yes', + off_text: 'No' } + %tr + %td + %b State + %td + - if @track.self_organized? + .btn-group + %button{ type: 'button', class: 'btn btn-link dropdown-toggle', 'data-toggle' => 'dropdown' } + = @track.state.humanize + %span.caret + %ul.dropdown-menu{ role: 'menu' } + = render 'change_state_dropdown', track: @track + - else + = @track.state.humanize + %tr + %td + %b Description + %td + = markdown(@track.description) + - if @track.self_organized? + %tr + %td + %b Relevance + %td + = markdown(@track.relevance) + + .tab-pane#events + .col-md-12 + %table.table.table-hover.datatable + %thead + %th Title + %th Type + %th Submitter + %th State + %th Time + %tbody + - @track.events.each_with_index do |event| + %tr + %td + =link_to event.title, admin_conference_program_event_path(@conference.short_title, event) + %td + = event.event_type.title + %td + =link_to event.submitter.name, admin_user_path(event.submitter) + %td + = event.state + %td + = event.time diff --git a/app/views/admin/tracks/toggle_cfp_inclusion.js.erb b/app/views/admin/tracks/toggle_cfp_inclusion.js.erb new file mode 100644 index 00000000..84ddd69a --- /dev/null +++ b/app/views/admin/tracks/toggle_cfp_inclusion.js.erb @@ -0,0 +1,13 @@ +$('.alert').remove(); + +track_id = <%= @track.id %>; +track_cfp_td = $('#cfp_switch_' + track_id); +track_cfp_value = <%= @track.cfp_active %>; + +track_cfp_td.attr('data-order', track_cfp_value); + +/* +* The updated data-order attribute isn't taken into account +* until we invalidate the cell +*/ +$('#tracks').DataTable().cell(track_cfp_td).invalidate(); diff --git a/app/views/admin/venues/_form.html.haml b/app/views/admin/venues/_form.html.haml index 7dd08a2c..6674305b 100644 --- a/app/views/admin/venues/_form.html.haml +++ b/app/views/admin/venues/_form.html.haml @@ -15,6 +15,12 @@ = semantic_form_for(@venue, url: admin_conference_venue_path(@conference.short_title)) do |f| = f.inputs :name, :website = f.input :description, input_html: { rows: 5, cols: 20, data: { provide: 'markdown-editable' } }, hint: markdown_hint + = f.label 'Venue Logo' + %br + - if @venue.picture? + = image_tag @venue.picture.thumb.url + = f.input :picture, label: false, hint: 'This will be displayed on the venue are of the splash page.' + = f.hidden_field :picture_cache = f.inputs :street, :postalcode, :city, :country, :latitude, :longitude = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/versions/_object_desc_and_link.html.haml b/app/views/admin/versions/_object_desc_and_link.html.haml index 44eedf82..4852a50f 100644 --- a/app/views/admin/versions/_object_desc_and_link.html.haml +++ b/app/views/admin/versions/_object_desc_and_link.html.haml @@ -1,173 +1,241 @@ +- unless version.item_type == 'Role' || version.item_type == 'UsersRole' + - conference = Conference.find_by(id: version.conference_id) + - conference_short_title = conference.try(:short_title) || current_or_last_object_state('Conference', version.conference_id).try(:short_title) || ' ' + - case version.item_type +- when 'Organization' + organization + = link_to_organization(version.item_id) + - when 'UsersRole' - users_role = current_or_last_object_state(version.item_type, version.item_id) - - user = current_or_last_object_state('User', users_role.user_id) - = 'role' - = link_to users_role.role.name, admin_conference_role_path(conference_id: Conference.find(version.conference_id).short_title, id: users_role.role.name) - = version.event == 'create' ? 'to' : 'from' - = 'user' + - role = Role.find_by(id: users_role.role_id) if users_role + role + - if role.name == 'organization_admin' + - if Organization.find_by(id: version.conference_id) + -# organization_admin belongs to organization and not conferences + - organization = Organization.find_by(id: version.conference_id) + = link_if_alive version, role.name, + admins_admin_organization_path(organization), organization + - else + (Deleted Organization) + - else + - conference = Conference.find_by(id: version.conference_id) + - conference_short_title = conference.try(:short_title) || current_or_last_object_state('Conference', version.conference_id).try(:short_title) || ' ' + = link_if_alive version, role.try(:name), admin_conference_role_path(conference_short_title,role.try(:name) || ' '), conference - = link_to (user.try(:name) || 'deleted user'), admin_user_path(id: users_role.user.id) + = version.event == 'create' ? 'to' : 'from' + user + = link_to_user(users_role.user_id) - when 'Subscription', 'Registration' - = 'conference' - = link_to Conference.find(version.conference_id).title, - admin_conference_registrations_path(conference_id: Conference.find(version.conference_id).short_title) + conference + = link_to_conference(version.conference_id) - when 'Commercial' - commercial = current_or_last_object_state(version.item_type, version.item_id) - commercialable = current_or_last_object_state(commercial.commercialable_type, commercial.commercialable_id) - - commercialable_last_version = PaperTrail::Version.where(item_type: commercial.commercialable_type, item_id: commercial.commercialable_id).last - case commercial.commercialable_type - when 'Event' - commercial in - - if commercialable_last_version.item - event + commercial in event + - if commercialable && conference = link_to commercialable.title, - admin_conference_program_event_path(conference_id: Conference.find(version.conference_id).short_title, id: commercialable.id) + admin_conference_program_event_path(conference, commercialable.id) - else = commercialable.title + = "with ID #{commercialable.id}" - when 'Venue' commercial in venue - - if commercialable_last_version.item + - if commercialable && conference = link_to commercialable.name, - edit_admin_conference_venue_path(conference_id: Conference.find(version.conference_id).short_title, - id: commercialable.id, anchor: 'commercials-content') + edit_admin_conference_venue_path(conference_short_title, + commercialable.id, anchor: 'commercials-content') - else = commercialable.name + = "with ID #{commercialable.id}" - when 'Conference' - = link_to 'commercial', - admin_conference_commercials_path(conference_id: Conference.find(version.conference_id).short_title) + commercial in conference + - if commercialable + = link_to commercialable.short_title, + admin_conference_commercials_path(commercialable.short_title) + - else + = commercialable.short_title + = "with ID #{commercialable.id}" - when 'EventsRegistration', 'Comment', 'Vote', 'Event' - = 'event' + event - object = current_or_last_object_state(version.item_type, version.item_id) - event_id = object.try(:event_id) || object.try(:commentable_id) || object.id = link_to (current_or_last_object_state('Event', event_id).try(:title) || 'deleted event'), - admin_conference_program_event_path(conference_id: Conference.find(version.conference_id).short_title, id: event_id) + admin_conference_program_event_path(conference_short_title, event_id) - when 'Target' - = 'target' + target - target = current_or_last_object_state(version.item_type, version.item_id) - = link_if_alive version, target.to_s, admin_conference_targets_path(conference_id: Conference.find(version.conference_id).short_title) + = link_if_alive version, target.to_s, admin_conference_targets_path(conference_short_title), conference - when 'EventSchedule' - event_schedule = current_or_last_object_state(version.item_type, version.item_id) event - = link_to (current_or_last_object_state('Event', event_schedule.event_id).try(:title) || 'deleted event'), - admin_conference_program_event_path(conference_id: Conference.find(version.conference_id).short_title, id: event_schedule.event_id) + = link_to (current_or_last_object_state('Event', event_schedule.event_id).try(:title) || 'deleted'), + admin_conference_program_event_path(conference_short_title, event_schedule.event_id) in = link_to "Schedule #{event_schedule.schedule_id}", - admin_conference_schedule_path(conference_id: Conference.find(version.conference_id).short_title, id: event_schedule.schedule_id) + admin_conference_schedule_path(conference_short_title, event_schedule.schedule_id) - when 'Schedule' = link_if_alive version, "Schedule #{version.item_id}", - admin_conference_schedule_path(conference_id: Conference.find(version.conference_id).short_title, id: version.item_id) + admin_conference_schedule_path(conference_short_title, version.item_id), + conference - when 'Conference' - = 'conference' - = link_to Conference.find(version.conference_id).title, - edit_admin_conference_path(id: Conference.find(version.conference_id).short_title) + conference + = link_to_conference(version.item_id) - when 'RegistrationPeriod' = link_if_alive version, 'registration period', - admin_conference_registration_period_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_registration_period_path(conference_short_title), + conference - when 'Contact' = link_if_alive version, 'contact details', - edit_admin_conference_contact_path(conference_id: Conference.find(version.conference_id).short_title) + edit_admin_conference_contact_path(conference_short_title), + conference + +- when 'Booth' + = 'booth' + - booth = current_or_last_object_state(version.item_type, version.item_id) + = link_if_alive version, booth.title, + admin_conference_booth_path(conference_id: Conference.find(version.conference_id).short_title, id: version.item_id ), + conference - when 'Program' = link_if_alive version, 'program', - admin_conference_program_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_program_path(conference_short_title), + conference - when 'Cfp' - = link_if_alive version, 'cfp', - admin_conference_program_cfp_path(conference_id: Conference.find(version.conference_id).short_title) + cfp for + - cfp = current_or_last_object_state(version.item_type, version.item_id) + = link_if_alive version, cfp.cfp_type, + admin_conference_program_cfp_path(conference_short_title, version.item_id), + conference - when 'Track' - = 'track' + track - track = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, track.name, - admin_conference_program_track_path(conference_id: Conference.find(version.conference_id).short_title, id: version.item_id) + admin_conference_program_track_path(conference_short_title, track.try(:short_name)), + conference - when 'EventType' - = 'event type' + event type - event_type = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, event_type.title, - admin_conference_program_event_types_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_program_event_types_path(conference_short_title), + conference - when 'Role' - = 'role' + role - role = current_or_last_object_state(version.item_type, version.item_id) - = link_if_alive version, role.name, - admin_conference_role_path(conference_id: Conference.find(version.conference_id).short_title, id: role.name) + - role_name = role.try(:name) || PaperTrail::Version.where(item_type: 'Role', item_id: version.item_id).last.changeset[:name].second + - if role_name == 'organization_admin' + - if Organization.find_by(id: version.conference_id) + -# organization_admin belongs to organization and not conferences + - organization = Organization.find_by(id: version.conference_id) + = link_if_alive version, role.name, + admins_admin_organization_path(organization), organization + - else + (Role Deleted) + - else + - conference = Conference.find_by(id: version.conference_id) + - conference_short_title = conference.try(:short_title) || current_or_last_object_state('Conference', version.conference_id).try(:short_title) || ' ' + = link_if_alive version, role_name, + admin_conference_role_path(conference_short_title, role_name), conference - when 'Venue' - = 'venue' + venue - venue = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, venue.name, - admin_conference_venue_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_venue_path(conference_short_title), + conference - when 'Lodging' - = 'lodging' + lodging - lodging = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, lodging.name, - admin_conference_lodgings_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_lodgings_path(conference_short_title), + conference - when 'Room' - = 'room' + room - room = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, room.name, - admin_conference_venue_rooms_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_venue_rooms_path(conference_short_title), + conference - when 'Sponsor' - = 'sponsor' + sponsor - sponsor = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, sponsor.name, - admin_conference_sponsors_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_sponsors_path(conference_short_title), + conference - when 'SponsorshipLevel' - = 'sponsorship level' + sponsorship level - sponsorship_level = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, sponsorship_level.title, - admin_conference_sponsorship_levels_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_sponsorship_levels_path(conference_short_title), + conference - when 'Ticket' - = 'ticket' + ticket - ticket = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, ticket.title, - admin_conference_ticket_path(conference_id: Conference.find(version.conference_id).short_title, id: version.item_id) + admin_conference_ticket_path(conference_short_title, version.item_id), + conference - when 'Campaign' - = 'campaign' + campaign - campaign = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, campaign.name, - admin_conference_campaigns_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_campaigns_path(conference_short_title), + conference - when 'DifficultyLevel' - = 'difficulty level' + difficulty level - difficulty_level = current_or_last_object_state(version.item_type, version.item_id) = link_if_alive version, difficulty_level.title, - admin_conference_program_difficulty_level_path(conference_id: Conference.find(version.conference_id).short_title, id: version.item_id) + admin_conference_program_difficulty_level_path(conference_short_title, version.item_id), + conference - when 'Splashpage' = link_if_alive version, 'splashpage', - admin_conference_splashpage_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_splashpage_path(conference_short_title), + conference - when 'EmailSettings' = link_if_alive version, 'email settings', - admin_conference_emails_path(conference_id: Conference.find(version.conference_id).short_title) + admin_conference_emails_path(conference_short_title), + conference - when 'User' - if version.event == 'update' - = 'user' - = link_to (current_or_last_object_state('User', version.item_id).try(:name) || 'deleted user'), admin_user_path(id: version.item_id) + user + = link_to_user(version.item_id) -- unless %w(Conference Subscription Registration User).include?(version.item_type) - = "in conference" - = link_to Conference.find(version.conference_id).short_title, - edit_admin_conference_path(id: Conference.find(version.conference_id).short_title) +- unless %w(Conference Subscription Registration User Organization).include?(version.item_type) + - if (version.item_type == 'Role' && role_name == 'organization_admin') || (version.item_type == 'UsersRole' && role.name == 'organization_admin') + in organization + - if Organization.find_by(id: version.conference_id) + -# organization_admin belongs to organization and not conferences + - organization = Organization.find_by(id: version.conference_id) + = link_to_organization(version.conference_id) + - else + (Organization Deleted) + - else + in conference + = link_to_conference(version.conference_id) diff --git a/app/views/admin/versions/index.html.haml b/app/views/admin/versions/index.html.haml index 69af134b..963e0a57 100644 --- a/app/views/admin/versions/index.html.haml +++ b/app/views/admin/versions/index.html.haml @@ -1,6 +1,16 @@ .row .col-md-12 .page-header + + .dropdown.pull-right + %button.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown', type: 'button' } + = @conference.nil? ? 'All Conferences' : @conference.short_title + %span.caret + %ul.dropdown-menu + %li= link_to 'All Conferences & Users', admin_revision_history_path + - @conferences_with_role.each do |conference_short_title| + %li= link_to conference_short_title, admin_conference_revision_history_path(conference_id: conference_short_title) + %h1 Revision History %p.text-muted Log of changes made to conferences and associated resources diff --git a/app/views/booths/_form.html.haml b/app/views/booths/_form.html.haml new file mode 100644 index 00000000..55a51432 --- /dev/null +++ b/app/views/booths/_form.html.haml @@ -0,0 +1,30 @@ +.container + .row + .col-md-8 + = semantic_form_for(@booth, url: @url, html: { multipart: true }) do |f| + = f.input :title, as: :string, autofocus: true, required: true + = f.input :description, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + hint: 'This field becomes public upon request acceptance' + = f.input :reasoning, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + label: 'How it fits the conference' + = f.input :submitter_relationship, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + label: 'Submitter\'s relation', + hint: 'e.g. employee, comunity manager, etc' + = f.input :website_url + = responsibles_selector_input f + = image_tag f.object.picture.thumb.url if f.object.picture? + = f.input :picture + + %p.text-right + - if @booth.new_record? + = f.submit 'Create Booth Request', class: 'btn btn-success' + - else + = f.submit 'Update Booth Request', class: 'btn btn-success' + + :javascript + $(document).ready(function() { + $('#booth_responsible_ids').selectize({ + plugins: ['remove_button'], + minItems: 2 + } ) + }); diff --git a/app/views/booths/edit.html.haml b/app/views/booths/edit.html.haml new file mode 100644 index 00000000..9f60196d --- /dev/null +++ b/app/views/booths/edit.html.haml @@ -0,0 +1,6 @@ +.container + %h1 + Editing + = @booth.title + + = render 'form' diff --git a/app/views/booths/index.html.haml b/app/views/booths/index.html.haml new file mode 100644 index 00000000..d6c392dd --- /dev/null +++ b/app/views/booths/index.html.haml @@ -0,0 +1,51 @@ +.container + .row + .col-md-12.page-header + %h1 + Your booth requests for + = @conference.title + .row + .col-md-12 + .margin-booth-table + %table.table.table-striped.table-hover + %thead + %th + %b State + %th + %b Logo + %th + %b Title + %th + %b Actions + - @booths.each do |booth| + %tr + %td{ style: "padding:20px 8px 20px 8px;" } + - if (booth.state == 'to_accept' || booth.state == 'to_reject') + - show_state = 'new' + - else + - show_state = booth.state + %span{ title: show_state, class: "fa #{booth_status_icon(booth)}" } + %td + - if booth.logo_link + = image_tag(booth.picture.thumb.url, width: '20%') + %td + = link_to booth.title, conference_booth_path(@conference.short_title, booth) + %td + -if can? :edit, booth + = link_to 'Edit', edit_conference_booth_path(@conference.short_title, booth.id), + class: 'btn btn-default' + - if booth.transition_possible? :withdraw + = link_to 'Withdraw', + withdraw_conference_booth_path(@conference.short_title, booth), + method: :patch, class: 'btn btn-mini btn-warning', id: "withdraw_booth_#{booth.id}", + data: { confirm: 'Are you sure you really want to withdraw this request?' } + - if booth.transition_possible? :confirm + = link_to 'Confirm', + confirm_conference_booth_path(@conference.short_title, booth), + method: :patch, class: 'btn btn-mini btn-success', id: "confirm_booth_#{booth.id}" + - if booth.transition_possible? :restart + = link_to 'Re-submit', + restart_conference_booth_path(@conference.short_title, booth), + method: :patch, class: 'btn btn-mini btn-success', id: "restart_booth_#{booth.id}" + .pull-right + = link_to 'Add Booth', new_conference_booth_path(@conference.short_title), class: 'button btn btn-primary' diff --git a/app/views/booths/new.html.haml b/app/views/booths/new.html.haml new file mode 100644 index 00000000..169efd17 --- /dev/null +++ b/app/views/booths/new.html.haml @@ -0,0 +1,4 @@ +.container + %h1 Request a booth + + = render 'form' diff --git a/app/views/booths/show.html.haml b/app/views/booths/show.html.haml new file mode 100644 index 00000000..6b12229d --- /dev/null +++ b/app/views/booths/show.html.haml @@ -0,0 +1,60 @@ +.container + .row + .col-md-12 + %h3 + - if @booth.logo_link + = image_tag(@booth.picture.thumb.url, size: '20%', alt: '') + = @booth.title + .btn-group.pull-right + = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, @booth), class: 'btn btn-mini btn-primary' + + .row + .col-md-12 + %table.table + %tr + %td.col-md-2 + %b Description + %td + = markdown(@booth.description) + %tr + %td.col-md-2 + %b Reasoning + %td + = markdown(@booth.reasoning) + %tr + %td.col-md-2 + %b Website + %td + - if @booth.website_url.present? + = link_to @booth.website_url, @booth.website_url + %tr + %td.col-md-2 + %b Submitter + %td + = link_to @booth.submitter.name, user_path(@booth.submitter) + %tr + %td.col-md-2 + %b Submitter's relationship + %td + = @booth.submitter_relationship + %tr + %td.col-md-2 + %b Responsibles + %td + - @booth.responsibles.each_with_index do |responsibles, i| + .responsibles + = link_to responsibles.name, user_path(responsibles) + ( + = responsibles.email + ) + = ", " unless i == @booth.responsibles.length - 1 + %tr + %td.col-md-2 + %b Submitted on + %td + = @booth.created_at + %tr + %td.col-md-2 + %b Last updated on + %td + = @booth.updated_at diff --git a/app/views/conference_registrations/show.html.haml b/app/views/conference_registrations/show.html.haml index c510a263..a50cf198 100644 --- a/app/views/conference_registrations/show.html.haml +++ b/app/views/conference_registrations/show.html.haml @@ -101,7 +101,7 @@ = word_pluralize(@total_quantity[ticket_id], 'Ticket') for = tickets.first.price.symbol - = humanized_money tickets.first.price + = humanized_money @total_price_per_ticket[ticket_id] %br - if @tickets.any? = link_to 'Get more tickets', conference_tickets_path(@conference.short_title), class: "btn btn-default" @@ -109,7 +109,7 @@ You haven't bought any tickets. = link_to 'Please get some tickets to support us!', conference_tickets_path(@conference.short_title) %p - (Your participation won't be valid without getting a ticket) + (Your participation won't be valid without getting a registration ticket) .row .col-md-12 diff --git a/app/views/conferences/_booths.html.haml b/app/views/conferences/_booths.html.haml new file mode 100644 index 00000000..b40af9b8 --- /dev/null +++ b/app/views/conferences/_booths.html.haml @@ -0,0 +1,24 @@ += content_for :splash_nav do + %li + %a.smoothscroll{ href: '#booths' } Booths + +.container + .row + .col-md-12.text-center + %h2 Booths + - @conference.booths.confirmed.each_slice(3).with_index do |slice, index_for_row| + .row.row-centered + - slice.each.with_index do |booth, index_for_column| + .col-md-4.col-sm-4.col-xs-10.col-centered.col-top + .thumbnail + - if booth.logo_link + = link_to booth.website_url, class: 'thumbnail' do + = image_tag booth.picture.large.url + .caption + %h3.text-center + = booth.title + %p.text-center.text-muted + = link_to "#show_descrition_#{index_for_row}_#{index_for_column}", "data-toggle"=>"collapse" do + learn more + .collapse{ id: "show_descrition_#{index_for_row}_#{index_for_column}" } + = markdown(booth.description) diff --git a/app/views/conferences/_call_for_tracks.html.haml b/app/views/conferences/_call_for_tracks.html.haml new file mode 100644 index 00000000..1abeb956 --- /dev/null +++ b/app/views/conferences/_call_for_tracks.html.haml @@ -0,0 +1,30 @@ += content_for :splash_nav do + %li + %a.smoothscroll{ href: '#callfortracks' } Call For Tracks + +.container + .row + .col-md-12.text-center + %h2 + Call for Tracks + %p.lead + We are ready to accept requests for tracks! + .row + .col-md-6.col-md-offset-3.col-sm-10.col-sm-offset-1 + %p + The submission period for track requests has begun + %em.notranslate + = @conference.program.cfps.for_tracks.start_date.strftime('%A, %B %-d. %Y') + and closes + %em.notranslate + = @conference.program.cfps.for_tracks.end_date.strftime('%A, %B %-d. %Y.') + - if @conference.program.cfps.for_tracks.try(:open?) + That means you have only + %b.notranslate= pluralize(@conference.program.cfps.for_tracks.remaining_days, 'day') + left! + - else + The submission period for track requests is closed. + .row + .col-md-12.text-center + %p.cta-button + = link_to "Submit your request for track", conference_program_tracks_path(@conference.short_title), class: 'btn btn-success btn-lg text-center' diff --git a/app/views/conferences/_conference_details.html.haml b/app/views/conferences/_conference_details.html.haml index bdbd5fc6..84a87303 100644 --- a/app/views/conferences/_conference_details.html.haml +++ b/app/views/conferences/_conference_details.html.haml @@ -30,11 +30,21 @@ = link_to "Register", new_conference_conference_registration_path(conference.short_title), class: "btn btn-default", disabled: cannot?(:new, Registration.new(conference_id: conference.id)) - if cannot?(:new, Registration.new(conference_id: conference.id)) && conference.registration_limit_exceeded? Sorry, no places left + - if current_user && current_user.tracks.where(program: conference.program).length > 0 + = link_to 'My Track Requests', conference_program_tracks_path(conference.short_title), class: 'btn btn-default' + - elsif can? :new, conference.program.tracks.new + = link_to 'Submit Track Request', new_conference_program_track_path(conference.short_title), class: 'btn btn-default' - if !current_user.nil? && current_user.proposal_count(conference) > 0 = link_to "My Proposals", conference_program_proposals_path(conference.short_title), class: 'btn btn-default' - elsif can? :new, conference.program.events.new = link_to "Submit Proposal", new_conference_program_proposal_path(conference.short_title), class: 'btn btn-default' + - if current_user && current_user.booths.where(conference_id: conference.id).count > 0 + = link_to 'My Booth Requests', conference_booths_path(conference.short_title), class: 'btn btn-default' + - elsif can? :new, conference.booths.new + = link_to 'Request Booth', new_conference_booth_path(conference.short_title), class: 'btn btn-default' - if current_user.nil? || !current_user.subscribed?(conference) = link_to 'Subscribe', conference_subscriptions_path(conference.short_title), method: :post, class: 'btn btn-default' - else = link_to 'Unsubscribe', conference_subscriptions_path(conference.short_title), method: :delete, class: 'btn btn-default' + - if current_user && current_user.physical_tickets.by_conference(conference).any? + = link_to "My Tickets", conference_physical_tickets_path(conference.short_title), class: 'btn btn-default' diff --git a/app/views/conferences/_lodging.html.haml b/app/views/conferences/_lodging.html.haml index e94286a4..561e2421 100644 --- a/app/views/conferences/_lodging.html.haml +++ b/app/views/conferences/_lodging.html.haml @@ -12,22 +12,26 @@ = @conference.venue.city %p.lead We recommend these affordable lodging accommodations for your visit. - - @conference.lodgings.each_slice(3) do |slice| - .row.row-centered - - slice.each do |lodging| - .col-md-4.col-sm-4.ticket.col-centered.col-top - .thumbnail - - unless lodging.picture? - %p.text-center - %i.fa.fa-home.fa-5x + + .row.row-centered{ style:"display: flex; flex-wrap: wrap" } + - @conference.lodgings.each do |lodging| + .col-md-4.col-sm-4.col-centered.col-top{ style:"display:flex;" } + .thumbnail + - if lodging.picture? + -if lodging.website_link.present? + = link_to(lodging.website_link, class: 'thumbnail') do + = image_tag lodging.picture.large.url, class: 'img-responsive img-lodging' - else + = image_tag lodging.picture.large.url, class: 'img-responsive img-lodging' + - else + %p.text-center -if lodging.website_link.present? = link_to(lodging.website_link, class: 'thumbnail') do - = image_tag lodging.picture.large.url, class: 'img-responsive img-lodging' + %i.fa.fa-home.fa-5x - else - = image_tag lodging.picture.large.url, class: 'img-responsive img-lodging' - .caption - %h3.text-center - = lodging.name - -if lodging.description.present? - = markdown(lodging.description) + %i.fa.fa-home.fa-5x + .caption + %h3.text-center + = lodging.name + -if lodging.description.present? + = markdown(lodging.description) diff --git a/app/views/conferences/_schedule_splashpage.html.haml b/app/views/conferences/_schedule_splashpage.html.haml index 67bd52e4..790582cb 100644 --- a/app/views/conferences/_schedule_splashpage.html.haml +++ b/app/views/conferences/_schedule_splashpage.html.haml @@ -10,13 +10,22 @@ - if @conference.splashpage and @conference.program.tracks.any? and @conference.splashpage.include_tracks See rock-star speakers cover the topics of - if @conference.splashpage and @conference.splashpage.include_tracks - - @conference.program.tracks.each_slice(3) do |slice| + - @conference.program.tracks.confirmed.each_slice(3) do |slice| .row.row-centered - slice.each do |track| .col-md-4.col-sm-4.col-centered.col-top.track %h4.text-center = track.name = markdown(track.description) + - if track.start_date + %br + From: #{track.start_date.strftime('%A, %B %-d. %Y')} + - if track.end_date + %br + To: #{track.end_date.strftime('%A, %B %-d. %Y')} + - if track.room + %br + In: #{track.room.name} - if @conference.program and @conference.program.schedule_public .row diff --git a/app/views/conferences/_venue.html.haml b/app/views/conferences/_venue.html.haml index 62af90c7..4436b0fe 100644 --- a/app/views/conferences/_venue.html.haml +++ b/app/views/conferences/_venue.html.haml @@ -32,4 +32,4 @@ = @conference.venue.country_name - if @conference.venue.website %br - =link_to @conference.venue.website, @conference.venue.website + =link_to(h(@conference.venue.website), h(@conference.venue.website)).html_safe diff --git a/app/views/conferences/_venue_map.html.haml b/app/views/conferences/_venue_map.html.haml index b58b3053..9f34117f 100644 --- a/app/views/conferences/_venue_map.html.haml +++ b/app/views/conferences/_venue_map.html.haml @@ -1,17 +1,16 @@ #map{style: "height: 500px;" } -- popup = "

#{@conference.venue.name}


#{@conference.venue.street}
#{@conference.venue.city}
#{@conference.venue.country_name}" - content_for(:script_body) do :javascript // create a map in the "map" div, set the view to a given place and zoom - var map = L.map('map', { scrollWheelZoom: false }).setView([#{@conference.venue.latitude}, #{@conference.venue.longitude}], 11); + var map = L.map('map', { scrollWheelZoom: false }).setView([#{h(@conference.venue.latitude)}, #{h(@conference.venue.longitude)}], 11); // add an OpenStreetMap tile layer L.tileLayer('//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', maxZoom: 18 }).addTo(map); // add a marker in the given location, attach some popup content to it and open the popup - L.marker([#{@conference.venue.latitude}, #{@conference.venue.longitude}]).addTo(map) - .bindPopup("#{popup}") + L.marker([#{h @conference.venue.latitude}, #{h @conference.venue.longitude}]).addTo(map) + .bindPopup("#{escape_javascript(render '/conferences/venue_map_marker', venue: @conference.venue)}") .openPopup(); // Turn scrollwheel on when user clicks map.on('focus', function(e) { diff --git a/app/views/conferences/_venue_map_marker.html.haml b/app/views/conferences/_venue_map_marker.html.haml new file mode 100644 index 00000000..c5f78345 --- /dev/null +++ b/app/views/conferences/_venue_map_marker.html.haml @@ -0,0 +1,13 @@ +- if venue.picture? + = image_tag venue.picture.thumb.url, + alt: venue.name, + class: 'img-responsive pull-right' +%h3= venue.name +%p + = venue.street + %br + = venue.city + %br + = venue.country_name +- if venue.website + %p.text-center.clearfix= sanitize(link_to venue.website, venue.website) diff --git a/app/views/conferences/show.html.haml b/app/views/conferences/show.html.haml index fb488e22..dd6b2d62 100644 --- a/app/views/conferences/show.html.haml +++ b/app/views/conferences/show.html.haml @@ -49,6 +49,10 @@ %section#callforpapers = render 'call_for_paper' + - if @conference.program.cfps.for_tracks.try(:open?) && @conference.splashpage.include_cfp + %section#callfortracks + = render 'call_for_tracks' + - if @conference.venue and @conference.splashpage.include_venue %section#venue = render 'venue' @@ -61,6 +65,10 @@ %section#tickets = render 'tickets' + - if @conference.booths.confirmed.any? and @conference.splashpage.include_booths + %section#booths + = render 'booths' + - if @conference.sponsors.any? and @conference.splashpage.include_sponsors %section#sponsors = render 'sponsors' @@ -74,7 +82,7 @@ - content_for :script_head do :javascript - var triangle_tcs = tinycolor("#{@conference.color}").monochromatic(); + var triangle_tcs = tinycolor("#{h(@conference.color)}").monochromatic(); var triangle_colors = triangle_tcs.map(function(t) { return t.toHexString(); }); $(function () { $(document).ready(function() { diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index d97b4867..c884a194 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -15,7 +15,11 @@ %h4 Currently the following openIDs are associated with your account - @openids.each do |openid| - %li= "#{openid.provider}:#{openid.email}" + %li{ style: "list-style: none; margin-left: 20px;" } + = link_to user_openid_path(openid, user_id: current_user.id), method: :delete, + data: { confirm: "Remove association with #{openid.provider} account?" } do + %span.fa.fa-times{ style: "color: red;" } + %span #{openid.provider}:#{openid.email} %br %h4 To add an openID with a different email address to your account, sign in with your diff --git a/app/views/layouts/_admin_sidebar.html.haml b/app/views/layouts/_admin_sidebar.html.haml index a9a2a99a..4385d307 100644 --- a/app/views/layouts/_admin_sidebar.html.haml +++ b/app/views/layouts/_admin_sidebar.html.haml @@ -1,4 +1,4 @@ -%ul.nav.nav-stacked.nav-pills.mySidebar +%ul.nav.nav-stacked.nav-pills.mySidebar.collapse.navbar-collapse#side-nav .btn-group %button{type:'button', class: 'btn btn-default btn-link dropdown-toggle', 'data-toggle'=>'dropdown'} %span.fa.fa-cog @@ -16,7 +16,7 @@ %span.fa.fa-cog Manage = conference.short_title - - if (current_user.is_admin) || (current_user.has_role? :organizer, :any) + - if can? :new, Conference.new %li = link_to(new_admin_conference_path) do %span.fa.fa-plus @@ -67,8 +67,8 @@ - if @conference.program %ul - if can? :update, Cfp.new(program_id: @conference.program.id) - %li{class: active_nav_li(admin_conference_program_cfp_path(@conference.short_title))} - = link_to 'Call for Papers', admin_conference_program_cfp_path(@conference.short_title) + %li{class: active_nav_li(admin_conference_program_cfps_path(@conference.short_title))} + = link_to 'Call for Papers', admin_conference_program_cfps_path(@conference.short_title) - if can? :update, @conference.program.events.build %li{class: active_nav_li(admin_conference_program_events_path(@conference.short_title))} = link_to 'Events', admin_conference_program_events_path(@conference.short_title) @@ -116,7 +116,11 @@ - if can? :update, @conference.tickets.build %li{class: active_nav_li(admin_conference_tickets_path(@conference.short_title)) } = link_to 'Tickets', admin_conference_tickets_path(@conference.short_title) - + - if can? :manage, @conference.booths.build + %li + = link_to admin_conference_booths_path(@conference.short_title) do + %span.fa.fa-shopping-bag + Booths - if (can? :manage, @conference.targets.build) || (can? :manage, @conference.campaigns.build) %li %a @@ -139,8 +143,15 @@ = link_to(admin_conference_roles_path(@conference.short_title)) do %span.fa.fa-group Roles + - if can? :index, @conference.resources.new %li = link_to admin_conference_resources_path(@conference.short_title) do %span.fa.fa-pencil-square Resources + + - if can?(:index, PaperTrail::Version.new(conference_id: @conference.id, item_type: 'Event')) || can?(:index, PaperTrail::Version.new(conference_id: @conference.id, item_type: 'Registration')) + %li{:class=> active_nav_li(admin_conference_revision_history_path(@conference.short_title))} + = link_to(admin_conference_revision_history_path(@conference.short_title)) do + %span.fa.fa-history + Revision History diff --git a/app/views/layouts/_admin_sidebar_index.html.haml b/app/views/layouts/_admin_sidebar_index.html.haml index 4ae56dce..9a3d19f3 100644 --- a/app/views/layouts/_admin_sidebar_index.html.haml +++ b/app/views/layouts/_admin_sidebar_index.html.haml @@ -16,7 +16,7 @@ %span.fa.fa-cog Manage = conference.short_title - - if can? :create, Conference + - if can? :new, Conference.new %li = link_to(new_admin_conference_path) do %span.fa.fa-plus @@ -27,8 +27,13 @@ = link_to(admin_users_path) do %span.fa.fa-user Users - - if can? :index, PaperTrail::Version.new(item_type: 'User') + - if can? :index, PaperTrail::Version %li = link_to(admin_revision_history_path) do %span.fa.fa-history Revision History + - if ENV['ORGANIZATIONS_ENABLED'] == 'true' + %li + = link_to(admin_organizations_path) do + %span.fa.fa-group + Organizations diff --git a/app/views/layouts/_navigation.html.haml b/app/views/layouts/_navigation.html.haml index 48066cd7..9995123b 100644 --- a/app/views/layouts/_navigation.html.haml +++ b/app/views/layouts/_navigation.html.haml @@ -1,14 +1,23 @@ .navbar.navbar-default.navbar-fixed-top.nav-osem{role: 'navigation'} .container .navbar-header - %button{"data-target"=>".navbar-collapse", "data-toggle"=>"collapse", class: 'navbar-toggle', type: 'button'} + - if @conference && @conference.short_title.present? + %button{ "data-target"=>"#side-nav", "data-toggle"=>"collapse", class: 'navbar-toggle side-nav-btn', type: 'button' } + %span.sr-only Toggle navigation + %span.icon-bar + %span.icon-bar + %span.icon-bar + %button{"data-target"=>"#main-nav", "data-toggle"=>"collapse", class: 'navbar-toggle', type: 'button'} %span.sr-only Toggle navigation %span.icon-bar %span.icon-bar %span.icon-bar - = link_to (ENV['OSEM_NAME'] || 'OSEM'), root_path, class: 'navbar-brand', title: 'Open Source Event Manager' - .collapse.navbar-collapse + - if conference.nil? || conference.new_record? + = link_to (ENV['OSEM_NAME'] || 'OSEM'), root_path, class: 'navbar-brand', title: 'Open Source Event Manager' + - else + = link_to conference.organization.name, organizations_path, class: 'navbar-brand', title: 'Open Source Event Manager' + .collapse.navbar-collapse#main-nav - if content_for :splash_nav %ul.nav.navbar-nav#splash-nav = content_for :splash_nav @@ -60,13 +69,13 @@ .dropdown-menu - if ENV['OSEM_ICHAIN_ENABLED'] == 'true' = form_tag User.ichain_login_url do - = text_field_tag 'username', nil, id: 'user_ichain_email_dd', placeholder: 'Username' - = password_field_tag 'password', nil, id: 'user_ichain_password_dd', placeholder: 'Password' + = text_field_tag 'username', nil, id: 'user_ichain_email_dd', class: 'form-control', placeholder: 'Username' + = password_field_tag 'password', nil, id: 'user_ichain_password_dd', class: 'form-control', placeholder: 'Password' %button.btn.btn-success.btn-block Sign in - else = form_tag new_user_session_path do - = text_field_tag 'user[login]', nil, id: 'user_login_dd', placeholder: 'Username / E-Mail' - = password_field_tag 'user[password]', nil, id: 'user_password_dd', placeholder: 'Password' + = text_field_tag 'user[login]', nil, id: 'user_login_dd', class: 'form-control', placeholder: 'Username / E-Mail' + = password_field_tag 'user[password]', nil, id: 'user_password_dd', class: 'form-control', placeholder: 'Password' %p.text-right %small %label{for: 'user_remember_me'} Remember me diff --git a/app/views/layouts/_user_menu.html.haml b/app/views/layouts/_user_menu.html.haml index 3fb32273..10d4c386 100644 --- a/app/views/layouts/_user_menu.html.haml +++ b/app/views/layouts/_user_menu.html.haml @@ -12,6 +12,15 @@ = link_to(conference_program_proposals_path(@conference.short_title)) do %span.fa.fa-comment My Submissions + %li + = link_to(conference_program_tracks_path(@conference.short_title)) do + %span.fa.fa-road + My Tracks +-if @conference && @conference.program && (@conference.program.cfps.for_booths.try(:open?) || current_user.booths.where(conference_id: @conference.id).count > 0) + %li + = link_to (conference_booths_path(@conference.short_title)) do + %span.fa.fa-shopping-bag + My Booth Requests %li - if ENV['OSEM_ICHAIN_ENABLED'] == 'true' = link_to(destroy_user_ichain_session_path, method: 'delete') do @@ -28,10 +37,10 @@ = link_to(admin_conferences_path()) do %span.fa.fa-home Administration - - if can? :create, Conference + - if can? :new, Conference.new =link_to(new_admin_conference_path) do %span.fa.fa-plus - Create Conference + New Conference -if @conference and @conference.id and can? :show, @conference %li = link_to(admin_conference_path(@conference.short_title)) do @@ -44,8 +53,13 @@ = link_to(admin_users_path) do %span.fa.fa-user Users -- if can? :index, PaperTrail::Version.new(item_type: 'User') +- if can? :index, PaperTrail::Version %li = link_to(admin_revision_history_path) do %span.fa.fa-history Revision History + - if ENV['ORGANIZATIONS_ENABLED'] == 'true' + %li + = link_to(admin_organizations_path) do + %span.fa.fa-group + Organizations diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 09be2523..c1a6ce78 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -21,7 +21,7 @@ = yield(:head) %body - = render 'layouts/navigation' + = render 'layouts/navigation', conference: @conference -# Admin area - if controller.class.name.split("::").first=="Admin" = render 'layouts/admin' diff --git a/app/views/mailbot/ticket_confirmation_template.text.erb b/app/views/mailbot/ticket_confirmation_template.text.erb new file mode 100644 index 00000000..08f5175a --- /dev/null +++ b/app/views/mailbot/ticket_confirmation_template.text.erb @@ -0,0 +1,8 @@ +Dear <%= @user.name %>, + +Thanks! You have successfully booked <%= @ticket_purchase.quantity %> <%= @ticket_purchase.ticket.title %> ticket(s) for the event <%= @conference.title %>. Your transaction id is <%= @ticket_purchase.id %>. + +Please, find the ticket(s) pdf attached. + +Best wishes, +<%= @conference.title %> Team diff --git a/app/views/organizations/index.html.haml b/app/views/organizations/index.html.haml new file mode 100644 index 00000000..b2794bd8 --- /dev/null +++ b/app/views/organizations/index.html.haml @@ -0,0 +1,16 @@ +.container + .row + .col-md-12.page-header + %h1 + Organizations + .btn-group.pull-right + / = link_to 'Add new', new_organization_path, class: 'btn btn-mini btn-success' + - @organizations.each do |organization| + .col-md-4 + .thumbnail + = image_tag(organization.picture.thumb.url, width: '20%') + .caption + %h4 + = organization.name + %button.btn.btn-success Conferences + / = link_to 'Edit', edit_organization_path(organization), class: 'btn btn-mini btn-default' diff --git a/app/views/physical_tickets/index.html.haml b/app/views/physical_tickets/index.html.haml new file mode 100644 index 00000000..cb0710ad --- /dev/null +++ b/app/views/physical_tickets/index.html.haml @@ -0,0 +1,42 @@ +.container + .row + .col-md-12.page-header + %h2 + Tickets + .text-muted + Your tickets for the conference + + .col-md-12 + - if @physical_tickets.present? + %table.table.table-bordered.table-striped.table-hover#roles + %thead + %th ID + %th Type + %th User + %th Actions + %tbody + - @physical_tickets.each do |physical_ticket| + %tr + %td= physical_ticket.id + %td= physical_ticket.ticket.title + %td= physical_ticket.user.name + %td + .btn-group + = link_to 'Show', + conference_physical_ticket_path(@conference.short_title, + physical_ticket.token), + class: 'btn btn-primary' + = link_to 'Generate PDF', + conference_physical_ticket_path(@conference.short_title, + physical_ticket.token, + format: :pdf), + class: 'button btn btn-default btn-info' + - else + %h5 No Tickets found! + .row + .col-md-12 + - if @unpaid_ticket_purchases.any? + .h3 + You have unpaid tickets! + %small + = link_to "Pay them here", conference_ticket_purchases_path diff --git a/app/views/physical_tickets/show.html.haml b/app/views/physical_tickets/show.html.haml new file mode 100644 index 00000000..40513ae8 --- /dev/null +++ b/app/views/physical_tickets/show.html.haml @@ -0,0 +1,78 @@ +.container + .row + .col-md-12 + .page-header + %h1 + Ticket for + = @conference.title + %p.text-muted + - if @conference.venue + at + %strong + #{@conference.venue.name}, + #{@conference.venue.street}, + #{@conference.venue.city} / #{@conference.venue.country_name}. + %small + = date_string(@conference.start_date, @conference.end_date) + .row + .col-md-5.box.well + %h3.text-center + Ticket Holder + %p.text-left + %strong + Name + %br + = @user.name + %br + %br + %strong + Email + %br + = @user.email + .col-md-5.col-md-offset-2.box.well + - if @conference.picture? + - width = @conference.picture.image[:width] + - height = @conference.picture.image[:height] + - if 10 * width > 15 * height + = image_tag(@conference.picture_url, width: '150') + - else + = image_tag(@conference.picture_url, height: '100') + - else + = image_tag('/img/osem-logo.png', class: 'img-responsive') + %p.text-left + %br + %strong + Organization + %br + = @conference.organization.name + .col-md-5.box.well + %p.text-left + %strong + Event + %br + = @conference.title + %br + = @conference.start_date.strftime('%B %d, %Y') + %br + %br + %strong + Ticket + %br + = @physical_ticket.ticket.title + %br + %br + %strong + Ticket Ref. + %br + = @physical_ticket.ticket_purchase.id + %br + .col-md-5.col-md-offset-2.box.well + = image_tag(@qrcode_image.to_data_url, class: 'img-responsive qr-image') + .row + .col-md-12 + %p.text-left + = link_to 'Generate PDF', + conference_physical_ticket_path(@conference.short_title, + @physical_ticket.token, + format: :pdf), + class: 'button btn btn-default btn-info' diff --git a/app/views/proposals/_encouragement_text.html.haml b/app/views/proposals/_encouragement_text.html.haml index a0f513cd..48fcd045 100644 --- a/app/views/proposals/_encouragement_text.html.haml +++ b/app/views/proposals/_encouragement_text.html.haml @@ -2,9 +2,9 @@ - if @program.event_types.any? You can submit proposals for = "#{event_types(@conference)}." - - if @program.tracks.any? + - if @program.tracks.confirmed.cfp_active.any? Proposals should fit in one of the - = "#{pluralize(@program.tracks.count, 'track')}:" + = "#{pluralize(@program.tracks.confirmed.cfp_active.count, 'track')}:" = "#{tracks(@conference)}." - if @program.cfp_open? The submission period has begun diff --git a/app/views/proposals/_proposal_form.html.haml b/app/views/proposals/_proposal_form.html.haml index f31651a1..4b66240c 100644 --- a/app/views/proposals/_proposal_form.html.haml +++ b/app/views/proposals/_proposal_form.html.haml @@ -6,10 +6,7 @@ = speaker_selector_input f - - if @program.tracks.any? - = f.input :track_id, as: :select, - collection: @program.tracks.map {|track| ["#{track.name}", track.id] }, - include_blank: true + = track_selector_input f = f.input :event_type_id, as: :select, collection: @conference.program.event_types.map {|type| ["#{type.title} - #{show_time(type.length)}", type.id, diff --git a/app/views/proposals/index.html.haml b/app/views/proposals/index.html.haml index 06154d47..5ed332dc 100644 --- a/app/views/proposals/index.html.haml +++ b/app/views/proposals/index.html.haml @@ -55,7 +55,7 @@ %p Knowing the number of visitors for the conference helps the organizers plan better. - %table.table.table-striped + %table.table.table-striped#events - @events.each do |event| %tr %td{style: "padding:20px 8px 20px 8px;"} diff --git a/app/views/proposals/show.html.haml b/app/views/proposals/show.html.haml index 2d38d3a8..c181e283 100644 --- a/app/views/proposals/show.html.haml +++ b/app/views/proposals/show.html.haml @@ -114,7 +114,7 @@ %dl %dt Start Time: %dd - = event.program.selected_event_schedules.find_by(event: event).start_time.strftime("%Y %B %e %H:%M") + = event.program.selected_event_schedules.find { |es| es.event == event }.start_time.strftime("%Y %B %e %H:%M") %br %dt Room: %dd diff --git a/app/views/schedules/_carousel.html.haml b/app/views/schedules/_carousel.html.haml index ce28e909..dde7e72c 100644 --- a/app/views/schedules/_carousel.html.haml +++ b/app/views/schedules/_carousel.html.haml @@ -29,7 +29,7 @@ %td.room{ style: "height: #{ td_height(@rooms) }px;" } .room.elipsis.break-words{ style: "-webkit-line-clamp: #{ room_lines(@rooms) }; height: #{ room_height(@rooms) }px;" } = room.name - - event_schedules = room.event_schedules.select{ |e| (e.schedule_id == @conference.program.selected_schedule.id) && (e.end_time > start_time) && (e.start_time <= (start_time + hrs_per_slide.hour)) } + - event_schedules = room.event_schedules.select{ |e| @selected_schedules_ids.include?(e.schedule_id) && (e.end_time > start_time) && (e.start_time <= (start_time + hrs_per_slide.hour)) } - (1..intervals).each do |i| - if span > 1 - span -= 1 diff --git a/app/views/ticket_purchases/index.html.haml b/app/views/ticket_purchases/index.html.haml new file mode 100644 index 00000000..c9255c37 --- /dev/null +++ b/app/views/ticket_purchases/index.html.haml @@ -0,0 +1,30 @@ +.container + .row + .col-md-12.page-header + %h2 + Ticket Purchases + .text-muted + Your unpaid ticket purchases for the conference + + .col-md-12 + - if @unpaid_ticket_purchases.present? + %table.table.table-bordered.table-striped.table-hover#roles + %thead + %th ID + %th Type + %th Quantity + %th Date + %th Actions + %tbody + - @unpaid_ticket_purchases.each do |ticket_purchase| + %tr + %td= ticket_purchase.id + %td= ticket_purchase.ticket.title + %td= ticket_purchase.quantity + %td= ticket_purchase.created_at.strftime('%B %d, %Y') + %td + .btn-group + = link_to 'Pay', new_conference_payment_path, + class: 'btn btn-primary' + - else + %h5 You don't have any unpaid ticket purchase! diff --git a/app/views/tickets/_ticket.html.haml b/app/views/tickets/_ticket.html.haml index e3985773..8f5fc82b 100644 --- a/app/views/tickets/_ticket.html.haml +++ b/app/views/tickets/_ticket.html.haml @@ -1,5 +1,5 @@ %tr - %td.col-sm-8.col-md-6 + %td.col-sm-8.col-md-4 .media .media-body %h4.media-heading @@ -7,9 +7,14 @@ %h5.media-heading - unless ticket.description.blank? = markdown(ticket.description) + %td.col-sm-1.col-md-2.text-center + = ticket.registration_ticket? ? 'Yes' : 'No' %td.col-sm-1.col-md-1 - = text_field_tag("tickets[][#{ticket.id}]", 0, - type: 'number', min: 0, class: "form-control quantity", 'data-id' => ticket.id) + - options = { type: 'number', min: 0, class: "form-control quantity", 'data-id' => ticket.id } + - if ticket.registration_ticket? + - options[:max] = 1 + - options[:disabled] = current_user.tickets.for_registration(ticket.conference).present? + = text_field_tag("tickets[][#{ticket.id}]", 0, options) %td.col-sm-1.col-md-1.text-center = ticket.price.symbol %span{id: "price_#{ticket.id}"} diff --git a/app/views/tickets/index.html.haml b/app/views/tickets/index.html.haml index dc67f529..cba13ea5 100644 --- a/app/views/tickets/index.html.haml +++ b/app/views/tickets/index.html.haml @@ -14,6 +14,7 @@ %thead %tr %th Ticket + %th Registration Ticket %th Quantity %th Price %th Total @@ -21,6 +22,7 @@ - @conference.tickets.each do |ticket| = render partial: 'ticket', f: f, locals: {ticket: ticket} %tr + %td %td %td %td.col-sm-1.col-md-1.text-center @@ -46,4 +48,4 @@ .col-md-13 %p.text-muted.text-center %small - * Getting a ticket is mandatory. Your participation will not be valid until you get a ticket. + * Getting a registration ticket is mandatory. Your participation will not be valid until you get a registration ticket. diff --git a/app/views/tracks/_form.html.haml b/app/views/tracks/_form.html.haml new file mode 100644 index 00000000..42832e80 --- /dev/null +++ b/app/views/tracks/_form.html.haml @@ -0,0 +1,20 @@ +.container + .row + .col-md-12 + .page-header + %h1 + - if @track.new_record? + New + = @track.name + Track + .row + .col-md-12 + = semantic_form_for(@track, url: (@track.new_record? ? conference_program_tracks_path(@conference.short_title) : conference_program_track_path(@conference.short_title, @track))) do |f| + = f.input :name + = f.input :short_name, hint: "A short and unique handle for the track, using only letters, numbers, underscores, and dashes. This will be used to identify the track in URLs etc. Example: 'my_awesome_track'", input_html: { required: 'required', pattern: '[a-zA-Z0-9_-]+', title: 'Only letters, numbers, underscores, and dashes.' } + = f.input :color, input_html: {size: 6, type: 'color'}, required: true + = 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 :description, input_html: {rows: 2, data: { provide: 'markdown-editable' } }, required: true, hint: "This will be public #{markdown_hint}".html_safe + = f.input :relevance, input_html: {rows: 5, data: { provide: 'markdown-editable' } }, required: true, hint: "Please explain here how this track relates to the conference, how you are related to its content and why we should accept it. #{markdown_hint}".html_safe + = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/tracks/index.html.haml b/app/views/tracks/index.html.haml new file mode 100644 index 00000000..b20a3ca9 --- /dev/null +++ b/app/views/tracks/index.html.haml @@ -0,0 +1,82 @@ +.container + .row + .col-md-12.page-header + %h1 + Track requests for + %span.notranslate + = @conference.title + + .row + .col-md-12 + %p.text-right + = link_to '#status-help', class: 'btn btn-default', "data-toggle"=>"collapse" do + Help? + .collapse#status-help + %p + %strong + What happens next with my track request? + %p + If you submit a track request, the conference organizers will review it and either accept or reject it. + %br + If your track request is accepted, the conference organizers expect you to confirm that you will be able to hold it. + Then you will be assigned the Track organizer role. + %br + If your track request is rejected, you can either live with that or adapt it and resubmit it for review again. + %br + If something changes and you can't organize the track any more, you should withdraw it. + + - if @tracks.any? + .row + .col-md-12 + %table.table.table-striped#tracks + %th + %th + %th + %th From + %th To + %th Room + %th + - @tracks.each do |track| + %tr + %td{style: "padding:15px 0px 0px 8px;"} + - if %w(new to_accept to_reject).include? track.state + %span{ title: 'In review', class: 'fa fa-eye' } + - elsif track.state == 'accepted' + %span{ title: 'Accepted', class: 'fa fa-check text-muted' } + - elsif track.state == 'confirmed' + %spam{ title: 'Confirmed', class: 'fa fa-check text-success' } + - elsif %w(rejected withdrawn canceled).include? track.state + %span{ title: track.state.humanize, class: 'fa fa-ban'} + %td{style: "padding: 15px 0px 0px 0px;"} + = link_to conference_program_track_path(@conference.short_title, track), class: 'btn' do + %span.label{style: "background-color: #{track.color}; color: #{ contrast_color(track.color) }"} + = track.name + %td + = markdown(truncate(track.description)) + %td + = track.start_date.strftime('%A, %B %-d. %Y') if track.start_date + %td + = track.end_date.strftime('%A, %B %-d. %Y') if track.end_date + %td + = track.try(:room).try(:name) + %td + .pull-right + - if track.transition_possible? :confirm + = link_to 'Confirm', confirm_conference_program_track_path(@conference.short_title, track), + method: :patch, class: 'btn btn-mini btn-success', id: "confirm_track_#{track.id}" + - if track.transition_possible? :withdraw + = link_to 'Withdraw', withdraw_conference_program_track_path(@conference.short_title, track), + method: :patch, data: { confirm: 'Are you sure you want to withdraw this track request?' }, + class: 'btn btn-mini btn-warning', id: "withdraw_track_request_#{track.id}" + - if track.transition_possible? :restart + = link_to 'Re-Submit', restart_conference_program_track_path(@conference.short_title, track), + method: :patch, class: 'btn btn-mini btn-success', id: "resubmit_track_request_#{track.id}" + - if can? :edit, track + = link_to 'Edit', edit_conference_program_track_path(@conference.short_title, track), class: 'btn btn-default' + - if current_user.has_role? :track_organizer, track + = link_to 'Manage', admin_conference_program_track_path(@conference.short_title, track), class: 'btn btn-default' + + .row + .col-md-12 + - if can? :new, @program.tracks.new + = link_to "New Track request", new_conference_program_track_path(@conference.short_title), class: 'btn btn-success pull-right' diff --git a/app/views/tracks/show.html.haml b/app/views/tracks/show.html.haml new file mode 100644 index 00000000..c81d60ec --- /dev/null +++ b/app/views/tracks/show.html.haml @@ -0,0 +1,47 @@ +.container + .row + .col-md-12 + .page-header + %h2 + = @track.name + Track + .btn-group.pull-right + - if can? :edit, @track + = link_to 'Edit Track request', edit_conference_program_track_path(@conference.short_title, @track), class: 'btn btn-primary' + - if current_user.has_role? :track_organizer, @track + = link_to 'Manage', admin_conference_program_track_path(@conference.short_title, @track), class: 'btn btn-default' + .row + .col-md-8 + %dl.dl-horizontal + %dt + Color: + %dd + %span.label{style: "background-color: #{@track.color}; color: #{ contrast_color(@track.color) }"} + = @track.color + %dt + State: + %dd + - if %w(new to_accept to_reject).include? @track.state + New + - else + = @track.state.humanize + %dt + Start date: + %dd + = @track.start_date.strftime('%A, %B %-d. %Y') if @track.start_date + %dt + End date: + %dd + = @track.end_date.strftime('%A, %B %-d. %Y') if @track.end_date + %dt + Room: + %dd + = @track.room.try(:name) + %dt + Description + %dd + = markdown(@track.description) + %dt + Relevance + %dd + = markdown(@track.relevance) diff --git a/bootstrap.sh b/bootstrap.sh index 42766366..92a8d746 100644 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -2,15 +2,17 @@ pushd /vagrant echo -e "\ninstalling required software packages...\n" -zypper -q -n install update-alternatives ruby-devel make gcc gcc-c++ \ +zypper -q ar -f http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_42.2/devel:languages:ruby.repo +zypper -q --gpg-auto-import-keys --non-interactive ref +zypper -q -n install update-alternatives ruby2.4-devel make gcc gcc-c++ \ libxml2-devel libxslt-devel nodejs screen mariadb \ - libmysqld-devel sqlite3-devel imagemagick + libmysqld-devel sqlite3-devel ImageMagick echo -e "\ndisabling versioned gem binary names...\n" echo 'install: --no-format-executable' >> /etc/gemrc echo -e "\ninstalling bundler...\n" -gem install bundler +gem.ruby2.4 install bundler echo -e "\ninstalling your bundle...\n" su - vagrant -c "cd /vagrant/; bundle install --quiet" @@ -26,7 +28,7 @@ if [ ! -f /vagrant/config/database.yml ] && [ -f /vagrant/config/database.yml.ex echo -e "WARNING: Please make sure this database works in this vagrant box!\n\n" fi else - echo -e "\nnWARNING: You have already configured your database in config/database.yml." + echo -e "\n\nWARNING: You have already configured your database in config/database.yml." echo -e "WARNING: Please make sure this configuration works in this vagrant box!\n\n" fi diff --git a/config/application.rb b/config/application.rb index 9123e5bb..07ba40f3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,7 +25,6 @@ module Osem # Activate observers that should always be running. # config.active_record.observers = :cacher, :garbage_collector, :forum_observer - config.active_record.observers = :revision_observer # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. diff --git a/config/database.yml.docker b/config/database.yml.docker new file mode 100644 index 00000000..60ef8947 --- /dev/null +++ b/config/database.yml.docker @@ -0,0 +1,7 @@ +production: + adapter: mysql2 + host: <%= ENV['DATABASE_HOST'] %> + port: <%= ENV['DATABASE_PORT'] %> + username: <%= ENV['MYSQL_USER'] %> + password: <%= ENV['MYSQL_PASSWORD'] %> + database: <%= ENV['MYSQL_DATABASE'] %> diff --git a/config/environments/production.rb b/config/environments/production.rb index d2b43a46..4e2c2e79 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -36,7 +36,7 @@ Osem::Application.configure do # config.force_ssl = true # See everything in the log (default is :info) - # config.log_level = :debug + config.log_level = :info # Prepend all log lines with the following tags # config.log_tags = [ :subdomain, :uuid ] @@ -77,8 +77,9 @@ Osem::Application.configure do user_name: ENV['OSEM_SMTP_USERNAME'], password: ENV['OSEM_SMTP_PASSWORD'], authentication: ENV['OSEM_SMTP_AUTHENTICATION'].try(:to_sym), - domain: ENV['OSEM_SMTP_DOMAIN'], - enable_starttls_auto: true + domain: ENV['OSEM_SMTP_DOMAIN'], + enable_starttls_auto: ENV['OSEM_SMTP_ENABLE_STARTTLS_AUTO'], + openssl_verify_mode: ENV['OSEM_SMTP_OPENSSL_VERIFY_MODE'] } # Set the secret_key_base from the env, if not set by any other means diff --git a/config/initializers/domain_constraint.rb b/config/initializers/domain_constraint.rb new file mode 100644 index 00000000..5b35e5c7 --- /dev/null +++ b/config/initializers/domain_constraint.rb @@ -0,0 +1,6 @@ +class DomainConstraint + def self.matches?(request) + domains = Conference.where.not(custom_domain: nil).pluck(:custom_domain) + domains.include?(request.domain) + end +end diff --git a/config/routes.rb b/config/routes.rb index 9fa1c29a..6e8a66de 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,9 @@ Osem::Application.routes.draw do + constraints DomainConstraint do + get '/', to: 'conferences#show' + end + if ENV['OSEM_ICHAIN_ENABLED'] == 'true' devise_for :users, controllers: { registrations: :registrations } else @@ -15,18 +19,28 @@ Osem::Application.routes.draw do mount LetterOpenerWeb::Engine, at: "/letter_opener" end - resources :users, except: [:new, :index, :create, :destroy] + resources :users, except: [:new, :index, :create, :destroy] do + resources :openids, only: :destroy + end namespace :admin do + resources :organizations do + member do + get :admins + post :assign_org_admins + delete :unassign_org_admins + end + end resources :users do member do patch :toggle_confirmation end end + resource :ticket_scanning, only: [:create] resources :comments, only: [:index] resources :conferences do resource :contact, except: [:index, :new, :create, :show, :destroy] - resources :schedules, only: [:index, :create, :show, :update, :destroy] + resources :schedules, except: [:edit, :update] resources :event_schedules, only: [:create, :update, :destroy] get 'commercials/render_commercial' => 'commercials#render_commercial' resources :commercials, only: [:index, :create, :update, :destroy] @@ -34,6 +48,19 @@ Osem::Application.routes.draw do get '/volunteers' => 'volunteers#index', as: 'volunteers_info' patch '/volunteers' => 'volunteers#update', as: 'volunteers_update' + resources :booths do + member do + patch :accept + patch :restart + patch :to_accept + patch :reject + patch :reset + patch :to_reject + patch :cancel + patch :confirm + end + end + resources :registrations, except: [:create, :new] do member do patch :toggle_attendance @@ -50,8 +77,25 @@ Osem::Application.routes.draw do end resource :registration_period resource :program do - resource :cfp - resources :tracks + resources :cfps + resources :tracks do + member do + patch :toggle_cfp_inclusion + patch :restart + patch :to_accept + patch :accept + patch :confirm + patch :to_reject + patch :reject + patch :cancel + patch :update_selected_schedule + end + resources :roles, only: [:show, :edit, :update] do + member do + post :toggle_user + end + end + end resources :event_types resources :difficulty_levels resources :events do @@ -78,7 +122,8 @@ Osem::Application.routes.draw do resources :targets, except: [:show] resources :campaigns, except: [:show] resources :emails, only: [:show, :update, :index] - resources :roles, except: [ :new, :create ] do + resources :physical_tickets, only: [:index] + resources :roles, except: [:new, :create] do member do post :toggle_user end @@ -96,14 +141,23 @@ Osem::Application.routes.draw do patch :update_conference end end + + get '/revision_history' => 'versions#index' end get '/revision_history' => 'versions#index' get '/revision_history/:id/revert_object' => 'versions#revert_object', as: 'revision_history_revert_object' get '/revision_history/:id/revert_attribute' => 'versions#revert_attribute', as: 'revision_history_revert_attribute' end - + resources :organizations, only: [:index] resources :conferences, only: [:index, :show] do + resources :booths do + member do + patch :withdraw + patch :confirm + patch :restart + end + end resource :program, only: [] do resources :proposals, except: :destroy do get 'commercials/render_commercial' => 'commercials#render_commercial' @@ -111,18 +165,25 @@ Osem::Application.routes.draw do member do get :registrations patch '/withdraw' => 'proposals#withdraw' - get :registrations patch '/confirm' => 'proposals#confirm' patch '/restart' => 'proposals#restart' end end + resources :tracks, except: :destroy do + member do + patch :restart + patch :confirm + patch :withdraw + end + end end # TODO: change conference_registrations to singular resource resource :conference_registration, path: 'register' resources :tickets, only: [:index] - resources :ticket_purchases, only: [:create, :destroy] + resources :ticket_purchases, only: [:create, :destroy, :index] resources :payments, only: [:index, :new, :create] + resources :physical_tickets, only: [:index, :show] resource :subscriptions, only: [:create, :destroy] resource :schedule, only: [:show] do member do diff --git a/db/migrate/20140701123203_add_events_per_week_to_conference.rb b/db/migrate/20140701123203_add_events_per_week_to_conference.rb index 5c2eb9d4..36bbf40d 100644 --- a/db/migrate/20140701123203_add_events_per_week_to_conference.rb +++ b/db/migrate/20140701123203_add_events_per_week_to_conference.rb @@ -22,50 +22,50 @@ class AddEventsPerWeekToConference < ActiveRecord::Migration if event conference = TempConference.find_by_id(event.conference_id) if conference - week = event_version.created_at.end_of_week + week = event_version.created_at.end_of_week - no_events = { - new: 0, - withdrawn: 0, - unconfirmed: 0, - confirmed: 0, - canceled: 0, - rejected: 0, - } - - if !conference.events_per_week - conference.events_per_week = { - week => no_events + no_events = { + new: 0, + withdrawn: 0, + unconfirmed: 0, + confirmed: 0, + canceled: 0, + rejected: 0, } - elsif !conference.events_per_week[week] - conference.events_per_week[week] = no_events - end - if event_version.object_changes && - event_version.event == 'create' - - # Increment the new state - conference.events_per_week[week][:new] += 1 - elsif event_version.object_changes && - event_version.object_changes[:state] - - prev_state = event_version.object_changes[:state][0].to_sym - next_state = event_version.object_changes[:state][1].to_sym - - # Backward compatibility: deprecated state :review now :new - if prev_state == :review - prev_state = :new - elsif next_state == :review - next_state = :new + if !conference.events_per_week + conference.events_per_week = { + week => no_events + } + elsif !conference.events_per_week[week] + conference.events_per_week[week] = no_events end - # Increment the next state - conference.events_per_week[week][next_state] += 1 + if event_version.object_changes && + event_version.event == 'create' - # Decrement the previous state - conference.events_per_week[week][prev_state] -= 1 - end - conference.save + # Increment the new state + conference.events_per_week[week][:new] += 1 + elsif event_version.object_changes && + event_version.object_changes[:state] + + prev_state = event_version.object_changes[:state][0].to_sym + next_state = event_version.object_changes[:state][1].to_sym + + # Backward compatibility: deprecated state :review now :new + if prev_state == :review + prev_state = :new + elsif next_state == :review + next_state = :new + end + + # Increment the next state + conference.events_per_week[week][next_state] += 1 + + # Decrement the previous state + conference.events_per_week[week][prev_state] -= 1 + end + conference.save end end end diff --git a/db/migrate/20141104131625_generate_username.rb b/db/migrate/20141104131625_generate_username.rb index d7f4bdda..e1ddf111 100644 --- a/db/migrate/20141104131625_generate_username.rb +++ b/db/migrate/20141104131625_generate_username.rb @@ -7,9 +7,7 @@ class GenerateUsername < ActiveRecord::Migration TempUser.all.each do |user| if user.username.blank? username = user.email.split('@')[0] - if TempUser.find_by(username: username) - username = username + user.id.to_s - end + username += user.id.to_s if TempUser.find_by(username: username) user.update_attributes(username: username) end end diff --git a/db/migrate/20161229080315_add_comments_count_to_events.rb b/db/migrate/20161229080315_add_comments_count_to_events.rb new file mode 100644 index 00000000..e1d6ebec --- /dev/null +++ b/db/migrate/20161229080315_add_comments_count_to_events.rb @@ -0,0 +1,10 @@ +class AddCommentsCountToEvents < ActiveRecord::Migration + def change + add_column :events, :comments_count, :integer, default: 0, null: false + + Event.find_each do |event| + comments_count = event.comment_threads.count + event.update_attribute(:comments_count, comments_count) unless comments_count.zero? + end + end +end diff --git a/db/migrate/20170108053041_add_default_to_revision_in_conference.rb b/db/migrate/20170108053041_add_default_to_revision_in_conference.rb new file mode 100644 index 00000000..1c3571de --- /dev/null +++ b/db/migrate/20170108053041_add_default_to_revision_in_conference.rb @@ -0,0 +1,5 @@ +class AddDefaultToRevisionInConference < ActiveRecord::Migration + def change + change_column :conferences, :revision, :integer, default: 0, null: false + end +end diff --git a/db/migrate/20170419132148_add_week_to_ticket_purchases.rb b/db/migrate/20170419132148_add_week_to_ticket_purchases.rb new file mode 100644 index 00000000..0862eb16 --- /dev/null +++ b/db/migrate/20170419132148_add_week_to_ticket_purchases.rb @@ -0,0 +1,15 @@ +class AddWeekToTicketPurchases < ActiveRecord::Migration + class TmpTicketPurchase < ActiveRecord::Base + self.table_name = 'ticket_purchases' + end + + def change + add_column :ticket_purchases, :week, :integer + + TmpTicketPurchase.reset_column_information + TmpTicketPurchase.find_each do |purchase| + purchase.week = purchase.created_at ? purchase.created_at.strftime('%W') : 0 + purchase.save! + end + end +end diff --git a/db/migrate/20170516190048_create_booths.rb b/db/migrate/20170516190048_create_booths.rb new file mode 100644 index 00000000..d2aa0be6 --- /dev/null +++ b/db/migrate/20170516190048_create_booths.rb @@ -0,0 +1,16 @@ +class CreateBooths < ActiveRecord::Migration + def change + create_table :booths do |t| + t.string :title + t.text :description + t.text :reasoning + t.string :state + t.string :logo_link + t.string :website_url + t.text :submitter_relationship + t.references :conference + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170529215453_create_organizations.rb b/db/migrate/20170529215453_create_organizations.rb new file mode 100644 index 00000000..0fa5450b --- /dev/null +++ b/db/migrate/20170529215453_create_organizations.rb @@ -0,0 +1,9 @@ +class CreateOrganizations < ActiveRecord::Migration + def change + create_table :organizations do |t| + t.string :name, null: false + t.text :description + t.string :picture + end + end +end diff --git a/db/migrate/20170530072155_add_type_to_cfps.rb b/db/migrate/20170530072155_add_type_to_cfps.rb new file mode 100644 index 00000000..50423281 --- /dev/null +++ b/db/migrate/20170530072155_add_type_to_cfps.rb @@ -0,0 +1,15 @@ +class AddTypeToCfps < ActiveRecord::Migration + class TmpCfp < ActiveRecord::Base + self.table_name = 'cfps' + end + + def change + add_column :cfps, :cfp_type, :string + + TmpCfp.reset_column_information + TmpCfp.find_each do |cfp| + cfp.cfp_type = 'events' + cfp.save! + end + end +end diff --git a/db/migrate/20170530112510_create_booth_requests.rb b/db/migrate/20170530112510_create_booth_requests.rb new file mode 100644 index 00000000..75e73a93 --- /dev/null +++ b/db/migrate/20170530112510_create_booth_requests.rb @@ -0,0 +1,11 @@ +class CreateBoothRequests < ActiveRecord::Migration + def change + create_table :booth_requests do |t| + t.references :booth, index: true, foreign_key: true + t.references :user, index: true, foreign_key: true + t.string :role + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170531094819_move_conferences_to_organizations.rb b/db/migrate/20170531094819_move_conferences_to_organizations.rb new file mode 100644 index 00000000..e70acf16 --- /dev/null +++ b/db/migrate/20170531094819_move_conferences_to_organizations.rb @@ -0,0 +1,24 @@ +class MoveConferencesToOrganizations < ActiveRecord::Migration + class TempConference < ActiveRecord::Base + self.table_name = 'conferences' + end + + class TempOrganization < ActiveRecord::Base + self.table_name = 'organizations' + end + + def change + add_reference :conferences, :organization, index: true + + TempConference.reset_column_information + if TempConference.count != 0 + organization = TempOrganization.create(name: 'organization', description: 'Default organization') + TempConference.all.each do |conference| + conference.organization_id = organization.id + conference.save! + end + end + + add_foreign_key :conferences, :organizations, null: false, on_delete: :cascade + end +end diff --git a/db/migrate/20170603095900_create_physical_tickets.rb b/db/migrate/20170603095900_create_physical_tickets.rb new file mode 100644 index 00000000..5e0c1d98 --- /dev/null +++ b/db/migrate/20170603095900_create_physical_tickets.rb @@ -0,0 +1,9 @@ +class CreatePhysicalTickets < ActiveRecord::Migration + def change + create_table :physical_tickets do |t| + t.integer :ticket_purchase_id, null: false + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170629162450_add_short_name_to_tracks.rb b/db/migrate/20170629162450_add_short_name_to_tracks.rb new file mode 100644 index 00000000..cbc6b431 --- /dev/null +++ b/db/migrate/20170629162450_add_short_name_to_tracks.rb @@ -0,0 +1,39 @@ +class AddShortNameToTracks < ActiveRecord::Migration + class TmpProgram < ActiveRecord::Base + self.table_name = 'programs' + end + + class TmpTrack < ActiveRecord::Base + self.table_name = 'tracks' + end + + def change + add_column :tracks, :short_name, :string + + TmpTrack.reset_column_information + + TmpProgram.find_each do |program| + # Keeps count of how many times we've encountered a short_name + track_name_counter = {} + + TmpTrack.where(program_id: program.id).find_each do |track| + # Replace spaces with undercores and remove the non alphanumeric characters that aren't underscores or dashes + short_name = track.name.tr(' ', '_').tr('^a-zA-Z0-9_-', '') + + # If we've seen that short_name before then add the counter in the end to avoid collisions + if track_name_counter[short_name] + track_name_counter[short_name] += 1 + short_name += "_#{track_name_counter[short_name]}" + else + # Initialize the counter + track_name_counter[short_name] = 0 unless track_name_counter[short_name] + end + + track.short_name = short_name + track.save! + end + end + + change_column_null :tracks, :short_name, false + end +end diff --git a/db/migrate/20170629232817_add_ticket_layout_to_conferences.rb b/db/migrate/20170629232817_add_ticket_layout_to_conferences.rb new file mode 100644 index 00000000..c07f85c3 --- /dev/null +++ b/db/migrate/20170629232817_add_ticket_layout_to_conferences.rb @@ -0,0 +1,5 @@ +class AddTicketLayoutToConferences < ActiveRecord::Migration + def change + add_column :conferences, :ticket_layout, :integer, default: 0 + end +end diff --git a/db/migrate/20170705075039_add_state_cfp_active_and_submitter_reference_to_tracks.rb b/db/migrate/20170705075039_add_state_cfp_active_and_submitter_reference_to_tracks.rb new file mode 100644 index 00000000..9cb7f7de --- /dev/null +++ b/db/migrate/20170705075039_add_state_cfp_active_and_submitter_reference_to_tracks.rb @@ -0,0 +1,8 @@ +class AddStateCfpActiveAndSubmitterReferenceToTracks < ActiveRecord::Migration + def change + add_column :tracks, :state, :string + add_column :tracks, :cfp_active, :boolean + add_column :tracks, :submitter_id, :integer + add_index :tracks, :submitter_id + end +end diff --git a/db/migrate/20170711102511_create_ticket_scannings.rb b/db/migrate/20170711102511_create_ticket_scannings.rb new file mode 100644 index 00000000..d1879f68 --- /dev/null +++ b/db/migrate/20170711102511_create_ticket_scannings.rb @@ -0,0 +1,9 @@ +class CreateTicketScannings < ActiveRecord::Migration + def change + create_table :ticket_scannings do |t| + t.integer :physical_ticket_id, null: false + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170712120556_add_room_and_dates_to_tracks.rb b/db/migrate/20170712120556_add_room_and_dates_to_tracks.rb new file mode 100644 index 00000000..fb8a064b --- /dev/null +++ b/db/migrate/20170712120556_add_room_and_dates_to_tracks.rb @@ -0,0 +1,7 @@ +class AddRoomAndDatesToTracks < ActiveRecord::Migration + def change + add_reference :tracks, :room, index: true, foreign_key: true + add_column :tracks, :start_date, :date + add_column :tracks, :end_date, :date + end +end diff --git a/db/migrate/20170715131706_make_track_state_not_null_and_add_default_value.rb b/db/migrate/20170715131706_make_track_state_not_null_and_add_default_value.rb new file mode 100644 index 00000000..3d2520cf --- /dev/null +++ b/db/migrate/20170715131706_make_track_state_not_null_and_add_default_value.rb @@ -0,0 +1,14 @@ +class MakeTrackStateNotNullAndAddDefaultValue < ActiveRecord::Migration + class TmpTrack < ActiveRecord::Base + self.table_name = 'tracks' + end + + def change + TmpTrack.where(state: nil).each do |track| + track.state = 'confirmed' + track.save! + end + + change_column :tracks, :state, :string, null: false, default: 'new' + end +end diff --git a/db/migrate/20170720134353_make_track_cfp_active_not_null.rb b/db/migrate/20170720134353_make_track_cfp_active_not_null.rb new file mode 100644 index 00000000..0f75d046 --- /dev/null +++ b/db/migrate/20170720134353_make_track_cfp_active_not_null.rb @@ -0,0 +1,14 @@ +class MakeTrackCfpActiveNotNull < ActiveRecord::Migration + class TmpTrack < ActiveRecord::Base + self.table_name = 'tracks' + end + + def change + TmpTrack.where(cfp_active: nil).each do |track| + track.cfp_active = true + track.save! + end + + change_column_null :tracks, :cfp_active, false + end +end diff --git a/db/migrate/20170721001700_add_index_to_physical_tickets.rb b/db/migrate/20170721001700_add_index_to_physical_tickets.rb new file mode 100644 index 00000000..46a0b8c0 --- /dev/null +++ b/db/migrate/20170721001700_add_index_to_physical_tickets.rb @@ -0,0 +1,6 @@ +class AddIndexToPhysicalTickets < ActiveRecord::Migration + def change + add_column :physical_tickets, :token, :string + add_index :physical_tickets, :token, unique: true + end +end diff --git a/db/migrate/20170721184810_add_custom_domain_to_conferences.rb b/db/migrate/20170721184810_add_custom_domain_to_conferences.rb new file mode 100644 index 00000000..d5aef4fd --- /dev/null +++ b/db/migrate/20170721184810_add_custom_domain_to_conferences.rb @@ -0,0 +1,5 @@ +class AddCustomDomainToConferences < ActiveRecord::Migration + def change + add_column :conferences, :custom_domain, :string + end +end diff --git a/db/migrate/20170726065629_add_relevance_to_tracks.rb b/db/migrate/20170726065629_add_relevance_to_tracks.rb new file mode 100644 index 00000000..215200c1 --- /dev/null +++ b/db/migrate/20170726065629_add_relevance_to_tracks.rb @@ -0,0 +1,5 @@ +class AddRelevanceToTracks < ActiveRecord::Migration + def change + add_column :tracks, :relevance, :text + end +end diff --git a/db/migrate/20170727081731_add_include_booths_to_splashpages.rb b/db/migrate/20170727081731_add_include_booths_to_splashpages.rb new file mode 100644 index 00000000..da11ea8d --- /dev/null +++ b/db/migrate/20170727081731_add_include_booths_to_splashpages.rb @@ -0,0 +1,5 @@ +class AddIncludeBoothsToSplashpages < ActiveRecord::Migration + def change + add_column :splashpages, :include_booths, :boolean + end +end diff --git a/db/migrate/20170728182033_add_booth_limit_to_conferences.rb b/db/migrate/20170728182033_add_booth_limit_to_conferences.rb new file mode 100644 index 00000000..60643784 --- /dev/null +++ b/db/migrate/20170728182033_add_booth_limit_to_conferences.rb @@ -0,0 +1,5 @@ +class AddBoothLimitToConferences < ActiveRecord::Migration + def change + add_column :conferences, :booth_limit, :integer, default: 0 + end +end diff --git a/db/migrate/20170731161207_add_booths_to_email_settings.rb b/db/migrate/20170731161207_add_booths_to_email_settings.rb new file mode 100644 index 00000000..0b3a7a87 --- /dev/null +++ b/db/migrate/20170731161207_add_booths_to_email_settings.rb @@ -0,0 +1,10 @@ +class AddBoothsToEmailSettings < ActiveRecord::Migration + def change + add_column :email_settings, :send_on_booths_acceptance, :boolean, default: false + add_column :email_settings, :booths_acceptance_subject, :string + add_column :email_settings, :booths_acceptance_body, :text + add_column :email_settings, :send_on_booths_rejection, :boolean, default: false + add_column :email_settings, :booths_rejection_subject, :string + add_column :email_settings, :booths_rejection_body, :text + end +end diff --git a/db/migrate/20170807092805_add_registration_ticket_to_tickets.rb b/db/migrate/20170807092805_add_registration_ticket_to_tickets.rb new file mode 100644 index 00000000..0ee2410d --- /dev/null +++ b/db/migrate/20170807092805_add_registration_ticket_to_tickets.rb @@ -0,0 +1,5 @@ +class AddRegistrationTicketToTickets < ActiveRecord::Migration + def change + add_column :tickets, :registration_ticket, :boolean, default: false + end +end diff --git a/db/migrate/20170809120927_add_track_reference_to_schedule.rb b/db/migrate/20170809120927_add_track_reference_to_schedule.rb new file mode 100644 index 00000000..2c2d7f1c --- /dev/null +++ b/db/migrate/20170809120927_add_track_reference_to_schedule.rb @@ -0,0 +1,5 @@ +class AddTrackReferenceToSchedule < ActiveRecord::Migration + def change + add_reference :schedules, :track, index: true, foreign_key: true + end +end diff --git a/db/migrate/20170814174637_add_selected_schedule_to_tracks.rb b/db/migrate/20170814174637_add_selected_schedule_to_tracks.rb new file mode 100644 index 00000000..05b7d417 --- /dev/null +++ b/db/migrate/20170814174637_add_selected_schedule_to_tracks.rb @@ -0,0 +1,6 @@ +class AddSelectedScheduleToTracks < ActiveRecord::Migration + def change + add_column :tracks, :selected_schedule_id, :integer + add_index :tracks, :selected_schedule_id + end +end diff --git a/db/migrate/20170924190528_add_amount_paid_to_ticket_purchases.rb b/db/migrate/20170924190528_add_amount_paid_to_ticket_purchases.rb new file mode 100644 index 00000000..5e732d4c --- /dev/null +++ b/db/migrate/20170924190528_add_amount_paid_to_ticket_purchases.rb @@ -0,0 +1,5 @@ +class AddAmountPaidToTicketPurchases < ActiveRecord::Migration + def change + add_column :ticket_purchases, :amount_paid, :float, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index ab95b4dc..6c6531c1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170302145716) do +ActiveRecord::Schema.define(version: 20170924190528) do create_table "ahoy_events", force: :cascade do |t| t.uuid "visit_id", limit: 16 @@ -31,6 +31,30 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.datetime "updated_at" end + create_table "booth_requests", force: :cascade do |t| + t.integer "booth_id" + t.integer "user_id" + t.string "role" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "booth_requests", ["booth_id"], name: "index_booth_requests_on_booth_id" + add_index "booth_requests", ["user_id"], name: "index_booth_requests_on_user_id" + + create_table "booths", force: :cascade do |t| + t.string "title" + t.text "description" + t.text "reasoning" + t.string "state" + t.string "logo_link" + t.string "website_url" + t.text "submitter_relationship" + t.integer "conference_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "campaigns", force: :cascade do |t| t.integer "conference_id" t.string "name" @@ -49,6 +73,7 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.datetime "created_at" t.datetime "updated_at" t.integer "program_id" + t.string "cfp_type" end create_table "comments", force: :cascade do |t| @@ -89,7 +114,7 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.datetime "created_at" t.datetime "updated_at" t.string "logo_file_name" - t.integer "revision" + t.integer "revision", default: 0, null: false t.boolean "use_vpositions", default: false t.boolean "use_vdays", default: false t.boolean "use_volunteers" @@ -100,8 +125,14 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.string "picture" t.integer "start_hour", default: 9 t.integer "end_hour", default: 20 + t.integer "organization_id" + t.integer "ticket_layout", default: 0 + t.string "custom_domain" + t.integer "booth_limit", default: 0 end + add_index "conferences", ["organization_id"], name: "index_conferences_on_organization_id" + create_table "conferences_questions", id: false, force: :cascade do |t| t.integer "conference_id" t.integer "question_id" @@ -176,6 +207,12 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.string "cfp_dates_updated_subject" t.text "program_schedule_public_body" t.text "cfp_dates_updated_body" + t.boolean "send_on_booths_acceptance", default: false + t.string "booths_acceptance_subject" + t.text "booths_acceptance_body" + t.boolean "send_on_booths_rejection", default: false + t.string "booths_rejection_subject" + t.text "booths_rejection_body" end create_table "event_schedules", force: :cascade do |t| @@ -234,6 +271,7 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.boolean "is_highlight", default: false t.integer "program_id" t.integer "max_attendees" + t.integer "comments_count", default: 0, null: false end create_table "events_registrations", force: :cascade do |t| @@ -266,6 +304,12 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.datetime "updated_at" end + create_table "organizations", force: :cascade do |t| + t.string "name", null: false + t.text "description" + t.string "picture" + end + create_table "payments", force: :cascade do |t| t.string "last4" t.integer "amount" @@ -277,6 +321,15 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.datetime "updated_at", null: false end + create_table "physical_tickets", force: :cascade do |t| + t.integer "ticket_purchase_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "token" + end + + add_index "physical_tickets", ["token"], name: "index_physical_tickets_on_token", unique: true + create_table "programs", force: :cascade do |t| t.integer "conference_id" t.integer "rating", default: 0 @@ -378,9 +431,11 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.integer "program_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "track_id" end add_index "schedules", ["program_id"], name: "index_schedules_on_program_id" + add_index "schedules", ["track_id"], name: "index_schedules_on_track_id" create_table "splashpages", force: :cascade do |t| t.integer "conference_id" @@ -400,6 +455,7 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.datetime "created_at" t.datetime "updated_at" t.boolean "include_cfp", default: false + t.boolean "include_booths" end create_table "sponsors", force: :cascade do |t| @@ -447,26 +503,48 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.integer "quantity", default: 1 t.integer "user_id" t.integer "payment_id" + t.integer "week" + t.float "amount_paid" + end + + create_table "ticket_scannings", force: :cascade do |t| + t.integer "physical_ticket_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "tickets", force: :cascade do |t| t.integer "conference_id" - t.string "title", null: false + t.string "title", null: false t.text "description" - t.integer "price_cents", default: 0, null: false - t.string "price_currency", default: "USD", null: false + t.integer "price_cents", default: 0, null: false + t.string "price_currency", default: "USD", null: false + t.boolean "registration_ticket", default: false end create_table "tracks", force: :cascade do |t| - t.string "guid", null: false - t.string "name", null: false + t.string "guid", null: false + t.string "name", null: false t.text "description" t.string "color" t.datetime "created_at" t.datetime "updated_at" t.integer "program_id" + t.string "short_name", null: false + t.string "state", default: "new", null: false + t.boolean "cfp_active", null: false + t.integer "submitter_id" + t.integer "room_id" + t.date "start_date" + t.date "end_date" + t.text "relevance" + t.integer "selected_schedule_id" end + add_index "tracks", ["room_id"], name: "index_tracks_on_room_id" + add_index "tracks", ["selected_schedule_id"], name: "index_tracks_on_selected_schedule_id" + add_index "tracks", ["submitter_id"], name: "index_tracks_on_submitter_id" + create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false @@ -500,6 +578,7 @@ ActiveRecord::Schema.define(version: 20170302145716) do t.boolean "is_admin", default: false t.string "username" t.boolean "is_disabled", default: false + t.string "token" end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true diff --git a/docker-compose.env.example b/docker-compose.env.example new file mode 100644 index 00000000..2aedbe73 --- /dev/null +++ b/docker-compose.env.example @@ -0,0 +1,39 @@ +## database related variables ## + +# variables prefixed with MYSQL_ are used by both database and web containers +# variables prefixed with DATABASE_ are used exlusively by the web container + +MYSQL_DATABASE=osem +MYSQL_USER=osem +MYSQL_PASSWORD=changemeimmediately +MYSQL_ROOT_PASSWORD=changemeevenmoreimmediately + +# the following settings should not be modified unless the database service +# is renamed in docker-compose.yml or you plan to use an external database +DATABASE_HOST=database +DATABASE_PORT=3306 + + +## OSEM options ## +# you can configure any option described in this document here instead of +# having to create a .env file: +# https://github.com/openSUSE/osem/blob/master/dotenv.example + +OSEM_NAME=Dockerized OSEM +OSEM_HOSTNAME=http://localhost:9292 +OSEM_ERRBIT_HOST=localhost +SECRET_KEY_BASE=changemechangemechangeme + +# these settings work for the MailHog server that is enabled by default in +# docker-compose.yml +# if you do not plan to use MailHog (you most likely don't want to), you need +# to change these settings to use an external working mailserver, otherwise +# your users are going to see the HTTP status 500 page +# you should comment out or remove the mailhog service from docker-compose.yml, +# too +OSEM_EMAIL_ADDRESS=osem@mailhog +OSEM_SMTP_AUTHENTICATION=login +OSEM_SMTP_ADDRESS=mailhog +OSEM_SMTP_PORT=1025 +OSEM_SMTP_USERNAME=mailhog +OSEM_SMTP_PASSWORD=mailhog diff --git a/docker-compose.yml.example b/docker-compose.yml.example new file mode 100644 index 00000000..0959523c --- /dev/null +++ b/docker-compose.yml.example @@ -0,0 +1,29 @@ +version: "2" + +services: + database: + image: mariadb:10.1 + env_file: docker-compose.env + volumes: + - database:/var/lib/mysql + + mailhog: + image: mailhog/mailhog:latest + ports: + - "127.0.0.1:8025:8025" + + web: + build: . + env_file: docker-compose.env + depends_on: + - database + - mailhog + ports: + - "127.0.0.1:9292:9292" + volumes: + - "web:/data" + +# these named volumes are used to persist data +volumes: + database: + web: diff --git a/docker/init.sh b/docker/init.sh new file mode 100644 index 00000000..3a2031f3 --- /dev/null +++ b/docker/init.sh @@ -0,0 +1,51 @@ +#! /bin/bash + +set -e + +# data directory is required for caching the secret key in a file +if [ "$DATA_DIR" == "" ]; then + echo -n "Error: DATA_DIR environment variable not set!" + echo "Are you sure you are running this script in a Docker container?" + exit 1 +fi + +SECRET_KEY_FILE="$DATA_DIR/secret_key" + +if [ ! -f "$SECRET_KEY_FILE" ]; then + echo ">>> Creating a new secret key file..." + install -m 0600 /dev/null "$SECRET_KEY_FILE" + SECRET_KEY=$(bundle exec rake secret) + echo "$key" > "$SECRET_KEY_FILE" + chmod -w "$SECRET_KEY_FILE" +else + SECRET_KEY=$(cat "$SECRET_KEY_FILE") +fi + +export SECRET_KEY +export RAILS_ENV=production + +install -m 0600 /dev/null .my.cnf +cat > .my.cnf <>> Waiting for database to get ready to connect..." +dockerize -wait tcp://$DATABASE_HOST:$DATABASE_PORT -timeout 60s true + +if [ $(echo "show tables;" | mysql --host $DATABASE_HOST --port $DATABASE_PORT $MYSQL_DATABASE | wc -l) -le 1 ]; then + echo ">>> Initializing database..." + bundle exec rake db:schema:load +fi + +echo ">>> Upgrading database..." +bundle exec rake db:migrate + +rm .my.cnf + +echo ">>> Precompiling assets..." +bundle exec rake assets:precompile + +echo ">>> Starting application server..." +exec bundle exec rails server -e production -b 0.0.0.0 -p 9292 diff --git a/dotenv.example b/dotenv.example index 0c852658..77c28e4c 100644 --- a/dotenv.example +++ b/dotenv.example @@ -58,6 +58,8 @@ OSEM_SMTP_USERNAME="" OSEM_SMTP_PASSWORD="" OSEM_SMTP_AUTHENTICATION="" OSEM_SMTP_DOMAIN="" +OSEM_SMTP_ENABLE_STARTTLS_AUTO="" +OSEM_SMTP_OPENSSL_VERIFY_MODE="" # Enable the usage of the devise ichain plugin OSEM_ICHAIN_ENABLED=false diff --git a/lib/tasks/demo_data_for_development.rake b/lib/tasks/demo_data_for_development.rake index a86371b3..5be30c8e 100644 --- a/lib/tasks/demo_data_for_development.rake +++ b/lib/tasks/demo_data_for_development.rake @@ -4,37 +4,37 @@ namespace :data do include FactoryGirl::Syntax::Methods def generate_program conference - program = conference.program - user1 = create(:user) - user2 = create(:user) + program = conference.program + user1 = create(:user) + user2 = create(:user) - conference_rooms = conference.venue.rooms + conference_rooms = conference.venue.rooms - selected_schedule = create(:schedule, program: program) - demo_schedule = create(:schedule, program: program) - program.update_attributes!(selected_schedule: selected_schedule) + selected_schedule = create(:schedule, program: program) + demo_schedule = create(:schedule, program: program) + program.update_attributes!(selected_schedule: selected_schedule) - create(:event, program: program, title: 'Demo Event', abstract: 'This is a demo event instance whose state not defined.') - create(:event, program: program, title: 'Demo Rejected Event', state: 'rejected', abstract: 'This is demo event instance in a rejected state.') - create(:event, program: program, title: 'Demo Unconfirmed Event', state: 'unconfirmed', abstract: 'This is a demo event instance in unconfirmed state.') - create(:event, program: program, title: 'Demo Confirmed Unscheduled Event', state: 'confirmed', abstract: 'This is a demo event instance in a confirmed state.') + create(:event, program: program, title: 'Demo Event', abstract: 'This is a demo event instance whose state not defined.') + create(:event, program: program, title: 'Demo Rejected Event', state: 'rejected', abstract: 'This is demo event instance in a rejected state.') + create(:event, program: program, title: 'Demo Unconfirmed Event', state: 'unconfirmed', abstract: 'This is a demo event instance in unconfirmed state.') + create(:event, program: program, title: 'Demo Confirmed Unscheduled Event', state: 'confirmed', abstract: 'This is a demo event instance in a confirmed state.') - first_scheduled_event = create(:event, program: program, title: 'first_scheduled_event', state: 'confirmed', abstract: 'This is a demo scheduled event instance.') - second_scheduled_event = create(:event, program: program, title: 'second_scheduled_event', state: 'confirmed', abstract: 'This is a demo scheduled event instance.') - multiple_speaker_event = create(:event, program: program, title: 'multiple_speaker_event', state: 'confirmed', abstract: 'This is a demo scheduled event instance having multiple speakers.') + first_scheduled_event = create(:event, program: program, title: 'first_scheduled_event', state: 'confirmed', abstract: 'This is a demo scheduled event instance.') + second_scheduled_event = create(:event, program: program, title: 'second_scheduled_event', state: 'confirmed', abstract: 'This is a demo scheduled event instance.') + multiple_speaker_event = create(:event, program: program, title: 'multiple_speaker_event', state: 'confirmed', abstract: 'This is a demo scheduled event instance having multiple speakers.') - create(:event_user, event: multiple_speaker_event, user: user1, event_role: 'speaker') - create(:event_user, event: multiple_speaker_event, user: user2, event_role: 'speaker') + create(:event_user, event: multiple_speaker_event, user: user1, event_role: 'speaker') + create(:event_user, event: multiple_speaker_event, user: user2, event_role: 'speaker') - create(:event_schedule, event: first_scheduled_event, schedule: selected_schedule, start_time: conference.start_date + conference.start_hour.hours, room: conference_rooms.first) - create(:event_schedule, event: second_scheduled_event, schedule: selected_schedule, start_time: conference.start_date + conference.start_hour.hours + 15.minutes, room: conference_rooms.second) - create(:event_schedule, event: multiple_speaker_event, schedule: selected_schedule, start_time: conference.start_date + conference.start_hour.hours + 30.minutes, room: conference_rooms.third) - create(:event_schedule, event: first_scheduled_event, schedule: demo_schedule, start_time: conference.start_date + conference.start_hour.hours + 15.minutes, room: conference_rooms.third) - create(:event_schedule, event: second_scheduled_event, schedule: demo_schedule, start_time: conference.start_date + conference.start_hour.hours + 30.minutes, room: conference_rooms.third) - create(:event_schedule, event: multiple_speaker_event, schedule: demo_schedule, start_time: conference.start_date + conference.start_hour.hours, room: conference_rooms.first) + create(:event_schedule, event: first_scheduled_event, schedule: selected_schedule, start_time: conference.start_date + conference.start_hour.hours, room: conference_rooms.first) + create(:event_schedule, event: second_scheduled_event, schedule: selected_schedule, start_time: conference.start_date + conference.start_hour.hours + 15.minutes, room: conference_rooms.second) + create(:event_schedule, event: multiple_speaker_event, schedule: selected_schedule, start_time: conference.start_date + conference.start_hour.hours + 30.minutes, room: conference_rooms.third) + create(:event_schedule, event: first_scheduled_event, schedule: demo_schedule, start_time: conference.start_date + conference.start_hour.hours + 15.minutes, room: conference_rooms.third) + create(:event_schedule, event: second_scheduled_event, schedule: demo_schedule, start_time: conference.start_date + conference.start_hour.hours + 30.minutes, room: conference_rooms.third) + create(:event_schedule, event: multiple_speaker_event, schedule: demo_schedule, start_time: conference.start_date + conference.start_hour.hours, room: conference_rooms.first) - create(:registration, user: user1, conference: conference) - create(:registration, user: user2, conference: conference) + create(:registration, user: user1, conference: conference) + create(:registration, user: user2, conference: conference) end # This is a full conference demo instance that will happen in the future. @@ -73,5 +73,5 @@ namespace :data do # Registration for this conference has reached its limit. conference = create(:full_conference, title: 'Zypper Docker Conference', short_title: 'zypper', registration_limit: 2, start_date: 3.days.from_now, end_date: 7.days.from_now, start_hour: 7, end_hour: 19, description: 'This is a full conference demo instance. Its registrations has reached the limit.') generate_program conference - end + end end diff --git a/lib/tasks/roles.rake b/lib/tasks/roles.rake index 8c817db0..c29d0e24 100644 --- a/lib/tasks/roles.rake +++ b/lib/tasks/roles.rake @@ -2,6 +2,10 @@ namespace :roles do desc 'Adds back deleted roles to all conferences' task add: :environment do + Organization.all.each do |org| + Role.where(name: 'organization_admin', resource: org).first_or_create(description: 'For the administrators of an organization and its conferences') + end + Conference.all.each do |c| Role.where(name: 'organizer', resource: c).first_or_create(description: 'For the organizers of the conference (who shall have full access)') Role.where(name: 'cfp', resource: c).first_or_create(description: 'For the members of the CfP team') diff --git a/lib/tasks/version.rake b/lib/tasks/version.rake index 084ff181..a25653c6 100644 --- a/lib/tasks/version.rake +++ b/lib/tasks/version.rake @@ -1,21 +1,33 @@ namespace :data do desc 'Sets conference_id in all pre-existing PaperTrail::Version objects' task set_conference_in_versions: :environment do - - PaperTrail::Version.where(conference_id: nil, item_type: ['Conference', 'Event']).each do |version| + ids_with_failure = [] + PaperTrail::Version.where(conference_id: nil, item_type: %w[Conference Event]).each do |version| # All pre-existing versions are either of Conference or Event if version.item_type == 'Conference' version.update_attributes(conference_id: version.item_id) elsif version.item_type == 'Event' event = (version.item || version.reify || version.next.reify) - if event.try(:program) - version.update_attributes(conference_id: event.program.conference_id) - else - puts "Setting conference_id value failed for PaperTrail::Version object with ID=#{version.id}" - end + + conference_id = if event.try(:program) + event.program.conference_id + # Event had attribute conference_id before it was replaced with program_id + elsif version.changeset[:conference_id] + version.changeset[:conference_id].second + elsif version.changeset[:program_id] + version.changeset[:program_id].second + elsif version.object && (object = YAML.safe_load(version.object)) + object['conference_id'] + else + ids_with_failure << version.id + puts "Setting conference_id value failed for PaperTrail::Version object with ID=#{version.id}" + nil + end + version.update_attributes(conference_id: conference_id) end end puts 'All done!' + puts "IDs with failures: #{ids_with_failure}" if ids_with_failure.any? end end diff --git a/spec/controllers/admin/booths_controller_spec.rb b/spec/controllers/admin/booths_controller_spec.rb new file mode 100644 index 00000000..37004531 --- /dev/null +++ b/spec/controllers/admin/booths_controller_spec.rb @@ -0,0 +1,130 @@ +require 'spec_helper' + +describe Admin::BoothsController do + + let(:admin) { create(:admin) } + let(:conference) { create(:conference) } + let(:booth) { create(:booth, title: 'Title', conference: conference) } + let(:admin) { create(:admin) } + + context 'not logged in user' do + + describe 'GET index' do + it 'does not render admin/booths#index' do + get :index, conference_id: conference.short_title + expect(response).to redirect_to(user_session_path) + end + end + + describe 'GET show' do + it 'does not render admin/booths#show' do + get :show, id: booth.id, conference_id: conference.short_title + expect(response).to redirect_to(user_session_path) + end + end + end + + context 'user is admin' do + before :each do + sign_in admin + end + + describe 'GET index' do + before { get :index, conference_id: conference.short_title } + + it 'assigns attributes for booths' do + expect(assigns(:booths)).to eq([booth]) + end + + it 'renders index template' do + expect(response).to render_template('index') + end + end + + describe 'GET new' do + before { get :new, conference_id: conference.short_title } + + it 'assigns attributes for booths' do + expect(assigns(:booth)).to be_a_new(Booth) + end + + it 'renders new template' do + expect(response).to render_template('new') + end + end + + describe 'POST #create' do + context 'successfully created' do + before { post :create, booth: attributes_for(:booth), conference_id: conference.short_title } + + it 'creates a new booth' do + expected = expect do + post :create, booth: attributes_for(:booth), conference_id: conference.short_title + end + expected.to change { Booth.count }.by(1) + end + + it 'redirects to admin booth index' do + expect(response).to redirect_to(admin_conference_booths_path) + end + + it 'has responsibles' do + expect(booth.responsibles.count).to_not eq(0) + end + + it 'shows success message' do + expect(flash[:notice]).to match('Booth successfully created.') + end + end + + context 'create action fails' do + before { post :create, booth: attributes_for(:booth, title: ''), conference_id: conference.short_title } + + it 'does not create any record' do + expected = expect do + post :create, booth: attributes_for(:booth, title: ''), conference_id: conference.short_title + end + expected.to_not change(Booth, :count) + end + + it 'redirects to new' do + expect(response).to render_template('new') + end + + it 'shows flash message' do + expect(flash[:error]).to eq("Creating booth failed. Title can't be blank.") + end + end + end + + describe 'GET #edit' do + before { get :edit, id: booth.id, conference_id: conference.short_title } + + it 'renders edit template' do + expect(response).to render_template('edit') + end + + it 'assigns booth variable' do + expect(assigns(:booth)).to eq booth + end + end + + describe 'PATCH #update' do + context 'updates suchessfully' do + before { patch :update, id: booth.id, booth: attributes_for(:booth, title: 'different'), conference_id: conference.short_title } + it 'redirects to admin booth index path' do + expect(response).to redirect_to admin_conference_booths_path + end + + it 'shows success message' do + expect(flash[:notice]).to match 'Successfully updated booth.' + end + + it 'updates booth' do + booth.reload + expect(booth.title).to eq('different') + end + end + end + end +end diff --git a/spec/controllers/admin/comments_controller_spec.rb b/spec/controllers/admin/comments_controller_spec.rb index 5429dabd..23b6b88a 100644 --- a/spec/controllers/admin/comments_controller_spec.rb +++ b/spec/controllers/admin/comments_controller_spec.rb @@ -51,7 +51,7 @@ describe Admin::CommentsController, type: :controller do comment get :index expect(response).to redirect_to(root_path) - expect(flash[:alert]).to match('You are not authorized to access this area!') + expect(flash[:alert]).to match('You are not authorized to access this page.') end end end diff --git a/spec/controllers/admin/conferences_controller_spec.rb b/spec/controllers/admin/conferences_controller_spec.rb index 3964996f..009e40ba 100644 --- a/spec/controllers/admin/conferences_controller_spec.rb +++ b/spec/controllers/admin/conferences_controller_spec.rb @@ -3,19 +3,18 @@ require 'spec_helper' describe Admin::ConferencesController do # It is necessary to use bang version of let to build roles before user - let(:conference) { create(:conference, end_date: Date.new(2014, 05, 26) + 15) } + let!(:organization) { create(:organization, name: 'organization') } + let!(:conference) { create(:conference, organization: organization, end_date: Date.new(2014, 05, 26) + 15) } let!(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) } - + let!(:organization_admin_role) { Role.find_by(name: 'organization_admin', resource: organization) } + let(:organization_admin) { create(:user, role_ids: organization_admin_role.id) } let(:organizer) { create(:user, role_ids: organizer_role.id) } let(:organizer2) { create(:user, email: 'organizer2@email.osem', role_ids: organizer_role.id) } let(:participant) { create(:user) } - shared_examples 'access as organizer' do - + shared_examples 'access as organizer or organization_admin' do describe 'PATCH #update' do - context 'valid attributes' do - it 'locates the requested conference' do patch :update, id: conference.short_title, conference: attributes_for(:conference, title: 'Example Con') expect(assigns(:conference)).to eq(conference) @@ -25,7 +24,6 @@ describe Admin::ConferencesController do patch :update, id: conference.short_title, conference: attributes_for(:conference, title: 'Example Con', short_title: 'ExCon') - conference.reload expect(conference.title).to eq('Example Con') expect(conference.short_title).to eq('ExCon') @@ -75,72 +73,6 @@ describe Admin::ConferencesController do end end - describe 'POST #create' do - context 'with valid attributes' do - it 'saves the conference to the database' do - expected = expect do - post :create, conference: - attributes_for(:conference, short_title: 'dps15') - end - expected.to change { Conference.count }.by 1 - end - - it 'redirects to conference#show' do - post :create, conference: - attributes_for(:conference, short_title: 'dps15') - - expect(response).to redirect_to admin_conference_path( - assigns[:conference].short_title) - end - - it 'creates roles for the conference' do - cfp_role = Role.find_by(name: 'cfp', resource: conference) - info_desk_role = Role.find_by(name: 'info_desk', resource: conference) - volunteers_coordinator_role = Role.find_by(name: 'volunteers_coordinator', resource: conference) - - post :create, conference: - attributes_for(:conference, short_title: 'dps15') - - expect(conference.roles.count).to eq 4 - - expect(conference.roles).to eq [organizer_role, cfp_role, info_desk_role, volunteers_coordinator_role] - end - end - - context 'with invalid attributes' do - it 'does not save the conference to the database' do - expected = expect do - post :create, conference: - attributes_for(:conference, short_title: nil) - end - expected.to_not change { Conference.count } - end - - it 're-renders the new template' do - post :create, conference: - attributes_for(:conference, short_title: nil) - expect(response).to be_success - end - end - - context 'with duplicate conference short title' do - it 'does not save the conference to the database' do - conference - expected = expect do - post :create, conference: - attributes_for(:conference, short_title: conference.short_title) - end - expected.to_not change { Conference.count } - end - - it 're-renders the new template' do - conference - post :create, conference: attributes_for(:conference, short_title: conference.short_title) - expect(response).to be_success - end - end - end - describe 'GET #edit' do it 'assigns the requested conference to conference' do get :edit, id: conference.short_title @@ -177,10 +109,10 @@ describe Admin::ConferencesController do it 'assigns cfp_max an array with maximum weeks' do conference date = Date.new(2014, 05, 26) - conference.program.cfp = create(:cfp, - program: conference.program, - start_date: date, - end_date: date + 14) + create(:cfp, + program: conference.program, + start_date: date, + end_date: date + 14) get :index expect(assigns(:cfp_weeks)).to match_array([1, 2, 3]) end @@ -203,6 +135,74 @@ describe Admin::ConferencesController do end end end + end + + shared_examples 'access as organization_admin' do + describe 'POST #create' do + context 'with valid attributes' do + it 'saves the conference to the database' do + expected = expect do + post :create, conference: + attributes_for(:conference, short_title: 'dps15', organization_id: organization.id) + end + expected.to change { Conference.count }.by 1 + end + + it 'redirects to conference#show' do + post :create, conference: + attributes_for(:conference, short_title: 'dps15', organization_id: organization.id) + + expect(response).to redirect_to admin_conference_path( + assigns[:conference].short_title) + end + + it 'creates roles for the conference' do + cfp_role = Role.find_by(name: 'cfp', resource: conference) + info_desk_role = Role.find_by(name: 'info_desk', resource: conference) + volunteers_coordinator_role = Role.find_by(name: 'volunteers_coordinator', resource: conference) + + post :create, conference: + attributes_for(:conference, short_title: 'dps15') + + expect(conference.roles.count).to eq 4 + + expect(conference.roles).to eq [organizer_role, cfp_role, info_desk_role, volunteers_coordinator_role] + end + end + + context 'with invalid attributes' do + it 'does not save the conference to the database' do + expected = expect do + post :create, conference: + attributes_for(:conference, short_title: nil, organization_id: organization.id) + end + expected.to_not change { Conference.count } + end + + it 're-renders the new template' do + post :create, conference: + attributes_for(:conference, short_title: nil, organization_id: organization.id) + expect(response).to be_success + end + end + + context 'with duplicate conference short title' do + it 'does not save the conference to the database' do + conference + expected = expect do + post :create, conference: + attributes_for(:conference, short_title: conference.short_title, organization_id: organization.id) + end + expected.to_not change { Conference.count } + end + + it 're-renders the new template' do + conference + post :create, conference: attributes_for(:conference, short_title: conference.short_title, organization_id: organization.id) + expect(response).to be_success + end + end + end describe 'GET #new' do it 'assigns a new conference to conference' do @@ -217,14 +217,45 @@ describe Admin::ConferencesController do end end - describe 'organizer access' do + describe 'organization admin access' do + before do + sign_in(organization_admin) + end + it_behaves_like 'access as organizer or organization_admin' + it_behaves_like 'access as organization_admin' + end + + shared_examples 'access as organizer, participant or guest' do |path, message| + describe 'GET #new' do + it 'requires organizer privileges' do + get :new + expect(response).to redirect_to(send(path)) + if message + expect(flash[:alert]).to match(/#{message}/) + end + end + end + + describe 'POST #create' do + it 'requires organizer privileges' do + post :create, conference: attributes_for(:conference, + short_title: 'ExCon', organization_id: organization.id) + expect(response).to redirect_to(send(path)) + if message + expect(flash[:alert]).to match(/#{message}/) + end + end + end + end + + describe 'organizer access' do before do sign_in(organizer) end - it_behaves_like 'access as organizer' - + it_behaves_like 'access as organizer or organization_admin' + it_behaves_like 'access as organizer, participant or guest', :root_path, 'You are not authorized to access this page.' end shared_examples 'access as participant or guest' do |path, message| @@ -248,27 +279,6 @@ describe Admin::ConferencesController do end end - describe 'GET #new' do - it 'requires organizer privileges' do - get :new - expect(response).to redirect_to(send(path)) - if message - expect(flash[:alert]).to match(/#{message}/) - end - end - end - - describe 'POST #create' do - it 'requires organizer privileges' do - post :create, conference: attributes_for(:conference, - short_title: 'ExCon') - expect(response).to redirect_to(send(path)) - if message - expect(flash[:alert]).to match(/#{message}/) - end - end - end - describe 'PATCH #update' do it 'requires organizer privileges' do patch :update, id: conference.short_title, @@ -287,13 +297,13 @@ describe Admin::ConferencesController do sign_in(participant) end - it_behaves_like 'access as participant or guest', :root_path, 'You are not authorized to access this area!' - + it_behaves_like 'access as participant or guest', :root_path, 'You are not authorized to access this page.' + it_behaves_like 'access as organizer, participant or guest', :root_path, 'You are not authorized to access this page.' end describe 'guest access' do it_behaves_like 'access as participant or guest', :new_user_session_path - + it_behaves_like 'access as organizer, participant or guest', :new_user_session_path end end diff --git a/spec/controllers/admin/events_controller_spec.rb b/spec/controllers/admin/events_controller_spec.rb index a413ba7a..682defce 100644 --- a/spec/controllers/admin/events_controller_spec.rb +++ b/spec/controllers/admin/events_controller_spec.rb @@ -4,9 +4,13 @@ describe Admin::EventsController do let(:conference) { create(:conference) } let(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) } let(:organizer) { create(:user, role_ids: organizer_role.id) } - let!(:event_without_commercial) { create(:event, program: conference.program) } - let!(:event_with_commercial) { create(:event, program: conference.program) } - let!(:event_commercial) { create(:event_commercial, commercialable: event_with_commercial, url: 'https://www.youtube.com/watch?v=M9bq_alk-sw') } + # The where_object() and where_object_changes() methods of paper_trail gem are broken when having: + # an Event with ID 1, an Event with ID 2, and a commercial with ID 1, for event with ID 2 + # (the numbers could be different as long as there is this matching of IDs). + # We implemented or own where method to solve this and those ids are for testing this case. + let!(:event_without_commercial) { create(:event, id: 1, program: conference.program) } + let!(:event_with_commercial) { create(:event, id: 2, program: conference.program) } + let!(:event_commercial) { create(:event_commercial, id: 1, commercialable: event_with_commercial, url: 'https://www.youtube.com/watch?v=M9bq_alk-sw') } with_versioning do describe 'GET #show' do @@ -17,8 +21,6 @@ describe Admin::EventsController do it 'assigns versions' do versions = event_without_commercial.versions - expect(event_without_commercial.id).to eq event_commercial.id - expect(event_commercial.id).not_to eq event_commercial.commercialable_id expect(assigns(:versions)).to eq versions end end diff --git a/spec/controllers/admin/organizations_controller_spec.rb b/spec/controllers/admin/organizations_controller_spec.rb new file mode 100644 index 00000000..801cce83 --- /dev/null +++ b/spec/controllers/admin/organizations_controller_spec.rb @@ -0,0 +1,198 @@ +require 'spec_helper' + +describe Admin::OrganizationsController do + let!(:admin) { create(:admin) } + let!(:organization) { create(:organization) } + let!(:user) { create(:user) } + + context 'logged in as user with no role' do + before :each do + sign_in user + end + + describe 'GET #new' do + before :each do + get :new + end + + it 'redirects to root' do + expect(flash[:alert]).to eq('You are not authorized to access this page.') + expect(response).to redirect_to(root_path) + end + end + + describe 'GET #index' do + before :each do + get :index + end + + it 'redirects to root' do + expect(flash[:alert]).to eq('You are not authorized to access this page.') + expect(response).to redirect_to(root_path) + end + end + + describe 'POST #create' do + it 'does not create new organization' do + expected = expect do + post :create, organization: attributes_for(:organization) + end + expected.to_not change(Organization, :count) + end + + it 'redirects to root' do + post :create, organization: attributes_for(:organization) + + expect(flash[:alert]).to eq('You are not authorized to access this page.') + expect(response).to redirect_to(root_path) + end + end + + describe 'PATCH #update' do + it 'does not update and redirects to root' do + old_name = organization.name + patch :update, id: organization.id, organization: attributes_for(:organization, name: 'new name') + + organization.reload + expect(organization.name).to eq(old_name) + expect(flash[:alert]).to eq('You are not authorized to access this page.') + expect(response).to redirect_to(root_path) + end + end + + describe 'DELETE #destroy' do + context 'for a valid organization' do + it 'does not destroy a resource' do + expected = expect do + delete :destroy, id: organization.id + end + expected.to_not change(Organization, :count) + end + + it 'redirects to root' do + delete :destroy, id: organization.id + + expect(flash[:alert]).to eq('You are not authorized to access this page.') + expect(response).to redirect_to(root_path) + end + end + end + end + + context 'logged in as admin' do + before :each do + sign_in admin + end + + describe 'GET #new' do + before do + get :new + end + it { expect(response).to render_template('new') } + end + + describe 'GET #index' do + before do + get :index + end + it { expect(response).to render_template('index') } + end + + describe 'POST #create' do + context 'with valid attributes' do + it 'creates new organization' do + expected = expect do + post :create, organization: attributes_for(:organization) + end + expected.to change { Organization.count }.by(1) + end + + it 'redirects to index' do + post :create, organization: attributes_for(:organization) + + expect(flash[:notice]).to eq('Organization successfully created') + expect(response).to redirect_to(admin_organizations_path) + end + end + + context 'with invalid attributes' do + it 'does not create new organization' do + expected = expect do + post :create, organization: attributes_for(:organization, name: '') + end + expected.to_not change(Organization, :count) + end + + it 'redirects to new' do + post :create, organization: attributes_for(:organization, name: '') + + expect(flash[:error]).to eq("Name can't be blank") + expect(response).to redirect_to(new_admin_organization_path) + end + end + end + + describe 'PATCH #update' do + it 'saves and redirects to index when the attributes are valid' do + patch :update, id: organization.id, organization: attributes_for(:organization, name: 'changed name') + + organization.reload + expect(organization.name).to eq('changed name') + expect(flash[:notice]).to eq('Organization successfully updated') + expect(response).to redirect_to(admin_organizations_path) + end + + it 'redirects to edit when attributes are invalid' do + patch :update, id: organization.id, organization: attributes_for(:organization, name: '') + + expect(flash[:error]).to eq("Name can't be blank") + expect(response).to redirect_to(edit_admin_organization_path(organization)) + end + end + + describe 'DELETE #destroy' do + context 'for a valid organization' do + it 'should successfully destroy a resource' do + expected = expect do + delete :destroy, id: organization.id + end + expected.to change { Organization.count }.by(-1) + end + + it 'redirects to index' do + delete :destroy, id: organization.id + + expect(flash[:notice]).to eq('Organization successfully destroyed') + expect(response).to redirect_to(admin_organizations_path) + end + end + end + + describe 'POST #assign_org_admins' do + let(:org_admin_role) { Role.find_by(name: 'organization_admin', resource: organization) } + + before do + post :assign_org_admins, id: organization.id, + user: { email: user.email } + end + + it 'assigns organization_admin role' do + expect(user.roles).to eq [org_admin_role] + end + end + + describe 'DELETE #unassign_org_admins' do + let(:org_admin_role) { Role.find_by(name: 'organization_admin', resource: organization) } + let!(:org_admin_user) { create(:user, role_ids: [org_admin_role.id]) } + + before do + delete :unassign_org_admins, id: organization.id, + user: { email: org_admin_user.email } + end + + it 'unassigns organization_admin role' do + expect(org_admin_user.reload.roles).to eq [] + end + end + end +end diff --git a/spec/controllers/admin/registration_periods_controller_spec.rb b/spec/controllers/admin/registration_periods_controller_spec.rb index 6177fde7..fd98ce77 100644 --- a/spec/controllers/admin/registration_periods_controller_spec.rb +++ b/spec/controllers/admin/registration_periods_controller_spec.rb @@ -5,7 +5,7 @@ describe Admin::RegistrationPeriodsController do # It is necessary to use bang version of let to build roles before user let(:conference) { create(:conference) } let!(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) } - + let!(:registration_ticket) { create(:registration_ticket, conference: conference) } let(:organizer) { create(:user, role_ids: organizer_role.id) } let(:organizer2) { create(:user, email: 'organizer2@email.osem', role_ids: organizer_role.id) } let(:participant) { create(:user) } diff --git a/spec/controllers/admin/roles_controller_spec.rb b/spec/controllers/admin/roles_controller_spec.rb index 964346ef..dcc81313 100644 --- a/spec/controllers/admin/roles_controller_spec.rb +++ b/spec/controllers/admin/roles_controller_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe Admin::RolesController do - let(:conference) { create(:conference) } let(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) } let(:cfp_role) { Role.find_by(name: 'cfp', resource: conference) } @@ -55,7 +54,7 @@ describe Admin::RolesController do end describe 'POST #toggle' do - before:each do + before :each do sign_in admin post :toggle_user, conference_id: conference.short_title, user: { email: 'user1@osem.io' }, diff --git a/spec/controllers/admin/ticket_scannings_controller_spec.rb b/spec/controllers/admin/ticket_scannings_controller_spec.rb new file mode 100644 index 00000000..2eb2e4ed --- /dev/null +++ b/spec/controllers/admin/ticket_scannings_controller_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +describe Admin::TicketScanningsController do + let(:admin) { create(:admin) } + let(:conference) { create(:conference) } + let(:user) { create(:user) } + let!(:registration) { create(:registration, conference: conference, user: user) } + let(:registration_ticket) { create(:registration_ticket, conference: conference) } + let(:paid_ticket_purchase) { create(:ticket_purchase, conference: conference, user: user, ticket: registration_ticket, quantity: 1) } + let(:physical_ticket) { create(:physical_ticket, ticket_purchase: paid_ticket_purchase) } + + context 'logged in as user with no role' do + before :each do + sign_in user + end + describe 'POST #create' do + it 'does not create new ticket scanning' do + expected = expect do + post :create, physical_ticket_id: physical_ticket.token + end + expected.to_not change(TicketScanning, :count) + end + + it 'redirects to root' do + post :create, physical_ticket_id: physical_ticket.token + expect(flash[:alert]).to eq('You are not authorized to access this page.') + expect(response).to redirect_to(root_path) + end + end + end + + context 'logged in as admin' do + before :each do + sign_in admin + end + describe 'POST #create' do + context 'with valid physical_ticket' do + it 'creates new ticket scanning' do + expected = expect do + post :create, physical_ticket_id: physical_ticket.token + end + expected.to change { TicketScanning.count }.by(1) + end + + it 'redirects to index' do + post :create, physical_ticket_id: physical_ticket.token + expect(flash[:notice]).to eq("Ticket with token #{physical_ticket.token} successfully scanned.") + expect(response).to redirect_to(conferences_path) + end + end + + context 'with Invalid physical_ticket' do + it 'raises exception' do + expected = expect do + post :create, physical_ticket_id: 'XXXX' + end + expected.to raise_exception(ActiveRecord::RecordNotFound) + end + end + end + end +end diff --git a/spec/controllers/admin/tracks_controller_spec.rb b/spec/controllers/admin/tracks_controller_spec.rb new file mode 100644 index 00000000..03511ce1 --- /dev/null +++ b/spec/controllers/admin/tracks_controller_spec.rb @@ -0,0 +1,548 @@ +require 'spec_helper' + +describe Admin::TracksController do + let(:admin) { create(:admin) } + + let(:conference) { create(:conference, start_date: Date.current - 1.day) } + let(:venue) { create(:venue, conference: conference) } + let(:room) { create(:room, venue: venue) } + let!(:track) { create(:track, program: conference.program, color: '#800080') } + let!(:self_organized_track) { create(:track, :self_organized, program: conference.program, name: 'My awesome track', start_date: Date.current, end_date: Date.current, room: room) } + + before :each do + sign_in(admin) + end + + describe 'GET #index' do + before :each do + get :index, conference_id: conference.short_title + end + + it 'assigns @tracks with the correct values' do + expect(assigns(:tracks).length).to eq 2 + expect(assigns(:tracks).include?(track)).to eq true + expect(assigns(:tracks).include?(self_organized_track)).to eq true + end + + it 'renders the index template' do + expect(response).to render_template :index + end + end + + describe 'GET #show' do + before :each do + get :show, conference_id: conference.short_title, id: track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq track + end + + it 'renders the show template' do + expect(response).to render_template :show + end + end + + describe 'GET #new' do + before :each do + get :new, conference_id: conference.short_title + end + + it 'assigns a new track with the correct conference' do + expect(assigns(:track)).to be_a Track + expect(assigns(:track).new_record?).to eq true + expect(assigns(:track).program_id).to eq conference.program.id + end + + it 'renders the new template' do + expect(response).to render_template :new + end + end + + describe 'POST #create' do + context 'saves successfuly' do + before :each do + post :create, track: attributes_for(:track), conference_id: conference.short_title + end + + it 'assigns a new track with the correct conference' do + expect(assigns(:track)).to be_a Track + expect(assigns(:track).new_record?).to eq false + expect(assigns(:track).program_id).to eq conference.program.id + end + + it 'redirects to admin tracks index path' do + expect(response).to redirect_to admin_conference_program_tracks_path(conference_id: conference.short_title) + end + + it 'shows success message in flash notice' do + expect(flash[:notice]).to match('Track successfully created.') + end + + it 'creates new track' do + expect(Track.find(assigns(:track).id)).to be_a Track + end + + it 'the new track has the correct attributes' do + expect(assigns(:track).state).to eq 'confirmed' + expect(assigns(:track).cfp_active).to eq true + end + end + + context 'save fails' do + before :each do + allow_any_instance_of(Track).to receive(:save).and_return(false) + post :create, track: attributes_for(:track, short_name: 'my_track'), conference_id: conference.short_title + end + + it 'assigns a new track with the correct conference' do + expect(assigns(:track)).to be_a Track + expect(assigns(:track).new_record?).to eq true + expect(assigns(:track).program_id).to eq conference.program.id + end + + it 'renders the new template' do + expect(response).to render_template :new + end + + it 'shows error in flash message' do + expect(flash[:error]).to match("Creating Track failed: #{assigns(:track).errors.full_messages.join('. ')}.") + end + + it 'does not create a new track' do + expect(conference.program.tracks.find_by(short_name: 'my_track')).to eq nil + end + end + end + + describe 'GET #edit' do + before :each do + get :edit, conference_id: conference.short_title, id: track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq track + end + + it 'renders the show template' do + expect(response).to render_template :edit + end + end + + describe 'PATCH #update' do + context 'updates successfully' do + before :each do + patch :update, track: attributes_for(:track, color: '#FF0000'), + conference_id: conference.short_title, + id: track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq track + end + + it 'redirects to admin tracks index path' do + expect(response).to redirect_to admin_conference_program_tracks_path(conference_id: conference.short_title) + end + + it 'shows success message in flash notice' do + expect(flash[:notice]).to match('Track successfully updated.') + end + + it 'updates the track' do + track.reload + expect(track.color).to eq '#FF0000' + end + end + + context 'update fails' do + before :each do + allow_any_instance_of(Track).to receive(:save).and_return(false) + patch :update, track: attributes_for(:track, color: '#FF0000'), + conference_id: conference.short_title, + id: track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq track + end + + it 'renders edit template' do + expect(response).to render_template :edit + end + + it 'shows error in flash message' do + expect(flash[:error]).to match("Track update failed: #{assigns(:track).errors.full_messages.join('. ')}.") + end + + it 'does not update the track' do + track.reload + expect(track.color).to eq '#800080' + end + end + end + + describe 'DELETE #destroy' do + context 'deletes successfully' do + before :each do + delete :destroy, conference_id: conference.short_title, id: track.short_name + end + + it 'redirects to admin tracks index path' do + expect(response).to redirect_to admin_conference_program_tracks_path(conference_id: conference.short_title) + end + + it 'shows success message in flash notice' do + expect(flash[:notice]).to match('Track successfully deleted.') + end + + it 'deletes the track' do + expect(Track.find_by(id: track)).to eq nil + end + end + + context 'delete fails' do + before :each do + allow_any_instance_of(Track).to receive(:destroy).and_return(false) + delete :destroy, conference_id: conference.short_title, id: track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq track + end + + it 'redirects to admin tracks index path' do + expect(response).to redirect_to admin_conference_program_tracks_path(conference_id: conference.short_title) + end + + it 'shows error in flash message' do + expect(flash[:error]).to match("Track couldn't be deleted. #{track.errors.full_messages.join('. ')}.") + end + + it 'does not delete the track' do + expect(Track.find(track.id)).to eq track + end + end + end + + describe 'PATCH #toggle_cfp_inclusion' do + context 'cfp_active is false' do + before :each do + self_organized_track.cfp_active = false + self_organized_track.save! + end + + context 'toggles successfully' do + before :each do + patch :toggle_cfp_inclusion, conference_id: conference.short_title, id: self_organized_track.short_name, format: :js + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows success message in flash notice' do + expect(flash[:notice]).to match('Successfully changed cfp inclusion of My awesome track to true') + end + + it 'becomes true' do + expect(self_organized_track.cfp_active).to eq true + end + end + + context 'save fails' do + before :each do + allow_any_instance_of(Track).to receive(:save).and_return(false) + patch :toggle_cfp_inclusion, conference_id: conference.short_title, id: self_organized_track.short_name, format: :js + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows error message in flash notice' do + expect(flash[:error]).to match('Failed to toggle cfp inclusion of My awesome track to true') + end + + it 'stays false' do + expect(self_organized_track.cfp_active).to eq false + end + end + end + + context 'cfp_active is true' do + before :each do + self_organized_track.cfp_active = true + self_organized_track.save! + end + + context 'toggles successfully' do + before :each do + patch :toggle_cfp_inclusion, conference_id: conference.short_title, id: self_organized_track.short_name, format: :js + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows success message in flash notice' do + expect(flash[:notice]).to match('Successfully changed cfp inclusion of My awesome track to false') + end + + it 'becomes false' do + expect(self_organized_track.cfp_active).to eq false + end + end + + context 'save fails' do + before :each do + allow_any_instance_of(Track).to receive(:save).and_return(false) + patch :toggle_cfp_inclusion, conference_id: conference.short_title, id: self_organized_track.short_name, format: :js + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows error message in flash notice' do + expect(flash[:error]).to match('Failed to toggle cfp inclusion of My awesome track to false') + end + + it 'stays true' do + expect(self_organized_track.cfp_active).to eq true + end + end + end + end + + describe 'PATCH #restart' do + before :each do + self_organized_track.state = 'canceled' + self_organized_track.save! + patch :restart, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Review for My awesome track started!' + end + + it 'changes the track\'s state to new' do + expect(self_organized_track.state).to eq 'new' + end + end + + describe 'PATCH #to_accept' do + before :each do + patch :to_accept, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track marked as a possible acceptance!' + end + + it 'changes the track\'s state to to_accept' do + expect(self_organized_track.state).to eq 'to_accept' + end + end + + describe 'PATCH #accept' do + shared_examples 'fails to accept' do + before :each do + patch :accept, conference_id: conference.short_title, id: self_organized_track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'redirects to Tracks#edit' do + expect(response).to redirect_to edit_admin_conference_program_track_path(conference.short_title, self_organized_track) + end + + it 'shows message in flash alert' do + expect(flash[:alert]).to eq 'Please make sure that the track has a room and start/end dates before accepting it' + end + end + + context 'has start_date, end_date and room' do + before :each do + patch :accept, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track accepted!' + end + + it 'changes the track\'s state to accepted' do + expect(self_organized_track.state).to eq 'accepted' + end + end + + context 'has start_date and end_date' do + before :each do + self_organized_track.room = nil + self_organized_track.save! + end + + it_behaves_like 'fails to accept' + end + + context 'has start_date and room' do + before :each do + self_organized_track.end_date = nil + self_organized_track.save! + end + + it_behaves_like 'fails to accept' + end + + context 'has start_date' do + before :each do + self_organized_track.end_date = nil + self_organized_track.room = nil + self_organized_track.save! + end + + it_behaves_like 'fails to accept' + end + + context 'has end_date and room' do + before :each do + self_organized_track.start_date = nil + self_organized_track.save! + end + + it_behaves_like 'fails to accept' + end + + context 'has end_date' do + before :each do + self_organized_track.start_date = nil + self_organized_track.room = nil + self_organized_track.save! + end + + it_behaves_like 'fails to accept' + end + + context 'has room' do + before :each do + self_organized_track.start_date = nil + self_organized_track.end_date = nil + self_organized_track.save! + end + + it_behaves_like 'fails to accept' + end + + context 'has none of start_date, end_date, room' do + before :each do + self_organized_track.start_date = nil + self_organized_track.end_date = nil + self_organized_track.room = nil + self_organized_track.save! + end + + it_behaves_like 'fails to accept' + end + end + + describe 'PATCH #confirm' do + before :each do + self_organized_track.state = 'accepted' + self_organized_track.save! + patch :confirm, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track confirmed!' + end + + it 'changes the track\'s state to confirmed' do + expect(self_organized_track.state).to eq 'confirmed' + end + end + + describe 'PATCH #to_reject' do + before :each do + patch :to_reject, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track marked as a possible rejection!' + end + + it 'changes the track\'s state to to_reject' do + expect(self_organized_track.state).to eq 'to_reject' + end + end + + describe 'PATCH #reject' do + before :each do + patch :reject, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track rejected!' + end + + it 'changes the track\'s state to rejected' do + expect(self_organized_track.state).to eq 'rejected' + end + end + + describe 'PATCH #cancel' do + before :each do + self_organized_track.state = 'confirmed' + self_organized_track.save! + patch :cancel, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track canceled!' + end + + it 'changes the track\'s state to canceled' do + expect(self_organized_track.state).to eq 'canceled' + end + end +end diff --git a/spec/controllers/admin/versions_controller_spec.rb b/spec/controllers/admin/versions_controller_spec.rb index 4694837d..f04bc33d 100644 --- a/spec/controllers/admin/versions_controller_spec.rb +++ b/spec/controllers/admin/versions_controller_spec.rb @@ -4,6 +4,9 @@ describe Admin::VersionsController do let!(:conference) { create(:conference, short_title: 'exampletitle', description: 'Example Description') } let(:admin) { create(:admin) } + let(:role_organizer) { conference.roles.find_by(name: 'organizer') } + let(:role_cfp) { conference.roles.find_by(name: 'cfp') } + let(:role_info_desk) { conference.roles.find_by(name: 'info_desk') } with_versioning do describe 'GET #revert' do @@ -97,5 +100,57 @@ describe Admin::VersionsController do expect(flash[:error]).to match('Revert failed. Attribute missing or invalid') end end + + describe 'GET #index' do + it 'raises error if user is not of any role' do + user = create(:user) + sign_in user + get :index, conference_id: conference.short_title + expect(flash[:alert]).to match('You are not authorized to access this page.') + end + + context 'with conference' do + before :each do + @user = create(:user) + + conference.update_attributes(short_title: 'testtitle', description: 'Some random text') + @version_organizer = PaperTrail::Version.last + create(:cfp, program: conference.program) + @version_cfp = PaperTrail::Version.last + registration = create(:registration, conference: conference) + registration.update_attributes(attended: true) + @version_info_desk = PaperTrail::Version.last + end + + it 'when user has role cfp' do + @user.roles = [role_cfp] + sign_in @user + get :index, conference_id: conference.short_title + + expect(assigns(:versions).include?(@version_cfp)).to eq true + expect(assigns(:versions).include?(@version_organizer)).to eq false + end + + it 'when user has role info_desk' do + @user.roles = [role_info_desk] + sign_in @user + get :index, conference_id: conference.short_title + + expect(assigns(:versions).include?(@version_info_desk)).to eq true + expect(assigns(:versions).include?(@version_organizer)).to eq false + expect(assigns(:versions).include?(@version_cfp)).to eq false + end + + it 'when user has role organizer' do + @user.roles = [role_organizer] + sign_in @user + get :index, conference_id: conference.short_title + + expect(assigns(:versions).include?(@version_organizer)).to eq true + expect(assigns(:versions).include?(@version_cfp)).to eq true + expect(assigns(:versions).include?(@version_info_desk)).to eq true + end + end + end end end diff --git a/spec/controllers/booths_controller_spec.rb b/spec/controllers/booths_controller_spec.rb new file mode 100644 index 00000000..56f3b9d9 --- /dev/null +++ b/spec/controllers/booths_controller_spec.rb @@ -0,0 +1,111 @@ +require 'spec_helper' + +describe BoothsController do + + let(:user) { create(:user) } + let(:conference) { create(:conference) } + let(:booth) { create(:booth, title: 'Title', conference: conference) } + + context 'user is signed in with submitter role' do + before :each do + create(:cfp, program: conference.program, cfp_type: 'booths') + sign_in booth.submitter + end + + describe 'GET index' do + before { get :index, conference_id: conference.short_title } + + it 'assigns attributes for booths' do + expect(assigns(:booths)).to eq([booth]) + end + + it 'renders index template' do + expect(response).to render_template('index') + end + end + + describe 'GET #new' do + before { get :new, conference_id: conference.short_title } + + it 'assigns attributes for booths' do + expect(assigns(:booth)).to be_a_new(Booth) + end + + it 'renders new template' do + expect(response).to render_template('new') + end + end + + describe 'POST #create' do + context 'successfully created' do + before { post :create, booth: attributes_for(:booth), conference_id: conference.short_title } + + it 'creates a new booth' do + expect(Booth.count).to_not eq(0) + end + + it 'redirects to booth index' do + expect(response).to redirect_to(conference_booths_path) + end + + it 'has responsibles' do + expect(booth.responsibles.count).to_not eq(0) + end + + it 'shows success message' do + expect(flash[:notice]).to match('Booth successfully created.') + end + end + + context 'create action fails' do + before { post :create, booth: attributes_for(:booth, title: ''), conference_id: conference.short_title } + + it 'does not create any record' do + expected = expect do + post :create, booth: attributes_for(:booth, title: ''), conference_id: conference.short_title + end + expected.to_not change(Booth, :count) + end + + it 'redirects to new' do + expect(response).to render_template('new') + end + + it 'shows flash message' do + expect(flash[:error]).to eq("Creating booth failed. Title can't be blank.") + end + end + end + + describe 'GET #edit' do + before { get :edit, id: booth.id, conference_id: conference.short_title } + + it 'renders edit template' do + expect(response).to render_template('edit') + end + + it 'assigns booth variable' do + expect(assigns(:booth)).to eq booth + end + end + + describe 'PATCH #update' do + context 'updates suchessfully' do + before { patch :update, id: booth.id, booth: attributes_for(:booth, title: 'different'), conference_id: conference.short_title } + + it 'redirects to booth index path' do + expect(response).to redirect_to conference_booths_path + end + + it 'shows success message' do + expect(flash[:notice]).to match 'Booth successfully updated!' + end + + it 'updates booth' do + booth.reload + expect(booth.title).to eq('different') + end + end + end + end +end diff --git a/spec/controllers/conference_registration_controller_spec.rb b/spec/controllers/conference_registration_controller_spec.rb index 9afaaed8..a6f1503c 100644 --- a/spec/controllers/conference_registration_controller_spec.rb +++ b/spec/controllers/conference_registration_controller_spec.rb @@ -252,7 +252,7 @@ describe ConferenceRegistrationsController, type: :controller do end it 'does not assign price of purchased tickets to total_price and purchased tickets to tickets without payment' do - expect(assigns(:total_price)).to eq Money.new(0, 'USD') + expect(assigns(:total_price)).to eq 0 end end @@ -262,7 +262,7 @@ describe ConferenceRegistrationsController, type: :controller do end it 'assigns 0 dollars to total_price and empty array to tickets variables' do - expect(assigns(:total_price)).to eq Money.new(0, 'USD') + expect(assigns(:total_price)).to eq 0 expect(assigns(:tickets)).to match_array [] end end diff --git a/spec/controllers/conferences_controller_spec.rb b/spec/controllers/conferences_controller_spec.rb index 6479eb34..cdbebf9f 100644 --- a/spec/controllers/conferences_controller_spec.rb +++ b/spec/controllers/conferences_controller_spec.rb @@ -24,6 +24,20 @@ describe ConferencesController do expect(response).to render_template :show end end + + context 'accessing conference via custom domain' do + before do + conference.update_attribute(:custom_domain, 'lvh.me') + @request.host = 'lvh.me' + end + + it 'assigns correct conference' do + get :show + + expect(response).to render_template :show + expect(assigns(:conference)).to eq conference + end + end end describe 'OPTIONS #index' do diff --git a/spec/controllers/organizations_controller_spec.rb b/spec/controllers/organizations_controller_spec.rb new file mode 100644 index 00000000..2916fdea --- /dev/null +++ b/spec/controllers/organizations_controller_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe OrganizationsController do + let!(:organization) { create(:organization) } + let!(:user) { create(:user) } + + describe 'GET #index' do + before :each do + sign_in user + get :index + end + + it { expect(response).to render_template('index') } + end +end diff --git a/spec/controllers/physical_ticket_controller_spec.rb b/spec/controllers/physical_ticket_controller_spec.rb new file mode 100644 index 00000000..476a1717 --- /dev/null +++ b/spec/controllers/physical_ticket_controller_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe PhysicalTicketsController do + let(:conference) { create(:conference) } + let(:user) { create(:user) } + let(:paid_ticket_purchase) { create(:ticket_purchase, conference: conference, user: user) } + let(:physical_ticket) { create(:physical_ticket, ticket_purchase: paid_ticket_purchase) } + + describe 'GET #show' do + before :each do + sign_in user + get :show, id: physical_ticket.token, conference_id: conference.short_title + end + + it 'assigns ticket_layout' do + ticket_layout = conference.ticket_layout.to_sym + expect(assigns(:ticket_layout)).to eq ticket_layout + end + end +end diff --git a/spec/controllers/proposals_controller_spec.rb b/spec/controllers/proposals_controller_spec.rb index 8f033973..9b01e55f 100644 --- a/spec/controllers/proposals_controller_spec.rb +++ b/spec/controllers/proposals_controller_spec.rb @@ -10,7 +10,7 @@ describe ProposalsController do describe 'GET #new' do before do # We allow new proposal only if program has open cfp - conference.program.update_attributes(cfp: create(:cfp)) + create(:cfp, program: conference.program) get :new, conference_id: conference.short_title end @@ -26,7 +26,7 @@ describe ProposalsController do describe 'POST #create' do # We allow proposal create only if program has open cfp - before { conference.program.update_attributes(cfp: create(:cfp)) } + before { create(:cfp, program: conference.program) } it 'assigns url variables' do post :create, event: attributes_for(:event, event_type_id: event_type.id), @@ -192,7 +192,7 @@ describe ProposalsController do describe 'GET #new' do before do # We allow new proposal only if program has open cfp - conference.program.update_attributes(cfp: create(:cfp)) + create(:cfp, program: conference.program) get :new, conference_id: conference.short_title end @@ -223,7 +223,7 @@ describe ProposalsController do describe 'POST #create' do # We allow proposal create only if program has open cfp - before { conference.program.update_attributes(cfp: create(:cfp)) } + before { create(:cfp, program: conference.program) } it 'assigns url variables' do post :create, event: attributes_for(:event, event_type_id: event_type.id), diff --git a/spec/controllers/subscriptions_controller_spec.rb b/spec/controllers/subscriptions_controller_spec.rb index 193ae23d..ac20a34b 100644 --- a/spec/controllers/subscriptions_controller_spec.rb +++ b/spec/controllers/subscriptions_controller_spec.rb @@ -24,7 +24,7 @@ describe SubscriptionsController do it 'shows success message in flash notice' do post :create, conference_id: conference.short_title - expect(flash[:notice]).to match("You have been subscribed to receive email notifications for #{conference.short_title}") + expect(flash[:notice]).to match("You have subscribed to receive email notifications for #{conference.title}") end it 'subscribes user to conference' do @@ -47,7 +47,7 @@ describe SubscriptionsController do it 'shows success message in flash notice' do delete :destroy, conference_id: conference.short_title - expect(flash[:notice]).to match("You have been unsubscribed and now you will not be receiving email notifications for #{conference.short_title}.") + expect(flash[:notice]).to match("You have unsubscribed and you will not be receiving email notifications for #{conference.title}.") end end end diff --git a/spec/controllers/tracks_controller_spec.rb b/spec/controllers/tracks_controller_spec.rb new file mode 100644 index 00000000..a555dba8 --- /dev/null +++ b/spec/controllers/tracks_controller_spec.rb @@ -0,0 +1,241 @@ +require 'spec_helper' + +describe TracksController do + let(:user) { create(:admin) } + + let(:conference) { create(:conference) } + let!(:regular_track) { create(:track, program: conference.program) } + let!(:self_organized_track) { create(:track, :self_organized, program: conference.program, submitter: user, name: 'My awesome track', color: '#800080') } + + before :each do + sign_in(user) + end + + describe 'GET #index' do + before :each do + get :index, conference_id: conference.short_title + end + + it 'assigns @tracks with the correct values' do + expect(assigns(:tracks).length).to eq 1 + expect(assigns(:tracks).include?(regular_track)).to eq false + expect(assigns(:tracks).include?(self_organized_track)).to eq true + end + + it 'renders the index template' do + expect(response).to render_template :index + end + end + + describe 'GET #show' do + before :each do + get :show, conference_id: conference.short_title, id: self_organized_track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'renders the show template' do + expect(response).to render_template :show + end + end + + describe 'GET #new' do + before :each do + get :new, conference_id: conference.short_title + end + + it 'assigns a new track with the correct conference' do + expect(assigns(:track)).to be_a Track + expect(assigns(:track).new_record?).to eq true + expect(assigns(:track).program_id).to eq conference.program.id + end + + it 'renders the new template' do + expect(response).to render_template :new + end + end + + describe 'POST #create' do + context 'saves successfuly' do + before :each do + post :create, track: attributes_for(:track, :self_organized, short_name: 'my_track'), conference_id: conference.short_title + end + + it 'redirects to tracks index path' do + expect(response).to redirect_to conference_program_tracks_path(conference_id: conference.short_title) + end + + it 'shows success message in flash notice' do + expect(flash[:notice]).to match('Track request successfully created.') + end + + it 'creates new track' do + expect(assigns(:track).new_record?).to eq false + end + + it 'the new tracks has the correct attributes' do + expect(assigns(:track).program_id).to eq conference.program.id + expect(assigns(:track).submitter).to eq user + expect(assigns(:track).state).to eq 'new' + expect(assigns(:track).cfp_active).to eq false + end + end + + context 'save fails' do + before :each do + allow_any_instance_of(Track).to receive(:save).and_return(false) + post :create, track: attributes_for(:track, :self_organized, short_name: 'my_track'), conference_id: conference.short_title + end + + it 'assigns a new track with the correct conference' do + expect(assigns(:track)).to be_a Track + expect(assigns(:track).new_record?).to eq true + expect(assigns(:track).program_id).to eq conference.program.id + end + + it 'renders the new template' do + expect(response).to render_template :new + end + + it 'shows error in flash message' do + expect(flash[:error]).to match("Creating Track request failed: #{assigns(:track).errors.full_messages.join('. ')}.") + end + + it 'does not create a new track' do + expect(conference.program.tracks.find_by(short_name: 'my_track')).to eq nil + end + end + end + + describe 'GET #edit' do + before :each do + get :edit, conference_id: conference.short_title, id: self_organized_track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'renders the show template' do + expect(response).to render_template :edit + end + end + + describe 'PATCH #update' do + context 'updates successfully' do + before :each do + patch :update, track: attributes_for(:track, :self_organized, color: '#FF0000'), + conference_id: conference.short_title, + id: self_organized_track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'redirects to tracks index path' do + expect(response).to redirect_to conference_program_tracks_path(conference_id: conference.short_title) + end + + it 'shows success message in flash notice' do + expect(flash[:notice]).to match('Track request successfully updated.') + end + + it 'updates the track' do + self_organized_track.reload + expect(self_organized_track.color).to eq '#FF0000' + end + end + + context 'update fails' do + before :each do + allow_any_instance_of(Track).to receive(:save).and_return(false) + patch :update, track: attributes_for(:track, :self_organized, color: '#FF0000'), + conference_id: conference.short_title, + id: self_organized_track.short_name + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'renders edit template' do + expect(response).to render_template :edit + end + + it 'shows error in flash message' do + expect(flash[:error]).to match("Track request update failed: #{assigns(:track).errors.full_messages.join('. ')}.") + end + + it 'does not update the track' do + self_organized_track.reload + expect(self_organized_track.color).to eq '#800080' + end + end + end + + describe 'PATCH #restart' do + before :each do + self_organized_track.state = 'withdrawn' + self_organized_track.save! + patch :restart, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track re-submitted.' + end + + it 'changes the track\'s state to new' do + expect(self_organized_track.state).to eq 'new' + end + end + + describe 'PATCH #confirm' do + before :each do + self_organized_track.state = 'accepted' + self_organized_track.save! + patch :confirm, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track confirmed.' + end + + it 'changes the track\'s state to confirmed' do + expect(self_organized_track.state).to eq 'confirmed' + end + end + + describe 'PATCH #withdraw' do + before :each do + self_organized_track.state = 'confirmed' + self_organized_track.save! + patch :withdraw, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track withdrawn.' + end + + it 'changes the track\'s state to withdrawn' do + expect(self_organized_track.state).to eq 'withdrawn' + end + end +end diff --git a/spec/factories/booth_request.rb b/spec/factories/booth_request.rb new file mode 100644 index 00000000..fba0cace --- /dev/null +++ b/spec/factories/booth_request.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + factory :booth_request do + booth + user + role 'responsible' + + end +end diff --git a/spec/factories/booths.rb b/spec/factories/booths.rb new file mode 100644 index 00000000..1ac3ff87 --- /dev/null +++ b/spec/factories/booths.rb @@ -0,0 +1,14 @@ +FactoryGirl.define do + factory :booth do + title { Faker::Hipster.sentence } + description { Faker::Lorem.paragraph } + reasoning { Faker::Lorem.paragraph } + website_url { Faker::Internet.url } + submitter_relationship { Faker::Lorem.paragraph } + + conference + + submitter { create(:user) } + responsible_ids { [create(:user).id] } + end +end diff --git a/spec/factories/cfps.rb b/spec/factories/cfps.rb index 1ff58b9d..6b061d53 100644 --- a/spec/factories/cfps.rb +++ b/spec/factories/cfps.rb @@ -3,7 +3,8 @@ FactoryGirl.define do factory :cfp do start_date { 1.day.ago } - end_date { 6.days.from_now } + end_date { 2.days.from_now } + cfp_type 'events' program end diff --git a/spec/factories/conferences.rb b/spec/factories/conferences.rb index 8a9186a6..1e4d8551 100644 --- a/spec/factories/conferences.rb +++ b/spec/factories/conferences.rb @@ -10,8 +10,9 @@ FactoryGirl.define do start_hour 9 end_hour 20 registration_limit 0 + ticket_layout 'portrait' description { Faker::Hipster.paragraph } - + organization after(:create) do |conference| Role.where(name: 'organizer', resource: conference).first_or_create(description: 'For the organizers of the conference (who shall have full access)') Role.where(name: 'cfp', resource: conference).first_or_create(description: 'For the members of the CfP team') diff --git a/spec/factories/organizations.rb b/spec/factories/organizations.rb new file mode 100644 index 00000000..0ee76554 --- /dev/null +++ b/spec/factories/organizations.rb @@ -0,0 +1,13 @@ +FactoryGirl.define do + factory :organization do + name { Faker::Company.name } + description { Faker::Lorem.paragraph } + + # after(:create) do |organization| + # File.open("spec/support/logos/#{1 + rand(13)}.png") do |file| + # organization.picture = file + # end + # organization.save! + # end + end +end diff --git a/spec/factories/physical_ticket.rb b/spec/factories/physical_ticket.rb new file mode 100644 index 00000000..8b4f2a0e --- /dev/null +++ b/spec/factories/physical_ticket.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :physical_ticket do + ticket_purchase + end +end diff --git a/spec/factories/programs.rb b/spec/factories/programs.rb index a1a6e1f7..e3c20153 100644 --- a/spec/factories/programs.rb +++ b/spec/factories/programs.rb @@ -5,5 +5,9 @@ FactoryGirl.define do schedule_public false schedule_fluid false conference + + trait :with_cfp do + after(:create) { |program| create(:cfp, program: program) } + end end end diff --git a/spec/factories/ticket_purchases.rb b/spec/factories/ticket_purchases.rb index 82103db3..bee365cf 100644 --- a/spec/factories/ticket_purchases.rb +++ b/spec/factories/ticket_purchases.rb @@ -4,5 +4,11 @@ FactoryGirl.define do conference ticket quantity 10 + factory :paid_ticket_purchase do + after(:build) do |ticket_purchase| + payment = create(:payment) + ticket_purchase.pay(payment) + end + end end end diff --git a/spec/factories/ticket_scanning.rb b/spec/factories/ticket_scanning.rb new file mode 100644 index 00000000..540d1fd0 --- /dev/null +++ b/spec/factories/ticket_scanning.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :ticket_scanning do + physical_ticket + end +end diff --git a/spec/factories/tickets.rb b/spec/factories/tickets.rb index 88532b86..c4bb1539 100644 --- a/spec/factories/tickets.rb +++ b/spec/factories/tickets.rb @@ -3,5 +3,8 @@ FactoryGirl.define do title { "#{Faker::Hipster.word} Ticket" } price_cents 1000 price_currency 'USD' + factory :registration_ticket do + registration_ticket true + end end end diff --git a/spec/factories/tracks.rb b/spec/factories/tracks.rb index 1b7f8083..3c0042f2 100644 --- a/spec/factories/tracks.rb +++ b/spec/factories/tracks.rb @@ -3,6 +3,19 @@ FactoryGirl.define do name { Faker::Commerce.department(2, true) } description { Faker::Lorem.sentence } color { Faker::Color.hex_color } + short_name { SecureRandom.urlsafe_base64(5) } + state 'confirmed' + cfp_active true program + + trait :self_organized do + association :submitter, factory: :user + state 'new' + cfp_active false + start_date { Date.today } + end_date { Date.today } + room + relevance { Faker::Hipster.paragraph(2) } + end end end diff --git a/spec/features/ability_spec.rb b/spec/features/ability_spec.rb deleted file mode 100644 index 80e968bd..00000000 --- a/spec/features/ability_spec.rb +++ /dev/null @@ -1,667 +0,0 @@ -require 'spec_helper' - -feature 'Has correct abilities' do - # It is necessary to use bang version of let to build roles before user - let(:conference1) { create(:full_conference) } # user is organizer - let(:conference2) { create(:full_conference) } # user is cfp - let(:conference3) { create(:full_conference) } # user is info_desk - let(:conference6) { create(:conference) } # user is organizer, venue is not set by default - - let(:role_organizer_conf1) { Role.find_by(name: 'organizer', resource: conference1) } - let(:role_organizer_conf6) { Role.find_by(name: 'organizer', resource: conference6) } - let(:role_cfp) { Role.find_by(name: 'cfp', resource: conference2) } - let(:role_info_desk) { Role.find_by(name: 'info_desk', resource: conference3) } - - let(:user) { create(:user) } - let(:user_organizer) { create(:user, role_ids: [role_organizer_conf1.id, role_organizer_conf6.id]) } - let(:user_cfp) { create(:user, role_ids: [role_cfp.id]) } - let(:user_info_desk) { create(:user, role_ids: [role_info_desk.id]) } - - scenario 'when user has no role' do - sign_in user - - visit admin_conference_path(conference1.short_title) - expect(current_path).to eq root_path - expect(flash).to eq 'You are not authorized to access this area!' - end - - scenario 'when user is organizer' do - sign_in user_organizer - - visit admin_conference_path(conference1.short_title) - expect(current_path).to eq(admin_conference_path(conference1.short_title)) - - expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') - expect(page).to have_link('Basics', href: "/admin/conferences/#{conference1.short_title}/edit") - expect(page).to have_link('Contact', href: "/admin/conferences/#{conference1.short_title}/contact/edit") - expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference1.short_title}/commercials") - expect(page).to have_link('Splashpage', href: "/admin/conferences/#{conference1.short_title}/splashpage") - expect(page).to have_link('Venue', href: "/admin/conferences/#{conference1.short_title}/venue") - expect(page).to have_link('Rooms', href: "/admin/conferences/#{conference1.short_title}/venue/rooms") - expect(page).to have_link('Lodgings', href: "/admin/conferences/#{conference1.short_title}/lodgings") - expect(page).to have_link('Program', href: "/admin/conferences/#{conference1.short_title}/program") - expect(page).to have_link('Call for Papers', href: "/admin/conferences/#{conference1.short_title}/program/cfp") - expect(page).to have_link('Events', href: "/admin/conferences/#{conference1.short_title}/program/events") - expect(page).to have_link('Tracks', href: "/admin/conferences/#{conference1.short_title}/program/tracks") - expect(page).to have_link('Event Types', href: "/admin/conferences/#{conference1.short_title}/program/event_types") - expect(page).to have_link('Difficulty Levels', href: "/admin/conferences/#{conference1.short_title}/program/difficulty_levels") - expect(page).to have_link('Schedules', href: "/admin/conferences/#{conference1.short_title}/schedules") - expect(page).to have_link('Reports', href: "/admin/conferences/#{conference1.short_title}/program/reports") - expect(page).to have_link('Registrations', href: "/admin/conferences/#{conference1.short_title}/registrations") - expect(page).to have_link('Registration Period', href: "/admin/conferences/#{conference1.short_title}/registration_period") - expect(page).to have_link('Questions', href: "/admin/conferences/#{conference1.short_title}/questions") - expect(page).to have_text('Donations') - expect(page).to have_link('Sponsorship Levels', href: "/admin/conferences/#{conference1.short_title}/sponsorship_levels") - expect(page).to have_link('Sponsors', href: "/admin/conferences/#{conference1.short_title}/sponsors") - expect(page).to have_link('Tickets', href: "/admin/conferences/#{conference1.short_title}/tickets") - expect(page).to have_text('Objectives') - expect(page).to have_link('Campaigns', href: "/admin/conferences/#{conference1.short_title}/campaigns") - expect(page).to have_link('Goals', href: "/admin/conferences/#{conference1.short_title}/targets") - expect(page).to have_link('E-Mails', href: "/admin/conferences/#{conference1.short_title}/emails") - expect(page).to have_link('Roles', href: "/admin/conferences/#{conference1.short_title}/roles") - expect(page).to have_link('Resources', href: "/admin/conferences/#{conference1.short_title}/resources") - - visit admin_conference_path(conference6.short_title) - expect(page).to have_link('Add venue', href: "/admin/conferences/#{conference6.short_title}/venue/new") - - visit edit_admin_conference_path(conference1.short_title) - expect(current_path).to eq(edit_admin_conference_path(conference1.short_title)) - - visit edit_admin_conference_contact_path(conference1.short_title) - expect(current_path).to eq(edit_admin_conference_contact_path(conference1.short_title)) - - visit admin_conference_commercials_path(conference1.short_title) - expect(current_path).to eq(admin_conference_commercials_path(conference1.short_title)) - - visit new_admin_conference_splashpage_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_splashpage_path(conference1.short_title)) - - visit edit_admin_conference_splashpage_path(conference1.short_title) - expect(current_path).to eq(edit_admin_conference_splashpage_path(conference1.short_title)) - - visit new_admin_conference_venue_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_venue_path(conference1.short_title)) - - conference1.venue = create(:venue) - visit edit_admin_conference_venue_path(conference1.short_title) - expect(current_path).to eq(edit_admin_conference_venue_path(conference1.short_title)) - - visit admin_conference_venue_rooms_path(conference1.short_title) - expect(current_path).to eq(admin_conference_venue_rooms_path(conference1.short_title)) - - create(:room, venue: conference1.venue) - visit edit_admin_conference_venue_room_path(conference1.short_title, conference1.venue.rooms.first) - expect(current_path).to eq(edit_admin_conference_venue_room_path(conference1.short_title, conference1.venue.rooms.first)) - - visit admin_conference_lodgings_path(conference1.short_title) - expect(current_path).to eq(admin_conference_lodgings_path(conference1.short_title)) - - visit new_admin_conference_lodging_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_lodging_path(conference1.short_title)) - - create(:lodging, conference: conference1) - visit edit_admin_conference_lodging_path(conference1.short_title, conference1.lodgings.first) - expect(current_path).to eq(edit_admin_conference_lodging_path(conference1.short_title, conference1.lodgings.first)) - - visit new_admin_conference_program_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_program_path(conference1.short_title)) - - visit edit_admin_conference_program_path(conference1.short_title) - expect(current_path).to eq(edit_admin_conference_program_path(conference1.short_title)) - - visit new_admin_conference_program_cfp_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_program_cfp_path(conference1.short_title)) - - visit edit_admin_conference_program_cfp_path(conference1.short_title) - expect(current_path).to eq(edit_admin_conference_program_cfp_path(conference1.short_title)) - - visit admin_conference_program_events_path(conference1.short_title) - expect(current_path).to eq(admin_conference_program_events_path(conference1.short_title)) - - create(:event, program: conference1.program) - visit edit_admin_conference_program_event_path(conference1.short_title, conference1.program.events.first) - expect(current_path).to eq(edit_admin_conference_program_event_path(conference1.short_title, conference1.program.events.first)) - - visit admin_conference_program_event_types_path(conference1.short_title) - expect(current_path).to eq(admin_conference_program_event_types_path(conference1.short_title)) - - visit new_admin_conference_program_event_type_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_program_event_type_path(conference1.short_title)) - - visit edit_admin_conference_program_event_type_path(conference1.short_title, conference1.program.event_types.first) - expect(current_path).to eq(edit_admin_conference_program_event_type_path(conference1.short_title, conference1.program.event_types.first)) - - visit admin_conference_program_difficulty_levels_path(conference1.short_title) - expect(current_path).to eq(admin_conference_program_difficulty_levels_path(conference1.short_title)) - - visit new_admin_conference_program_difficulty_level_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_program_difficulty_level_path(conference1.short_title)) - - visit edit_admin_conference_program_difficulty_level_path(conference1.short_title, conference1.program.difficulty_levels.first) - expect(current_path).to eq(edit_admin_conference_program_difficulty_level_path(conference1.short_title, conference1.program.difficulty_levels.first)) - - visit admin_conference_schedules_path(conference1.short_title) - expect(current_path).to eq(admin_conference_schedules_path(conference1.short_title)) - - create(:schedule, program: conference1.program) - visit admin_conference_schedule_path(conference1.short_title, conference1.program.schedules.first) - expect(current_path).to eq(admin_conference_schedule_path(conference1.short_title, conference1.program.schedules.first)) - - visit admin_conference_program_reports_path(conference1.short_title) - expect(current_path).to eq(admin_conference_program_reports_path(conference1.short_title)) - - visit admin_conference_registrations_path(conference1.short_title) - expect(current_path).to eq(admin_conference_registrations_path(conference1.short_title)) - - create(:registration, user: create(:user), conference: conference1) - visit edit_admin_conference_registration_path(conference1.short_title, conference1.registrations.first) - expect(current_path).to eq(edit_admin_conference_registration_path(conference1.short_title, conference1.registrations.first)) - - visit new_admin_conference_registration_period_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_registration_period_path(conference1.short_title)) - - create(:registration_period, conference: conference1) - visit edit_admin_conference_registration_period_path(conference1.short_title) - expect(current_path).to eq(edit_admin_conference_registration_period_path(conference1.short_title)) - - visit admin_conference_questions_path(conference1.short_title) - expect(current_path).to eq(admin_conference_questions_path(conference1.short_title)) - - visit admin_conference_sponsorship_levels_path(conference1.short_title) - expect(current_path).to eq(admin_conference_sponsorship_levels_path(conference1.short_title)) - - visit new_admin_conference_sponsorship_level_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_sponsorship_level_path(conference1.short_title)) - - create(:sponsorship_level, conference: conference1) - visit edit_admin_conference_sponsorship_level_path(conference1.short_title, conference1.sponsorship_levels.first) - expect(current_path).to eq(edit_admin_conference_sponsorship_level_path(conference1.short_title, conference1.sponsorship_levels.first)) - - visit admin_conference_sponsors_path(conference1.short_title) - expect(current_path).to eq(admin_conference_sponsors_path(conference1.short_title)) - - visit new_admin_conference_sponsor_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_sponsor_path(conference1.short_title)) - - create(:sponsor, conference: conference1, sponsorship_level: conference1.sponsorship_levels.first) - visit edit_admin_conference_sponsor_path(conference1.short_title, conference1.sponsors.first) - expect(current_path).to eq(edit_admin_conference_sponsor_path(conference1.short_title, conference1.sponsors.first)) - - visit admin_conference_tickets_path(conference1.short_title) - expect(current_path).to eq(admin_conference_tickets_path(conference1.short_title)) - - visit new_admin_conference_ticket_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_ticket_path(conference1.short_title)) - - create(:ticket, conference: conference1) - visit edit_admin_conference_ticket_path(conference1.short_title, conference1.tickets.first) - expect(current_path).to eq(edit_admin_conference_ticket_path(conference1.short_title, conference1.tickets.first)) - - visit admin_conference_campaigns_path(conference1.short_title) - expect(current_path).to eq(admin_conference_campaigns_path(conference1.short_title)) - - visit new_admin_conference_campaign_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_campaign_path(conference1.short_title)) - - create(:campaign, conference: conference1) - visit edit_admin_conference_campaign_path(conference1.short_title, conference1.campaigns.first) - expect(current_path).to eq(edit_admin_conference_campaign_path(conference1.short_title, conference1.campaigns.first)) - - visit admin_conference_targets_path(conference1.short_title) - expect(current_path).to eq(admin_conference_targets_path(conference1.short_title)) - - visit new_admin_conference_target_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_target_path(conference1.short_title)) - - create(:target, conference: conference1) - visit edit_admin_conference_target_path(conference1.short_title, conference1.targets.first) - expect(current_path).to eq(edit_admin_conference_target_path(conference1.short_title, conference1.targets.first)) - - visit admin_conference_program_tracks_path(conference1.short_title) - expect(current_path).to eq(admin_conference_program_tracks_path(conference1.short_title)) - - visit admin_conference_roles_path(conference1.short_title) - expect(current_path).to eq(admin_conference_roles_path(conference1.short_title)) - - visit admin_conference_emails_path(conference1.short_title) - expect(current_path).to eq(admin_conference_emails_path(conference1.short_title)) - - visit admin_conference_resources_path(conference1.short_title) - expect(current_path).to eq(admin_conference_resources_path(conference1.short_title)) - - visit new_admin_conference_resource_path(conference1.short_title) - expect(current_path).to eq(new_admin_conference_resource_path(conference1.short_title)) - - create(:resource, conference: conference1) - visit edit_admin_conference_resource_path(conference1.short_title, conference1.resources.first) - expect(current_path).to eq(edit_admin_conference_resource_path(conference1.short_title, conference1.resources.first)) - - visit admin_revision_history_path - expect(current_path).to eq(admin_revision_history_path) - end - - scenario 'when user is cfp' do - sign_in user_cfp - - visit admin_conference_path(conference2.short_title) - expect(current_path).to eq(admin_conference_path(conference2.short_title)) - - expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') - expect(page).to_not have_link('Basics', href: "/admin/conferences/#{conference2.short_title}/edit") - expect(page).to have_text('Basics') - expect(page).to_not have_link('Contact', href: "/admin/conferences/#{conference2.short_title}/contact/edit") - expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference2.short_title}/commercials") - expect(page).to_not have_link('Splashpage', href: "/admin/conferences/#{conference2.short_title}/splashpage") - expect(page).to have_link('Venue', href: "/admin/conferences/#{conference2.short_title}/venue") - expect(page).to have_link('Rooms', href: "/admin/conferences/#{conference2.short_title}/venue/rooms") - expect(page).to_not have_link('Lodgings', href: "/admin/conferences/#{conference2.short_title}/lodgings") - expect(page).to have_link('Program', href: "/admin/conferences/#{conference2.short_title}/program") - expect(page).to have_link('Call for Papers', href: "/admin/conferences/#{conference2.short_title}/program/cfp") - expect(page).to have_link('Events', href: "/admin/conferences/#{conference2.short_title}/program/events") - expect(page).to have_link('Tracks', href: "/admin/conferences/#{conference2.short_title}/program/tracks") - expect(page).to have_link('Event Types', href: "/admin/conferences/#{conference2.short_title}/program/event_types") - expect(page).to have_link('Difficulty Levels', href: "/admin/conferences/#{conference2.short_title}/program/difficulty_levels") - expect(page).to have_link('Schedules', href: "/admin/conferences/#{conference2.short_title}/schedules") - expect(page).to have_link('Reports', href: "/admin/conferences/#{conference2.short_title}/program/reports") - expect(page).to_not have_link('Registrations', href: "/admin/conferences/#{conference2.short_title}/registrations") - expect(page).to_not have_link('Registration Period', href: "/admin/conferences/#{conference2.short_title}/registration_period") - expect(page).to_not have_link('Questions', href: "/admin/conferences/#{conference2.short_title}/questions") - expect(page).to_not have_text('Donations') - expect(page).to_not have_link('Sponsorship Levels', href: "/admin/conferences/#{conference2.short_title}/supporter_levels") - expect(page).to_not have_link('Sponsors', href: "/admin/conferences/#{conference2.short_title}/sponsors") - expect(page).to_not have_link('Tickets', href: "/admin/conferences/#{conference2.short_title}/tickets") - expect(page).to_not have_text('Objectives') - expect(page).to_not have_link('Campaigns', href: "/admin/conferences/#{conference2.short_title}/campaigns") - expect(page).to_not have_link('Goals', href: "/admin/conferences/#{conference2.short_title}/targets") - expect(page).to have_link('E-Mails', href: "/admin/conferences/#{conference2.short_title}/emails") - expect(page).to have_link('Roles', href: "/admin/conferences/#{conference2.short_title}/roles") - expect(page).to have_link('Resources', href: "/admin/conferences/#{conference2.short_title}/resources") - - visit edit_admin_conference_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit edit_admin_conference_contact_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_commercials_path(conference2.short_title) - expect(current_path).to eq(admin_conference_commercials_path(conference2.short_title)) - - visit new_admin_conference_splashpage_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit edit_admin_conference_splashpage_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_venue_path(conference2.short_title) - expect(current_path).to eq(root_path) - - conference2.venue = create(:venue) - visit edit_admin_conference_venue_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_venue_rooms_path(conference2.short_title) - expect(current_path).to eq(admin_conference_venue_rooms_path(conference2.short_title)) - create(:room, venue: conference2.venue) - visit edit_admin_conference_venue_room_path(conference2.short_title, conference2.venue.rooms.first) - expect(current_path).to eq(edit_admin_conference_venue_room_path(conference2.short_title, conference2.venue.rooms.first)) - - visit admin_conference_lodgings_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_lodging_path(conference2.short_title) - expect(current_path).to eq(root_path) - - create(:lodging, conference: conference2) - visit edit_admin_conference_lodging_path(conference2.short_title, conference2.lodgings.first) - expect(current_path).to eq(root_path) - - visit new_admin_conference_program_path(conference2.short_title) - expect(current_path).to eq(new_admin_conference_program_path(conference2.short_title)) - - visit edit_admin_conference_program_path(conference2.short_title) - expect(current_path).to eq(edit_admin_conference_program_path(conference2.short_title)) - - visit new_admin_conference_program_cfp_path(conference2.short_title) - expect(current_path).to eq(new_admin_conference_program_cfp_path(conference2.short_title)) - - visit edit_admin_conference_program_cfp_path(conference2.short_title) - expect(current_path).to eq(edit_admin_conference_program_cfp_path(conference2.short_title)) - - visit admin_conference_program_events_path(conference2.short_title) - expect(current_path).to eq(admin_conference_program_events_path(conference2.short_title)) - - create(:event, program: conference2.program) - visit edit_admin_conference_program_event_path(conference2.short_title, conference2.program.events.first) - expect(current_path).to eq(edit_admin_conference_program_event_path(conference2.short_title, conference2.program.events.first)) - - visit admin_conference_program_event_types_path(conference2.short_title) - expect(current_path).to eq(admin_conference_program_event_types_path(conference2.short_title)) - - visit new_admin_conference_program_event_type_path(conference2.short_title) - expect(current_path).to eq(new_admin_conference_program_event_type_path(conference2.short_title)) - - visit edit_admin_conference_program_event_type_path(conference2.short_title, conference2.program.event_types.first) - expect(current_path).to eq(edit_admin_conference_program_event_type_path(conference2.short_title, conference2.program.event_types.first)) - - visit admin_conference_program_difficulty_levels_path(conference2.short_title) - expect(current_path).to eq(admin_conference_program_difficulty_levels_path(conference2.short_title)) - - visit new_admin_conference_program_difficulty_level_path(conference2.short_title) - expect(current_path).to eq(new_admin_conference_program_difficulty_level_path(conference2.short_title)) - - visit edit_admin_conference_program_difficulty_level_path(conference2.short_title, conference2.program.difficulty_levels.first) - expect(current_path).to eq(edit_admin_conference_program_difficulty_level_path(conference2.short_title, conference2.program.difficulty_levels.first)) - - visit admin_conference_schedules_path(conference2.short_title) - expect(current_path).to eq(admin_conference_schedules_path(conference2.short_title)) - - create(:schedule, program: conference2.program) - visit admin_conference_schedule_path(conference2.short_title, conference2.program.schedules.first) - expect(current_path).to eq(admin_conference_schedule_path(conference2.short_title, conference2.program.schedules.first)) - - visit admin_conference_program_reports_path(conference2.short_title) - expect(current_path).to eq(admin_conference_program_reports_path(conference2.short_title)) - - visit admin_conference_registrations_path(conference2.short_title) - expect(current_path).to eq(admin_conference_registrations_path(conference2.short_title)) - - create(:registration, user: create(:user), conference: conference2) - visit edit_admin_conference_registration_path(conference2.short_title, conference2.registrations.first) - expect(current_path).to eq(root_path) - - visit new_admin_conference_registration_period_path(conference2.short_title) - expect(current_path).to eq(root_path) - - create(:registration_period, conference: conference2) - visit edit_admin_conference_registration_period_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_questions_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_sponsorship_levels_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_sponsorship_level_path(conference2.short_title) - expect(current_path).to eq(root_path) - - create(:sponsorship_level, conference: conference2) - visit edit_admin_conference_sponsorship_level_path(conference2.short_title, conference2.sponsorship_levels.first) - expect(current_path).to eq(root_path) - - visit admin_conference_sponsors_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_sponsor_path(conference2.short_title) - expect(current_path).to eq(root_path) - - create(:sponsor, conference: conference2, sponsorship_level: conference2.sponsorship_levels.first) - visit edit_admin_conference_sponsor_path(conference2.short_title, conference2.sponsors.first) - expect(current_path).to eq(root_path) - - visit admin_conference_tickets_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_ticket_path(conference2.short_title) - expect(current_path).to eq(root_path) - - create(:ticket, conference: conference2) - visit edit_admin_conference_ticket_path(conference2.short_title, conference2.tickets.first) - expect(current_path).to eq(root_path) - - visit admin_conference_campaigns_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_campaign_path(conference2.short_title) - expect(current_path).to eq(root_path) - - create(:campaign, conference: conference2) - visit edit_admin_conference_campaign_path(conference2.short_title, conference2.campaigns.first) - expect(current_path).to eq(root_path) - - visit admin_conference_targets_path(conference2.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_target_path(conference2.short_title) - expect(current_path).to eq(root_path) - - create(:target, conference: conference2) - visit edit_admin_conference_target_path(conference2.short_title, conference2.targets.first) - expect(current_path).to eq(root_path) - - visit admin_conference_program_tracks_path(conference2.short_title) - expect(current_path).to eq(admin_conference_program_tracks_path(conference2.short_title)) - - visit admin_conference_roles_path(conference2.short_title) - expect(current_path).to eq(admin_conference_roles_path(conference2.short_title)) - - visit admin_conference_emails_path(conference2.short_title) - expect(current_path).to eq(admin_conference_emails_path(conference2.short_title)) - - visit admin_conference_resources_path(conference2.short_title) - expect(current_path).to eq(admin_conference_resources_path(conference2.short_title)) - - visit new_admin_conference_resource_path(conference2.short_title) - expect(current_path).to eq(new_admin_conference_resource_path(conference2.short_title)) - - create(:resource, conference: conference2) - visit edit_admin_conference_resource_path(conference2.short_title, conference2.resources.first) - expect(current_path).to eq(edit_admin_conference_resource_path(conference2.short_title, conference2.resources.first)) - - visit admin_revision_history_path - expect(current_path).to eq(root_path) - end - - scenario 'when user is info desk' do - sign_in user_info_desk - - visit admin_conference_path(conference3.short_title) - expect(current_path).to eq(admin_conference_path(conference3.short_title)) - - expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') - expect(page).to_not have_link('Basics', href: "/admin/conferences/#{conference3.short_title}/edit") - expect(page).to have_text('Basics') - expect(page).to_not have_link('Contact', href: "/admin/conferences/#{conference3.short_title}/contact/edit") - expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference3.short_title}/commercials") - expect(page).to_not have_link('Splashpage', href: "/admin/conferences/#{conference3.short_title}/splashpage") - expect(page).to_not have_link('Venue', href: "/admin/conferences/#{conference3.short_title}/venue") - expect(page).to_not have_link('Rooms', href: "/admin/conferences/#{conference3.short_title}/venue/rooms") - expect(page).to_not have_link('Lodgings', href: "/admin/conferences/#{conference3.short_title}/lodgings") - expect(page).to_not have_link('Program', href: "/admin/conferences/#{conference3.short_title}/program") - expect(page).to_not have_link('Call for Papers', href: "/admin/conferences/#{conference2.short_title}/program/cfp") - expect(page).to_not have_link('Events', href: "/admin/conferences/#{conference3.short_title}/program/events") - expect(page).to_not have_link('Tracks', href: "/admin/conferences/#{conference3.short_title}/program/tracks") - expect(page).to_not have_link('Event Types', href: "/admin/conferences/#{conference3.short_title}/program/event_types") - expect(page).to_not have_link('Difficulty Levels', href: "/admin/conferences/#{conference3.short_title}/program/difficulty_levels") - expect(page).to_not have_link('Schedules', href: "/admin/conferences/#{conference3.short_title}/schedules") - expect(page).to_not have_link('Reports', href: "/admin/conferences/#{conference3.short_title}/program/reports") - expect(page).to have_link('Registrations', href: "/admin/conferences/#{conference3.short_title}/registrations") - expect(page).to_not have_link('Registration Period', href: "/admin/conferences/#{conference3.short_title}/registration_period") - expect(page).to have_link('Questions', href: "/admin/conferences/#{conference3.short_title}/questions") - expect(page).to_not have_text('Donations') - expect(page).to_not have_link('Sponsorship Levels', href: "/admin/conferences/#{conference3.short_title}/sponsorship_levels") - expect(page).to_not have_link('Sponsors', href: "/admin/conferences/#{conference3.short_title}/sponsors") - expect(page).to_not have_link('Tickets', href: "/admin/conferences/#{conference3.short_title}/tickets") - expect(page).to_not have_text('Objectives') - expect(page).to_not have_link('Campaigns', href: "/admin/conferences/#{conference3.short_title}/campaigns") - expect(page).to_not have_link('Goals', href: "/admin/conferences/#{conference3.short_title}/targets") - expect(page).to_not have_link('E-Mails', href: "/admin/conferences/#{conference3.short_title}/emails") - expect(page).to have_link('Roles', href: "/admin/conferences/#{conference3.short_title}/roles") - expect(page).to have_link('Resources', href: "/admin/conferences/#{conference3.short_title}/resources") - - visit edit_admin_conference_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit edit_admin_conference_contact_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_commercials_path(conference3.short_title) - expect(current_path).to eq(admin_conference_commercials_path(conference3.short_title)) - - visit new_admin_conference_splashpage_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit edit_admin_conference_splashpage_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_venue_path(conference3.short_title) - expect(current_path).to eq(root_path) - - conference3.venue = create(:venue) - visit edit_admin_conference_venue_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_venue_rooms_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:room, venue: conference3.venue) - visit edit_admin_conference_venue_room_path(conference3.short_title, conference3.venue.rooms.first) - expect(current_path).to eq(root_path) - - visit admin_conference_lodgings_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_lodging_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:lodging, conference: conference3) - visit edit_admin_conference_lodging_path(conference3.short_title, conference3.lodgings.first) - expect(current_path).to eq(root_path) - - visit new_admin_conference_program_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit edit_admin_conference_program_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_program_cfp_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit edit_admin_conference_program_cfp_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_program_events_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:event, program: conference3.program) - visit edit_admin_conference_program_event_path(conference3.short_title, conference3.program.events.first) - expect(current_path).to eq(root_path) - - visit admin_conference_program_event_types_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_program_event_type_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit edit_admin_conference_program_event_type_path(conference3.short_title, conference3.program.event_types.first) - expect(current_path).to eq(root_path) - - visit admin_conference_program_difficulty_levels_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_program_difficulty_level_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit edit_admin_conference_program_difficulty_level_path(conference3.short_title, conference3.program.difficulty_levels.first) - expect(current_path).to eq(root_path) - - visit admin_conference_schedules_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:schedule, program: conference3.program) - visit admin_conference_schedule_path(conference3.short_title, conference3.program.schedules.first) - expect(current_path).to eq(root_path) - - visit admin_conference_program_reports_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_registrations_path(conference3.short_title) - expect(current_path).to eq(admin_conference_registrations_path(conference3.short_title)) - - create(:registration, user: create(:user), conference: conference3) - visit edit_admin_conference_registration_path(conference3.short_title, conference3.registrations.first) - expect(current_path).to eq(edit_admin_conference_registration_path(conference3.short_title, conference3.registrations.first)) - - visit new_admin_conference_registration_period_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:registration_period, conference: conference3) - visit edit_admin_conference_registration_period_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_questions_path(conference3.short_title) - expect(current_path).to eq(admin_conference_questions_path(conference3.short_title)) - - visit admin_conference_sponsorship_levels_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_sponsorship_level_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:sponsorship_level, conference: conference3) - visit edit_admin_conference_sponsorship_level_path(conference3.short_title, conference3.sponsorship_levels.first) - expect(current_path).to eq(root_path) - - visit admin_conference_sponsors_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_sponsor_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:sponsor, conference: conference3, sponsorship_level: conference3.sponsorship_levels.first) - visit edit_admin_conference_sponsor_path(conference3.short_title, conference3.sponsors.first) - expect(current_path).to eq(root_path) - - visit admin_conference_tickets_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_ticket_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:ticket, conference: conference3) - visit edit_admin_conference_ticket_path(conference3.short_title, conference3.tickets.first) - expect(current_path).to eq(root_path) - - visit admin_conference_campaigns_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_campaign_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:campaign, conference: conference3) - visit edit_admin_conference_campaign_path(conference3.short_title, conference3.campaigns.first) - expect(current_path).to eq(root_path) - - visit admin_conference_targets_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit new_admin_conference_target_path(conference3.short_title) - expect(current_path).to eq(root_path) - - create(:target, conference: conference3) - visit edit_admin_conference_target_path(conference3.short_title, conference3.targets.first) - expect(current_path).to eq(root_path) - - visit admin_conference_program_tracks_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_roles_path(conference3.short_title) - expect(current_path).to eq(admin_conference_roles_path(conference3.short_title)) - - visit admin_conference_emails_path(conference3.short_title) - expect(current_path).to eq(root_path) - - visit admin_conference_resources_path(conference3.short_title) - expect(current_path).to eq(admin_conference_resources_path(conference3.short_title)) - - visit new_admin_conference_resource_path(conference3.short_title) - expect(current_path).to eq(new_admin_conference_resource_path(conference3.short_title)) - - create(:resource, conference: conference3) - visit edit_admin_conference_resource_path(conference3.short_title, conference3.resources.first) - expect(current_path).to eq(edit_admin_conference_resource_path(conference3.short_title, conference3.resources.first)) - - visit admin_revision_history_path - expect(current_path).to eq(root_path) - end -end diff --git a/spec/features/base_controller_spec.rb b/spec/features/base_controller_spec.rb index be52c3a9..1f75f81b 100644 --- a/spec/features/base_controller_spec.rb +++ b/spec/features/base_controller_spec.rb @@ -25,7 +25,7 @@ feature 'BaseController' do it 'not an admin it redirects to root_path' do visit admin_conferences_path expect(current_path).to eq root_path - expect(flash).to eq 'You are not authorized to access this area!' + expect(flash).to eq 'You are not authorized to access this page.' end it 'an admin he can access the admin area' do diff --git a/spec/features/cfp_ability_spec.rb b/spec/features/cfp_ability_spec.rb new file mode 100644 index 00000000..7229ab8d --- /dev/null +++ b/spec/features/cfp_ability_spec.rb @@ -0,0 +1,304 @@ +require 'spec_helper' + +feature 'Has correct abilities' do + + let(:organization) { create(:organization) } + let(:conference) { create(:full_conference, organization: organization) } + let(:role_cfp) { Role.find_by(name: 'cfp', resource: conference) } + let(:user_cfp) { create(:user, role_ids: [role_cfp.id]) } + + context 'when user is cfp' do + before do + sign_in user_cfp + end + + scenario 'for organization and conference attributes' do + visit admin_conference_path(conference.short_title) + expect(current_path).to eq(admin_conference_path(conference.short_title)) + + expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') + expect(page).to_not have_link('Basics', href: "/admin/conferences/#{conference.short_title}/edit") + expect(page).to have_text('Basics') + expect(page).to_not have_link('Contact', href: "/admin/conferences/#{conference.short_title}/contact/edit") + expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference.short_title}/commercials") + expect(page).to_not have_link('Splashpage', href: "/admin/conferences/#{conference.short_title}/splashpage") + expect(page).to have_link('Venue', href: "/admin/conferences/#{conference.short_title}/venue") + expect(page).to have_link('Rooms', href: "/admin/conferences/#{conference.short_title}/venue/rooms") + expect(page).to have_link('Program', href: "/admin/conferences/#{conference.short_title}/program") + expect(page).to have_link('Call for Papers', href: "/admin/conferences/#{conference.short_title}/program/cfps") + expect(page).to have_link('Events', href: "/admin/conferences/#{conference.short_title}/program/events") + expect(page).to have_link('Tracks', href: "/admin/conferences/#{conference.short_title}/program/tracks") + expect(page).to have_link('Event Types', href: "/admin/conferences/#{conference.short_title}/program/event_types") + expect(page).to have_link('Difficulty Levels', href: "/admin/conferences/#{conference.short_title}/program/difficulty_levels") + expect(page).to have_link('Schedules', href: "/admin/conferences/#{conference.short_title}/schedules") + expect(page).to have_link('Reports', href: "/admin/conferences/#{conference.short_title}/program/reports") + expect(page).to_not have_link('Registrations', href: "/admin/conferences/#{conference.short_title}/registrations") + expect(page).to_not have_link('Questions', href: "/admin/conferences/#{conference.short_title}/questions") + expect(page).to have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails") + expect(page).to_not have_link('Lodgings', href: "/admin/conferences/#{conference.short_title}/lodgings") + expect(page).to_not have_link('Registration Period', href: "/admin/conferences/#{conference.short_title}/registration_period") + expect(page).to_not have_text('Donations') + expect(page).to_not have_link('Sponsorship Levels', href: "/admin/conferences/#{conference.short_title}/sponsorship_levels") + expect(page).to_not have_link('Sponsors', href: "/admin/conferences/#{conference.short_title}/sponsors") + expect(page).to_not have_link('Tickets', href: "/admin/conferences/#{conference.short_title}/tickets") + expect(page).to_not have_text('Objectives') + expect(page).to_not have_link('Campaigns', href: "/admin/conferences/#{conference.short_title}/campaigns") + expect(page).to_not have_link('Goals', href: "/admin/conferences/#{conference.short_title}/targets") + expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles") + expect(page).to have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources") + expect(page).to_not have_link('New Conference', href: '/admin/conferences/new') + + visit admin_conference_venue_rooms_path(conference.short_title) + expect(current_path).to eq(admin_conference_venue_rooms_path(conference.short_title)) + create(:room, venue: conference.venue) + visit edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first) + expect(current_path).to eq(edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first)) + + visit new_admin_conference_program_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_path(conference.short_title)) + + visit edit_admin_conference_program_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_program_path(conference.short_title)) + + # Only event type exists + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + # Event and booth cfps exist + cfb = create(:cfp, cfp_type: 'booths', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + visit edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp) + expect(current_path).to eq(edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp)) + + # Event, booth, track cfps exist + call_for_tracks = create(:cfp, cfp_type: 'tracks', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq root_path + + # Booth and track cfps exist + conference.program.cfp.destroy! + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + # Only booth exists + call_for_tracks.destroy! + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + visit edit_admin_conference_program_cfp_path(conference.short_title, cfb) + expect(current_path). to eq(edit_admin_conference_program_cfp_path(conference.short_title, cfb)) + + # No cfp exists + cfb.destroy + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + # Only Tracks cfp exists + call_for_tracks = create(:cfp, cfp_type: 'tracks', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + visit edit_admin_conference_program_cfp_path(conference.short_title, call_for_tracks) + expect(current_path).to eq edit_admin_conference_program_cfp_path(conference.short_title, call_for_tracks) + + # Event and track cfps exist + create(:cfp, cfp_type: 'events', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + call_for_tracks.destroy! + create(:event, program: conference.program) + visit edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first) + expect(current_path).to eq(edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first)) + + visit admin_conference_program_event_types_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_event_types_path(conference.short_title)) + + visit new_admin_conference_program_event_type_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_event_type_path(conference.short_title)) + + visit edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first) + expect(current_path).to eq(edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first)) + + visit admin_conference_program_difficulty_levels_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_difficulty_levels_path(conference.short_title)) + + visit new_admin_conference_program_difficulty_level_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_difficulty_level_path(conference.short_title)) + + visit edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first) + expect(current_path).to eq(edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first)) + + visit admin_conference_schedules_path(conference.short_title) + expect(current_path).to eq(admin_conference_schedules_path(conference.short_title)) + + create(:schedule, program: conference.program) + visit admin_conference_schedule_path(conference.short_title, conference.program.schedules.first) + expect(current_path).to eq(admin_conference_schedule_path(conference.short_title, conference.program.schedules.first)) + + visit admin_conference_program_reports_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_reports_path(conference.short_title)) + + visit admin_conference_registrations_path(conference.short_title) + expect(current_path).to eq(admin_conference_registrations_path(conference.short_title)) + + create(:registration, user: create(:user), conference: conference) + visit edit_admin_conference_registration_path(conference.short_title, conference.registrations.first) + expect(current_path).to eq(root_path) + + visit new_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:registration_period, conference: conference) + visit edit_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_questions_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_program_tracks_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_tracks_path(conference.short_title)) + + visit admin_conference_roles_path(conference.short_title) + expect(current_path).to eq(admin_conference_roles_path(conference.short_title)) + + visit admin_conference_emails_path(conference.short_title) + expect(current_path).to eq(admin_conference_emails_path(conference.short_title)) + + visit admin_conference_path(conference.short_title) + expect(current_path).to eq(admin_conference_path(conference.short_title)) + + visit admin_organizations_path + expect(current_path).to eq(admin_organizations_path) + + visit edit_admin_organization_path(organization) + expect(current_path).to eq(root_path) + + visit new_admin_organization_path + expect(current_path).to eq(root_path) + + visit edit_admin_conference_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit edit_admin_conference_contact_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_commercials_path(conference.short_title) + expect(current_path).to eq(admin_conference_commercials_path(conference.short_title)) + + visit new_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit edit_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq(root_path) + + conference.venue = create(:venue) + visit edit_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_lodgings_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_lodging_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:lodging, conference: conference) + visit edit_admin_conference_lodging_path(conference.short_title, conference.lodgings.first) + expect(current_path).to eq(root_path) + + visit new_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:registration_period, conference: conference) + visit edit_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_sponsorship_levels_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_sponsorship_level_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:sponsorship_level, conference: conference) + visit edit_admin_conference_sponsorship_level_path(conference.short_title, conference.sponsorship_levels.first) + expect(current_path).to eq(root_path) + + visit admin_conference_sponsors_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_sponsor_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:sponsor, conference: conference, sponsorship_level: conference.sponsorship_levels.first) + visit edit_admin_conference_sponsor_path(conference.short_title, conference.sponsors.first) + expect(current_path).to eq(root_path) + + visit admin_conference_tickets_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_ticket_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:ticket, conference: conference) + visit edit_admin_conference_ticket_path(conference.short_title, conference.tickets.first) + expect(current_path).to eq(root_path) + + visit admin_conference_campaigns_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_campaign_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:campaign, conference: conference) + visit edit_admin_conference_campaign_path(conference.short_title, conference.campaigns.first) + expect(current_path).to eq(root_path) + + visit admin_conference_targets_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_target_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:target, conference: conference) + visit edit_admin_conference_target_path(conference.short_title, conference.targets.first) + expect(current_path).to eq(root_path) + + visit admin_conference_roles_path(conference.short_title) + expect(current_path).to eq(admin_conference_roles_path(conference.short_title)) + + visit admin_conference_booths_path(conference.short_title) + expect(current_path).to eq(admin_conference_booths_path(conference.short_title)) + + visit new_admin_conference_booth_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_booth_path(conference.short_title)) + + create(:booth, conference: conference) + visit edit_admin_conference_booth_path(conference.short_title, conference.booths.first) + expect(current_path).to eq(edit_admin_conference_booth_path(conference.short_title, conference.booths.first)) + + visit admin_conference_resources_path(conference.short_title) + expect(current_path).to eq(admin_conference_resources_path(conference.short_title)) + + visit new_admin_conference_resource_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_resource_path(conference.short_title)) + + create(:resource, conference: conference) + visit edit_admin_conference_resource_path(conference.short_title, conference.resources.first) + expect(current_path).to eq(edit_admin_conference_resource_path(conference.short_title, conference.resources.first)) + + visit admin_users_path + expect(current_path).to eq(root_path) + + visit admin_user_path(user_cfp) + expect(current_path).to eq(root_path) + + visit admin_revision_history_path + expect(current_path).to eq(admin_revision_history_path) + end + end +end diff --git a/spec/features/cfp_spec.rb b/spec/features/cfp_spec.rb index 8869de1f..7ecfb1e6 100644 --- a/spec/features/cfp_spec.rb +++ b/spec/features/cfp_spec.rb @@ -31,6 +31,8 @@ feature Conference do # Validations expect(flash) .to eq('Call for papers successfully created.') + + visit admin_conference_program_cfp_path(conference.short_title, conference.program.cfp) expect(find('#start_date').text).to eq(today.strftime('%A, %B %-d. %Y')) expect(find('#end_date').text).to eq((today + 6).strftime('%A, %B %-d. %Y')) @@ -38,11 +40,11 @@ feature Conference do end scenario 'update cfp', feature: true, js: true do - conference.program.cfp = create(:cfp) + create(:cfp, program: conference.program) expected_count = Cfp.count sign_in organizer - visit admin_conference_program_cfp_path(conference.short_title) + visit admin_conference_program_cfp_path(conference.short_title, conference.program.cfp) click_link 'Edit' # Validate update with empty start date will not saved @@ -65,6 +67,8 @@ feature Conference do # Validations expect(flash) .to eq('Call for papers successfully updated.') + + visit admin_conference_program_cfp_path(conference.short_title, conference.program.cfp) expect(find('#start_date').text).to eq(today.strftime('%A, %B %-d. %Y')) expect(find('#end_date').text).to eq((today + 14).strftime('%A, %B %-d. %Y')) expect(Cfp.count).to eq(expected_count) diff --git a/spec/features/conference_spec.rb b/spec/features/conference_spec.rb index d17ce5fc..0dc4e228 100644 --- a/spec/features/conference_spec.rb +++ b/spec/features/conference_spec.rb @@ -2,13 +2,15 @@ require 'spec_helper' feature Conference do let!(:user) { create(:admin) } - + let!(:organization) { create(:organization) } shared_examples 'add and update conference' do scenario 'adds a new conference', feature: true, js: true do expected_count = Conference.count + 1 sign_in user visit new_admin_conference_path + + select organization.name, from: 'conference_organization_id' fill_in 'conference_title', with: 'Example Con' fill_in 'conference_short_title', with: 'ExCon' @@ -27,7 +29,7 @@ feature Conference do expect(flash) .to eq('Conference was successfully created.') expect(Conference.count).to eq(expected_count) - + expect(Conference.last.organization).to eq(organization) expect(user.has_role? :organizer, Conference.last).to eq(true) end @@ -71,6 +73,15 @@ feature Conference do end describe 'admin' do + let!(:conference) { create(:conference) } + + scenario 'has organization name in menu bar for conference views', feature: true, js: true do + sign_in user + visit admin_conference_path(conference.short_title) + + expect(find('.navbar-brand').text).to eq(conference.organization.name) + end + it_behaves_like 'add and update conference' end end diff --git a/spec/features/info_desk_ability_spec.rb b/spec/features/info_desk_ability_spec.rb new file mode 100644 index 00000000..f4ef4fce --- /dev/null +++ b/spec/features/info_desk_ability_spec.rb @@ -0,0 +1,250 @@ +require 'spec_helper' + +feature 'Has correct abilities' do + + let(:organization) { create(:organization) } + let(:conference) { create(:full_conference, organization: organization) } + let(:role_info_desk) { Role.find_by(name: 'info_desk', resource: conference) } + let(:user_info_desk) { create(:user, role_ids: [role_info_desk.id]) } + + context 'when user is info desk' do + before do + sign_in user_info_desk + end + + scenario 'for organization and conference attributes' do + visit admin_conference_path(conference.short_title) + expect(current_path).to eq(admin_conference_path(conference.short_title)) + + expect(page).to_not have_link('Basics', href: "/admin/conferences/#{conference.short_title}/edit") + expect(page).to have_text('Basics') + expect(page).to_not have_link('Contact', href: "/admin/conferences/#{conference.short_title}/contact/edit") + expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference.short_title}/commercials") + expect(page).to_not have_link('Splashpage', href: "/admin/conferences/#{conference.short_title}/splashpage") + expect(page).to_not have_link('Lodgings', href: "/admin/conferences/#{conference.short_title}/lodgings") + expect(page).to_not have_link('Registration Period', href: "/admin/conferences/#{conference.short_title}/registration_period") + expect(page).to_not have_text('Donations') + expect(page).to_not have_link('Sponsorship Levels', href: "/admin/conferences/#{conference.short_title}/sponsorship_levels") + expect(page).to_not have_link('Sponsors', href: "/admin/conferences/#{conference.short_title}/sponsors") + expect(page).to_not have_link('Tickets', href: "/admin/conferences/#{conference.short_title}/tickets") + expect(page).to_not have_text('Objectives') + expect(page).to_not have_link('Campaigns', href: "/admin/conferences/#{conference.short_title}/campaigns") + expect(page).to_not have_link('Goals', href: "/admin/conferences/#{conference.short_title}/targets") + expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles") + expect(page).to have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources") + expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') + expect(page).to_not have_link('Venue', href: "/admin/conferences/#{conference.short_title}/venue") + expect(page).to_not have_link('Rooms', href: "/admin/conferences/#{conference.short_title}/venue/rooms") + expect(page).to_not have_link('Program', href: "/admin/conferences/#{conference.short_title}/program") + expect(page).to_not have_link('Call for Papers', href: "/admin/conferences/#{conference.short_title}/program/cfps") + expect(page).to_not have_link('Events', href: "/admin/conferences/#{conference.short_title}/program/events") + expect(page).to_not have_link('Tracks', href: "/admin/conferences/#{conference.short_title}/program/tracks") + expect(page).to_not have_link('Event Types', href: "/admin/conferences/#{conference.short_title}/program/event_types") + expect(page).to_not have_link('Difficulty Levels', href: "/admin/conferences/#{conference.short_title}/program/difficulty_levels") + expect(page).to_not have_link('Schedules', href: "/admin/conferences/#{conference.short_title}/schedules") + expect(page).to_not have_link('Reports', href: "/admin/conferences/#{conference.short_title}/program/reports") + expect(page).to have_link('Registrations', href: "/admin/conferences/#{conference.short_title}/registrations") + expect(page).to have_link('Questions', href: "/admin/conferences/#{conference.short_title}/questions") + expect(page).to_not have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails") + expect(page).to_not have_link('New Conference', href: '/admin/conferences/new') + + visit admin_organizations_path + expect(current_path).to eq(admin_organizations_path) + + visit edit_admin_organization_path(organization) + expect(current_path).to eq(root_path) + + visit new_admin_organization_path + expect(current_path).to eq(root_path) + + visit edit_admin_conference_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit edit_admin_conference_contact_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_commercials_path(conference.short_title) + expect(current_path).to eq(admin_conference_commercials_path(conference.short_title)) + + visit new_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit edit_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq(root_path) + + conference.venue = create(:venue) + visit edit_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_lodgings_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_lodging_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:lodging, conference: conference) + visit edit_admin_conference_lodging_path(conference.short_title, conference.lodgings.first) + expect(current_path).to eq(root_path) + + visit new_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:registration_period, conference: conference) + visit edit_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_sponsorship_levels_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_sponsorship_level_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:sponsorship_level, conference: conference) + visit edit_admin_conference_sponsorship_level_path(conference.short_title, conference.sponsorship_levels.first) + expect(current_path).to eq(root_path) + + visit admin_conference_sponsors_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_sponsor_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:sponsor, conference: conference, sponsorship_level: conference.sponsorship_levels.first) + visit edit_admin_conference_sponsor_path(conference.short_title, conference.sponsors.first) + expect(current_path).to eq(root_path) + + visit admin_conference_tickets_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_ticket_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:ticket, conference: conference) + visit edit_admin_conference_ticket_path(conference.short_title, conference.tickets.first) + expect(current_path).to eq(root_path) + + visit admin_conference_campaigns_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_campaign_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:campaign, conference: conference) + visit edit_admin_conference_campaign_path(conference.short_title, conference.campaigns.first) + expect(current_path).to eq(root_path) + + visit admin_conference_targets_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_target_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:target, conference: conference) + visit edit_admin_conference_target_path(conference.short_title, conference.targets.first) + expect(current_path).to eq(root_path) + + visit admin_conference_roles_path(conference.short_title) + expect(current_path).to eq(admin_conference_roles_path(conference.short_title)) + + visit admin_conference_resources_path(conference.short_title) + expect(current_path).to eq(admin_conference_resources_path(conference.short_title)) + + visit new_admin_conference_resource_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_resource_path(conference.short_title)) + + create(:resource, conference: conference) + visit edit_admin_conference_resource_path(conference.short_title, conference.resources.first) + expect(current_path).to eq(edit_admin_conference_resource_path(conference.short_title, conference.resources.first)) + + visit admin_revision_history_path + expect(current_path).to eq(admin_revision_history_path) + + visit admin_conference_path(conference.short_title) + expect(current_path).to eq(admin_conference_path(conference.short_title)) + + visit admin_conference_venue_rooms_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:room, venue: conference.venue) + visit edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first) + expect(current_path).to eq(root_path) + + visit new_admin_conference_program_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit edit_admin_conference_program_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq root_path + + conference.program.cfp.destroy! + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq root_path + create(:cfp, program: conference.program) + + visit edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp) + expect(current_path).to eq(root_path) + + visit admin_conference_program_events_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:event, program: conference.program) + visit edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first) + expect(current_path).to eq(root_path) + + visit admin_conference_program_event_types_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_program_event_type_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first) + expect(current_path).to eq(root_path) + + visit admin_conference_program_difficulty_levels_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit new_admin_conference_program_difficulty_level_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first) + expect(current_path).to eq(root_path) + + visit admin_conference_schedules_path(conference.short_title) + expect(current_path).to eq(root_path) + + create(:schedule, program: conference.program) + visit admin_conference_schedule_path(conference.short_title, conference.program.schedules.first) + expect(current_path).to eq(root_path) + + visit admin_conference_program_reports_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_conference_registrations_path(conference.short_title) + expect(current_path).to eq(admin_conference_registrations_path(conference.short_title)) + + create(:registration, user: create(:user), conference: conference) + visit edit_admin_conference_registration_path(conference.short_title, conference.registrations.first) + expect(current_path).to eq(edit_admin_conference_registration_path(conference.short_title, conference.registrations.first)) + + visit admin_conference_questions_path(conference.short_title) + expect(current_path).to eq(admin_conference_questions_path(conference.short_title)) + + visit admin_conference_program_tracks_path(conference.short_title) + expect(current_path).to eq(root_path) + + visit admin_users_path + expect(current_path).to eq(root_path) + + visit admin_user_path(user_info_desk) + expect(current_path).to eq(root_path) + + visit admin_conference_emails_path(conference.short_title) + expect(current_path).to eq(root_path) + end + end +end diff --git a/spec/features/organization_admin_ability_spec.rb b/spec/features/organization_admin_ability_spec.rb new file mode 100644 index 00000000..d84a7c01 --- /dev/null +++ b/spec/features/organization_admin_ability_spec.rb @@ -0,0 +1,280 @@ +require 'spec_helper' + +feature 'Has correct abilities' do + let(:organization) { create(:organization) } + let(:conference) { create(:full_conference, organization: organization) } + let(:role_organization_admin) { Role.find_by(name: 'organization_admin', resource: organization) } + let(:user_organization_admin) { create(:user, role_ids: [role_organization_admin.id]) } + let!(:registration_ticket) { create(:registration_ticket, conference: conference) } + + context 'when user is organization_admin' do + before do + sign_in user_organization_admin + end + + scenario 'for organization attributes' do + visit admin_organizations_path + expect(current_path).to eq(admin_organizations_path) + + visit edit_admin_organization_path(organization) + expect(current_path).to eq(edit_admin_organization_path(organization)) + + visit new_admin_organization_path + expect(current_path).to eq(root_path) + end + + scenario 'for conference attributes' do + visit admin_conference_path(conference.short_title) + expect(current_path).to eq(admin_conference_path(conference.short_title)) + + expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') + expect(page).to have_link('Basics', href: "/admin/conferences/#{conference.short_title}/edit") + expect(page).to have_link('Contact', href: "/admin/conferences/#{conference.short_title}/contact/edit") + expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference.short_title}/commercials") + expect(page).to have_link('Splashpage', href: "/admin/conferences/#{conference.short_title}/splashpage") + expect(page).to have_link('Venue', href: "/admin/conferences/#{conference.short_title}/venue") + expect(page).to have_link('Rooms', href: "/admin/conferences/#{conference.short_title}/venue/rooms") + expect(page).to have_link('Lodgings', href: "/admin/conferences/#{conference.short_title}/lodgings") + expect(page).to have_link('Program', href: "/admin/conferences/#{conference.short_title}/program") + expect(page).to have_link('Call for Papers', href: "/admin/conferences/#{conference.short_title}/program/cfps") + expect(page).to have_link('Events', href: "/admin/conferences/#{conference.short_title}/program/events") + expect(page).to have_link('Tracks', href: "/admin/conferences/#{conference.short_title}/program/tracks") + expect(page).to have_link('Event Types', href: "/admin/conferences/#{conference.short_title}/program/event_types") + expect(page).to have_link('Difficulty Levels', href: "/admin/conferences/#{conference.short_title}/program/difficulty_levels") + expect(page).to have_link('Schedules', href: "/admin/conferences/#{conference.short_title}/schedules") + expect(page).to have_link('Reports', href: "/admin/conferences/#{conference.short_title}/program/reports") + expect(page).to have_link('Registrations', href: "/admin/conferences/#{conference.short_title}/registrations") + expect(page).to have_link('Registration Period', href: "/admin/conferences/#{conference.short_title}/registration_period") + expect(page).to have_link('Questions', href: "/admin/conferences/#{conference.short_title}/questions") + expect(page).to have_text('Donations') + expect(page).to have_link('Sponsorship Levels', href: "/admin/conferences/#{conference.short_title}/sponsorship_levels") + expect(page).to have_link('Sponsors', href: "/admin/conferences/#{conference.short_title}/sponsors") + expect(page).to have_link('Tickets', href: "/admin/conferences/#{conference.short_title}/tickets") + expect(page).to have_text('Objectives') + expect(page).to have_link('Campaigns', href: "/admin/conferences/#{conference.short_title}/campaigns") + expect(page).to have_link('Goals', href: "/admin/conferences/#{conference.short_title}/targets") + expect(page).to have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails") + expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles") + expect(page).to have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources") + expect(page).to have_link('New Conference', href: '/admin/conferences/new') + + visit edit_admin_conference_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_path(conference.short_title)) + + visit edit_admin_conference_contact_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_contact_path(conference.short_title)) + + visit admin_conference_commercials_path(conference.short_title) + expect(current_path).to eq(admin_conference_commercials_path(conference.short_title)) + + visit new_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_splashpage_path(conference.short_title)) + + visit edit_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_splashpage_path(conference.short_title)) + + visit new_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_venue_path(conference.short_title)) + + conference.venue = create(:venue) + visit edit_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_venue_path(conference.short_title)) + + visit admin_conference_venue_rooms_path(conference.short_title) + expect(current_path).to eq(admin_conference_venue_rooms_path(conference.short_title)) + + create(:room, venue: conference.venue) + visit edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first) + expect(current_path).to eq(edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first)) + + visit admin_conference_lodgings_path(conference.short_title) + expect(current_path).to eq(admin_conference_lodgings_path(conference.short_title)) + + visit new_admin_conference_lodging_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_lodging_path(conference.short_title)) + + create(:lodging, conference: conference) + visit edit_admin_conference_lodging_path(conference.short_title, conference.lodgings.first) + expect(current_path).to eq(edit_admin_conference_lodging_path(conference.short_title, conference.lodgings.first)) + + visit new_admin_conference_program_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_path(conference.short_title)) + + visit edit_admin_conference_program_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_program_path(conference.short_title)) + + # Only event exists + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + # Event and booth cfps exist + cfb = create(:cfp, cfp_type: 'booths', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + visit edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp) + expect(current_path).to eq(edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp)) + + # Event, booth, track cfps exist + call_for_tracks = create(:cfp, cfp_type: 'tracks', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq root_path + + # Booth and track cfps exist + conference.program.cfp.destroy! + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + # Only booth exists + call_for_tracks.destroy! + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + visit edit_admin_conference_program_cfp_path(conference.short_title, cfb) + expect(current_path). to eq(edit_admin_conference_program_cfp_path(conference.short_title, cfb)) + + # No cfp exists + cfb.destroy + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + # Only Tracks cfp exists + call_for_tracks = create(:cfp, cfp_type: 'tracks', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + visit edit_admin_conference_program_cfp_path(conference.short_title, call_for_tracks) + expect(current_path).to eq edit_admin_conference_program_cfp_path(conference.short_title, call_for_tracks) + + # Event and track cfps exist + create(:cfp, cfp_type: 'events', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + call_for_tracks.destroy! + visit admin_conference_program_events_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_events_path(conference.short_title)) + + create(:event, program: conference.program) + visit edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first) + expect(current_path).to eq(edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first)) + + visit admin_conference_program_event_types_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_event_types_path(conference.short_title)) + + visit new_admin_conference_program_event_type_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_event_type_path(conference.short_title)) + + visit edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first) + expect(current_path).to eq(edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first)) + + visit admin_conference_program_difficulty_levels_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_difficulty_levels_path(conference.short_title)) + + visit new_admin_conference_program_difficulty_level_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_difficulty_level_path(conference.short_title)) + + visit edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first) + expect(current_path).to eq(edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first)) + + visit admin_conference_schedules_path(conference.short_title) + expect(current_path).to eq(admin_conference_schedules_path(conference.short_title)) + + create(:schedule, program: conference.program) + visit admin_conference_schedule_path(conference.short_title, conference.program.schedules.first) + expect(current_path).to eq(admin_conference_schedule_path(conference.short_title, conference.program.schedules.first)) + + visit admin_conference_program_reports_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_reports_path(conference.short_title)) + + visit admin_conference_registrations_path(conference.short_title) + expect(current_path).to eq(admin_conference_registrations_path(conference.short_title)) + + create(:registration, user: create(:user), conference: conference) + visit edit_admin_conference_registration_path(conference.short_title, conference.registrations.first) + expect(current_path).to eq(edit_admin_conference_registration_path(conference.short_title, conference.registrations.first)) + + visit new_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_registration_period_path(conference.short_title)) + + create(:registration_period, conference: conference) + visit edit_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_registration_period_path(conference.short_title)) + + visit admin_conference_questions_path(conference.short_title) + expect(current_path).to eq(admin_conference_questions_path(conference.short_title)) + + visit admin_conference_sponsorship_levels_path(conference.short_title) + expect(current_path).to eq(admin_conference_sponsorship_levels_path(conference.short_title)) + + visit new_admin_conference_sponsorship_level_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_sponsorship_level_path(conference.short_title)) + + create(:sponsorship_level, conference: conference) + visit edit_admin_conference_sponsorship_level_path(conference.short_title, conference.sponsorship_levels.first) + expect(current_path).to eq(edit_admin_conference_sponsorship_level_path(conference.short_title, conference.sponsorship_levels.first)) + + visit admin_conference_sponsors_path(conference.short_title) + expect(current_path).to eq(admin_conference_sponsors_path(conference.short_title)) + + visit new_admin_conference_sponsor_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_sponsor_path(conference.short_title)) + + create(:sponsor, conference: conference, sponsorship_level: conference.sponsorship_levels.first) + visit edit_admin_conference_sponsor_path(conference.short_title, conference.sponsors.first) + expect(current_path).to eq(edit_admin_conference_sponsor_path(conference.short_title, conference.sponsors.first)) + + visit admin_conference_tickets_path(conference.short_title) + expect(current_path).to eq(admin_conference_tickets_path(conference.short_title)) + + visit new_admin_conference_ticket_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_ticket_path(conference.short_title)) + + create(:ticket, conference: conference) + visit edit_admin_conference_ticket_path(conference.short_title, conference.tickets.first) + expect(current_path).to eq(edit_admin_conference_ticket_path(conference.short_title, conference.tickets.first)) + + visit admin_conference_campaigns_path(conference.short_title) + expect(current_path).to eq(admin_conference_campaigns_path(conference.short_title)) + + visit new_admin_conference_campaign_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_campaign_path(conference.short_title)) + + create(:campaign, conference: conference) + visit edit_admin_conference_campaign_path(conference.short_title, conference.campaigns.first) + expect(current_path).to eq(edit_admin_conference_campaign_path(conference.short_title, conference.campaigns.first)) + + visit admin_conference_targets_path(conference.short_title) + expect(current_path).to eq(admin_conference_targets_path(conference.short_title)) + + visit new_admin_conference_target_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_target_path(conference.short_title)) + + create(:target, conference: conference) + visit edit_admin_conference_target_path(conference.short_title, conference.targets.first) + expect(current_path).to eq(edit_admin_conference_target_path(conference.short_title, conference.targets.first)) + + visit admin_conference_program_tracks_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_tracks_path(conference.short_title)) + + visit admin_conference_roles_path(conference.short_title) + expect(current_path).to eq(admin_conference_roles_path(conference.short_title)) + + visit admin_conference_emails_path(conference.short_title) + expect(current_path).to eq(admin_conference_emails_path(conference.short_title)) + + visit admin_conference_resources_path(conference.short_title) + expect(current_path).to eq(admin_conference_resources_path(conference.short_title)) + + visit new_admin_conference_resource_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_resource_path(conference.short_title)) + + create(:resource, conference: conference) + visit edit_admin_conference_resource_path(conference.short_title, conference.resources.first) + expect(current_path).to eq(edit_admin_conference_resource_path(conference.short_title, conference.resources.first)) + + visit admin_revision_history_path + expect(current_path).to eq(admin_revision_history_path) + end + end +end diff --git a/spec/features/organization_spec.rb b/spec/features/organization_spec.rb new file mode 100644 index 00000000..6fcf590b --- /dev/null +++ b/spec/features/organization_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +feature Organization do + let!(:organization) { create(:organization) } + let!(:organization_admin_role) { Role.find_by(name: 'organization_admin', resource: organization) } + let(:organization_admin) { create(:user, role_ids: [organization_admin_role.id]) } + let(:admin_user) { create(:admin) } + + shared_examples 'successfully updates an organization' do + scenario 'updates a exsisting organization', feature: true, js: true do + visit edit_admin_organization_path(organization) + fill_in 'organization_name', with: 'changed name' + + click_button 'Update Organization' + + organization.reload + expect(flash).to eq('Organization successfully updated') + expect(organization.name).to eq('changed name') + end + end + + context 'signed in as site admin' do + before do + sign_in admin_user + end + scenario 'creates a new organization', feature: true, js: true do + visit new_admin_organization_path + fill_in 'organization_name', with: 'Organization name' + + click_button 'Create Organization' + + expect(flash).to eq('Organization successfully created') + expect(Organization.last.name).to eq('Organization name') + end + + it_behaves_like 'successfully updates an organization' + end + + context 'signed in as organization admin' do + before do + sign_in organization_admin + end + scenario "can't create new organization", feature: true, js: true do + visit new_admin_organization_path + + expect(flash).to eq('You are not authorized to access this page.') + end + + it_behaves_like 'successfully updates an organization' + end +end diff --git a/spec/features/organizer_ability_spec.rb b/spec/features/organizer_ability_spec.rb new file mode 100644 index 00000000..0127ea2f --- /dev/null +++ b/spec/features/organizer_ability_spec.rb @@ -0,0 +1,302 @@ +require 'spec_helper' + +feature 'Has correct abilities' do + + let(:organization) { create(:organization) } + let(:conference) { create(:full_conference, organization: organization) } + let(:other_conference) { create(:conference, organization: organization) } # user is organizer, venue is not set by default + let(:role_organizer_conf) { Role.find_by(name: 'organizer', resource: conference) } + let(:role_organizer_other_conf) { Role.find_by(name: 'organizer', resource: other_conference) } + let(:user_organizer) { create(:user, role_ids: [role_organizer_conf.id, role_organizer_other_conf.id]) } + let!(:registration_ticket) { create(:registration_ticket, conference: conference) } + + context 'when user is organizer' do + before do + sign_in user_organizer + end + + scenario 'for organization attributes' do + visit admin_organizations_path + expect(current_path).to eq(admin_organizations_path) + + visit edit_admin_organization_path(organization) + expect(current_path).to eq(root_path) + + visit new_admin_organization_path + expect(current_path).to eq(root_path) + end + + scenario 'for conference attributes' do + visit admin_conference_path(conference.short_title) + expect(current_path).to eq(admin_conference_path(conference.short_title)) + + expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') + expect(page).to have_link('Basics', href: "/admin/conferences/#{conference.short_title}/edit") + expect(page).to have_link('Contact', href: "/admin/conferences/#{conference.short_title}/contact/edit") + expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference.short_title}/commercials") + expect(page).to have_link('Splashpage', href: "/admin/conferences/#{conference.short_title}/splashpage") + expect(page).to have_link('Venue', href: "/admin/conferences/#{conference.short_title}/venue") + expect(page).to have_link('Rooms', href: "/admin/conferences/#{conference.short_title}/venue/rooms") + expect(page).to have_link('Lodgings', href: "/admin/conferences/#{conference.short_title}/lodgings") + expect(page).to have_link('Program', href: "/admin/conferences/#{conference.short_title}/program") + expect(page).to have_link('Call for Papers', href: "/admin/conferences/#{conference.short_title}/program/cfps") + expect(page).to have_link('Events', href: "/admin/conferences/#{conference.short_title}/program/events") + expect(page).to have_link('Tracks', href: "/admin/conferences/#{conference.short_title}/program/tracks") + expect(page).to have_link('Event Types', href: "/admin/conferences/#{conference.short_title}/program/event_types") + expect(page).to have_link('Difficulty Levels', href: "/admin/conferences/#{conference.short_title}/program/difficulty_levels") + expect(page).to have_link('Schedules', href: "/admin/conferences/#{conference.short_title}/schedules") + expect(page).to have_link('Reports', href: "/admin/conferences/#{conference.short_title}/program/reports") + expect(page).to have_link('Registrations', href: "/admin/conferences/#{conference.short_title}/registrations") + expect(page).to have_link('Registration Period', href: "/admin/conferences/#{conference.short_title}/registration_period") + expect(page).to have_link('Questions', href: "/admin/conferences/#{conference.short_title}/questions") + expect(page).to have_text('Donations') + expect(page).to have_link('Sponsorship Levels', href: "/admin/conferences/#{conference.short_title}/sponsorship_levels") + expect(page).to have_link('Sponsors', href: "/admin/conferences/#{conference.short_title}/sponsors") + expect(page).to have_link('Tickets', href: "/admin/conferences/#{conference.short_title}/tickets") + expect(page).to have_text('Objectives') + expect(page).to have_link('Campaigns', href: "/admin/conferences/#{conference.short_title}/campaigns") + expect(page).to have_link('Goals', href: "/admin/conferences/#{conference.short_title}/targets") + expect(page).to have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails") + expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles") + expect(page).to have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources") + expect(page).to_not have_link('New Conference', href: '/admin/conferences/new') + + visit admin_conference_path(other_conference.short_title) + expect(page).to have_link('Add venue', href: "/admin/conferences/#{other_conference.short_title}/venue/new") + + visit edit_admin_conference_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_path(conference.short_title)) + + visit edit_admin_conference_contact_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_contact_path(conference.short_title)) + + visit admin_conference_commercials_path(conference.short_title) + expect(current_path).to eq(admin_conference_commercials_path(conference.short_title)) + + visit new_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_splashpage_path(conference.short_title)) + + visit edit_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_splashpage_path(conference.short_title)) + + visit new_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_venue_path(conference.short_title)) + + conference.venue = create(:venue) + visit edit_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_venue_path(conference.short_title)) + + visit admin_conference_venue_rooms_path(conference.short_title) + expect(current_path).to eq(admin_conference_venue_rooms_path(conference.short_title)) + + create(:room, venue: conference.venue) + visit edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first) + expect(current_path).to eq(edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first)) + + visit admin_conference_lodgings_path(conference.short_title) + expect(current_path).to eq(admin_conference_lodgings_path(conference.short_title)) + + visit new_admin_conference_lodging_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_lodging_path(conference.short_title)) + + create(:lodging, conference: conference) + visit edit_admin_conference_lodging_path(conference.short_title, conference.lodgings.first) + expect(current_path).to eq(edit_admin_conference_lodging_path(conference.short_title, conference.lodgings.first)) + + visit new_admin_conference_program_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_path(conference.short_title)) + + visit edit_admin_conference_program_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_program_path(conference.short_title)) + + # Only event type exists + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + # Event and booth cfps exist + cfb = create(:cfp, cfp_type: 'booths', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + visit edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp) + expect(current_path).to eq(edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp)) + + # Event, booth, track cfps exist + call_for_tracks = create(:cfp, cfp_type: 'tracks', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq root_path + + # Booth and track cfps exist + conference.program.cfp.destroy! + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + # Only booth exists + call_for_tracks.destroy! + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + visit edit_admin_conference_program_cfp_path(conference.short_title, cfb) + expect(current_path). to eq(edit_admin_conference_program_cfp_path(conference.short_title, cfb)) + + # No cfp exists + cfb.destroy + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_cfp_path(conference.short_title)) + + # Only Tracks cfp exists + call_for_tracks = create(:cfp, cfp_type: 'tracks', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + visit edit_admin_conference_program_cfp_path(conference.short_title, call_for_tracks) + expect(current_path).to eq edit_admin_conference_program_cfp_path(conference.short_title, call_for_tracks) + + # Event and track cfps exist + create(:cfp, cfp_type: 'events', program: conference.program) + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq new_admin_conference_program_cfp_path(conference.short_title) + + call_for_tracks.destroy! + visit admin_conference_program_events_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_events_path(conference.short_title)) + + create(:event, program: conference.program) + visit edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first) + expect(current_path).to eq(edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first)) + + visit admin_conference_program_event_types_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_event_types_path(conference.short_title)) + + visit new_admin_conference_program_event_type_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_event_type_path(conference.short_title)) + + visit edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first) + expect(current_path).to eq(edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first)) + + visit admin_conference_program_difficulty_levels_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_difficulty_levels_path(conference.short_title)) + + visit new_admin_conference_program_difficulty_level_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_program_difficulty_level_path(conference.short_title)) + + visit edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first) + expect(current_path).to eq(edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first)) + + visit admin_conference_schedules_path(conference.short_title) + expect(current_path).to eq(admin_conference_schedules_path(conference.short_title)) + + create(:schedule, program: conference.program) + visit admin_conference_schedule_path(conference.short_title, conference.program.schedules.first) + expect(current_path).to eq(admin_conference_schedule_path(conference.short_title, conference.program.schedules.first)) + + visit admin_conference_program_reports_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_reports_path(conference.short_title)) + + visit admin_conference_registrations_path(conference.short_title) + expect(current_path).to eq(admin_conference_registrations_path(conference.short_title)) + + create(:registration, user: create(:user), conference: conference) + visit edit_admin_conference_registration_path(conference.short_title, conference.registrations.first) + expect(current_path).to eq(edit_admin_conference_registration_path(conference.short_title, conference.registrations.first)) + + visit new_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_registration_period_path(conference.short_title)) + + create(:registration_period, conference: conference) + visit edit_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq(edit_admin_conference_registration_period_path(conference.short_title)) + + visit admin_conference_questions_path(conference.short_title) + expect(current_path).to eq(admin_conference_questions_path(conference.short_title)) + + visit admin_conference_sponsorship_levels_path(conference.short_title) + expect(current_path).to eq(admin_conference_sponsorship_levels_path(conference.short_title)) + + visit new_admin_conference_sponsorship_level_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_sponsorship_level_path(conference.short_title)) + + create(:sponsorship_level, conference: conference) + visit edit_admin_conference_sponsorship_level_path(conference.short_title, conference.sponsorship_levels.first) + expect(current_path).to eq(edit_admin_conference_sponsorship_level_path(conference.short_title, conference.sponsorship_levels.first)) + + visit admin_conference_sponsors_path(conference.short_title) + expect(current_path).to eq(admin_conference_sponsors_path(conference.short_title)) + + visit new_admin_conference_sponsor_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_sponsor_path(conference.short_title)) + + create(:sponsor, conference: conference, sponsorship_level: conference.sponsorship_levels.first) + visit edit_admin_conference_sponsor_path(conference.short_title, conference.sponsors.first) + expect(current_path).to eq(edit_admin_conference_sponsor_path(conference.short_title, conference.sponsors.first)) + + visit admin_conference_tickets_path(conference.short_title) + expect(current_path).to eq(admin_conference_tickets_path(conference.short_title)) + + visit new_admin_conference_ticket_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_ticket_path(conference.short_title)) + + create(:ticket, conference: conference) + visit edit_admin_conference_ticket_path(conference.short_title, conference.tickets.first) + expect(current_path).to eq(edit_admin_conference_ticket_path(conference.short_title, conference.tickets.first)) + + visit admin_conference_campaigns_path(conference.short_title) + expect(current_path).to eq(admin_conference_campaigns_path(conference.short_title)) + + visit new_admin_conference_campaign_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_campaign_path(conference.short_title)) + + create(:campaign, conference: conference) + visit edit_admin_conference_campaign_path(conference.short_title, conference.campaigns.first) + expect(current_path).to eq(edit_admin_conference_campaign_path(conference.short_title, conference.campaigns.first)) + + visit admin_conference_targets_path(conference.short_title) + expect(current_path).to eq(admin_conference_targets_path(conference.short_title)) + + visit new_admin_conference_target_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_target_path(conference.short_title)) + + create(:target, conference: conference) + visit edit_admin_conference_target_path(conference.short_title, conference.targets.first) + expect(current_path).to eq(edit_admin_conference_target_path(conference.short_title, conference.targets.first)) + + visit admin_conference_booths_path(conference.short_title) + expect(current_path).to eq(admin_conference_booths_path(conference.short_title)) + + visit new_admin_conference_booth_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_booth_path(conference.short_title)) + + create(:booth, conference: conference) + visit edit_admin_conference_booth_path(conference.short_title, conference.booths.first) + expect(current_path).to eq(edit_admin_conference_booth_path(conference.short_title, conference.booths.first)) + + visit admin_conference_program_tracks_path(conference.short_title) + expect(current_path).to eq(admin_conference_program_tracks_path(conference.short_title)) + + visit admin_conference_roles_path(conference.short_title) + expect(current_path).to eq(admin_conference_roles_path(conference.short_title)) + + visit admin_conference_emails_path(conference.short_title) + expect(current_path).to eq(admin_conference_emails_path(conference.short_title)) + + visit admin_conference_resources_path(conference.short_title) + expect(current_path).to eq(admin_conference_resources_path(conference.short_title)) + + visit new_admin_conference_resource_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_resource_path(conference.short_title)) + + create(:resource, conference: conference) + visit edit_admin_conference_resource_path(conference.short_title, conference.resources.first) + expect(current_path).to eq(edit_admin_conference_resource_path(conference.short_title, conference.resources.first)) + + visit admin_users_path + expect(current_path).to eq(root_path) + + visit admin_user_path(user_organizer) + expect(current_path).to eq(root_path) + + visit admin_revision_history_path + expect(current_path).to eq(admin_revision_history_path) + end + end +end diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index b5620475..9dda06bf 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -28,23 +28,23 @@ feature Event do scenario 'rejects a proposal', feature: true, js: true do visit admin_conference_program_events_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' click_button 'New' click_link "reject_event_#{@event.id}" - expect(flash).to eq('Event rejected!') + expect(page).to have_content 'Event rejected!' @event.reload expect(@event.state).to eq('rejected') end scenario 'accepts a proposal', feature: true, js: true do visit admin_conference_program_events_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' click_button 'New' click_link "accept_event_#{@event.id}" - expect(flash).to eq('Event accepted!') - expect(page.has_content?('Unconfirmed')).to be true + expect(page).to have_content 'Event accepted!' + expect(page).to have_content 'Unconfirmed' @event.reload expect(@event.state).to eq('unconfirmed') end @@ -52,11 +52,11 @@ feature Event do scenario 'restarts review of a proposal', feature: true, js: true do @event.reject!(@options) visit admin_conference_program_events_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' click_button 'Rejected' click_link "restart_event_#{@event.id}" - expect(flash).to eq('Review started!') + expect(page).to have_content 'Review started!' @event.reload expect(@event.state).to eq('new') end @@ -83,7 +83,7 @@ feature Event do fill_in 'event_abstract', with: 'Lorem ipsum abstract' click_button 'Create Proposal' - expect(flash).to eq('Proposal was successfully submitted.') + expect(page).to have_content 'Proposal was successfully submitted.' expect(Event.count).to eq(expected_count_event) expect(User.count).to eq(expected_count_user) @@ -101,7 +101,7 @@ feature Event do select('Easy', from: 'event[difficulty_level_id]') click_button 'Update Proposal' - expect(flash).to eq('Proposal was successfully updated.') + expect(page).to have_content 'Proposal was successfully updated.' end scenario 'signed_in user submits a valid proposal', feature: true, js: true do @@ -119,7 +119,7 @@ feature Event do fill_in 'event_description', with: 'Lorem ipsum description' click_button 'Create Proposal' - expect(flash).to eq('Proposal was successfully submitted.') + expect(page).to have_content 'Proposal was successfully submitted.' expect(current_path).to eq(conference_program_proposals_path(conference.short_title)) expect(Event.count).to eq(expected_count) @@ -128,11 +128,10 @@ feature Event do scenario 'confirms a proposal', feature: true, js: true do sign_in participant visit conference_program_proposals_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' expect(@event.state).to eq('unconfirmed') click_link "confirm_proposal_#{@event.id}" - expect(flash) - .to eq('The proposal was confirmed. Please register to attend the conference.') + expect(page).to have_content 'The proposal was confirmed. Please register to attend the conference.' expect(current_path).to eq(new_conference_conference_registration_path(conference.short_title)) @event.reload expect(@event.state).to eq('confirmed') @@ -142,9 +141,9 @@ feature Event do sign_in participant @event.confirm! visit conference_program_proposals_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' click_link "delete_proposal_#{@event.id}" - expect(flash).to eq('Proposal was successfully withdrawn.') + expect(page).to have_content 'Proposal was successfully withdrawn.' @event.reload expect(@event.state).to eq('withdrawn') end diff --git a/spec/features/registration_periods_spec.rb b/spec/features/registration_periods_spec.rb index c6cc992e..5c1c90b5 100644 --- a/spec/features/registration_periods_spec.rb +++ b/spec/features/registration_periods_spec.rb @@ -6,6 +6,7 @@ feature RegistrationPeriod do let!(:conference) { create(:conference) } let!(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) } let!(:organizer) { create(:user, email: 'admin@example.com', role_ids: [organizer_role.id]) } + let!(:registration_ticket) { create(:registration_ticket, conference: conference) } shared_examples 'successfully' do scenario 'create and update registration period', js: true do diff --git a/spec/features/roles_spec.rb b/spec/features/roles_spec.rb index ebdeacaf..ba9e5d4c 100644 --- a/spec/features/roles_spec.rb +++ b/spec/features/roles_spec.rb @@ -98,6 +98,51 @@ feature Role do end end + context 'organization_admin' do + let!(:organization) { create(:organization) } + let!(:org_admin_role) { Role.find_by(name: 'organization_admin', resource: organization) } + let!(:organization_admin) { create(:user, role_ids: [org_admin_role.id]) } + let(:user_with_no_role) { create :user } + let!(:other_organization) { create(:organization) } + + before do + sign_in organization_admin + visit admin_organizations_path + end + + context 'for the organization it belongs to' do + scenario 'successfully adds role organization_admin' do + click_link('Admins', href: admins_admin_organization_path(organization.id)) + + fill_in 'user_email', with: user_with_no_role.email + click_button 'Add' + user_with_no_role.reload + + expect(user_with_no_role.has_role?('organization_admin', organization)).to eq true + end + + scenario 'successfully removes role organization_admin' do + click_link('Admins', href: admins_admin_organization_path(organization.id)) + + first('tr').find('.btn-danger').click + expect(organization_admin.has_role?('organization_admin', organization)).to eq false + end + end + + context 'for the organizations it does not belong to' do + scenario 'does not successfully add role organization_admin' do + click_link('Admins', href: admins_admin_organization_path(other_organization.id)) + + expect(page.has_field?('user_email')).to eq false + end + + scenario 'does not successfully removes role organization_admin' do + click_link('Admins', href: admins_admin_organization_path(other_organization.id)) + expect(page.has_css?('.btn-danger')).to eq false + end + end + end + context 'organizer' do Role.all.each.map(&:name).each do |role| it_behaves_like 'successfully', role, 'organizer' diff --git a/spec/features/splashpage_spec.rb b/spec/features/splashpage_spec.rb index e913d023..ee9b63d5 100644 --- a/spec/features/splashpage_spec.rb +++ b/spec/features/splashpage_spec.rb @@ -61,4 +61,15 @@ feature Splashpage do expect(current_path).to eq(root_path) end end + + context 'public splashpage already created' do + let!(:splashpage) { create(:splashpage, conference: conference, public: true)} + + scenario 'should have organization name', feature: true, js: true do + sign_in participant + visit conference_path(conference.short_title) + + expect(page).to have_text(conference.organization.name) + end + end end diff --git a/spec/features/sponsor_spec.rb b/spec/features/sponsor_spec.rb index c6b5009e..b12ee90d 100644 --- a/spec/features/sponsor_spec.rb +++ b/spec/features/sponsor_spec.rb @@ -36,7 +36,14 @@ feature Sponsor do end # Remove sponsor - click_link 'Delete' + visit admin_conference_sponsors_path( + conference_id: conference.short_title + ) + within('table#sponsors') do + page.accept_confirm do + click_link 'Delete' + end + end expect(flash).to eq('Sponsor successfully deleted.') expect(page).to_not have_selector('table#sponsors') end diff --git a/spec/features/ticket_purchases_spec.rb b/spec/features/ticket_purchases_spec.rb index f8c755a3..9cace981 100644 --- a/spec/features/ticket_purchases_spec.rb +++ b/spec/features/ticket_purchases_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' feature Registration do let!(:ticket) { create(:ticket) } let!(:free_ticket) { create(:ticket, price_cents: 0) } - let!(:conference) { create(:conference, title: 'ExampleCon', tickets: [ticket, free_ticket], registration_period: create(:registration_period, start_date: 3.days.ago)) } + let!(:first_registration_ticket) { create(:registration_ticket, price_cents: 0) } + let!(:second_registration_ticket) { create(:registration_ticket, price_cents: 0) } + let!(:conference) { create(:conference, title: 'ExampleCon', tickets: [ticket, free_ticket, first_registration_ticket, second_registration_ticket], registration_period: create(:registration_period, start_date: 3.days.ago)) } let!(:participant) { create(:user) } context 'as a participant' do @@ -101,12 +103,42 @@ feature Registration do click_button 'Continue' - expect(current_path).to eq(conference_conference_registration_path(conference.short_title)) + expect(current_path).to eq(conference_physical_tickets_path(conference.short_title)) purchase = TicketPurchase.where(user_id: participant.id, ticket_id: free_ticket.id).first expect(purchase.quantity).to eq(5) expect(purchase.paid).to be true + end - expect(page.has_content?("5 #{free_ticket.title} Tickets for $ 0")).to be true + scenario 'purchases more than one registration tickets of a single type' do + visit root_path + click_link 'Register' + + expect(current_path).to eq(new_conference_conference_registration_path(conference.short_title)) + click_button 'Register' + + fill_in "tickets__#{first_registration_ticket.id}", with: '5' + expect(current_path).to eq(conference_tickets_path(conference.short_title)) + + click_button 'Continue' + + expect(current_path).to eq(conference_tickets_path(conference.short_title)) + end + + scenario 'purchases one registration ticket of a different types' do + visit root_path + click_link 'Register' + + expect(current_path).to eq(new_conference_conference_registration_path(conference.short_title)) + click_button 'Register' + + fill_in "tickets__#{first_registration_ticket.id}", with: '1' + fill_in "tickets__#{second_registration_ticket.id}", with: '1' + expect(current_path).to eq(conference_tickets_path(conference.short_title)) + + click_button 'Continue' + + expect(flash).to eq('Oops, something went wrong with your purchase! You cannot buy more than one registration tickets.') + expect(current_path).to eq(conference_tickets_path(conference.short_title)) end end diff --git a/spec/features/track_organizer_ability_spec.rb b/spec/features/track_organizer_ability_spec.rb new file mode 100644 index 00000000..67e0c8de --- /dev/null +++ b/spec/features/track_organizer_ability_spec.rb @@ -0,0 +1,252 @@ +require 'spec_helper' + +feature 'Has correct abilities' do + + let(:organization) { create(:organization) } + let(:conference) { create(:full_conference, organization: organization) } + let(:self_organized_track) { create(:track, :self_organized, program: conference.program, state: 'confirmed') } + let(:role_track_organizer) { Role.where(name: 'track_organizer', resource: self_organized_track).first_or_create } + let(:user_track_organizer) { create(:user, role_ids: [role_track_organizer.id]) } + + context 'when user is track organizer' do + before do + sign_in user_track_organizer + end + + scenario 'for organization and conference attributes' do + visit admin_conference_path(conference.short_title) + expect(current_path).to eq(admin_conference_path(conference.short_title)) + + expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard') + expect(page).to_not have_link('Basics', href: "/admin/conferences/#{conference.short_title}/edit") + expect(page).to have_text('Basics') + expect(page).to_not have_link('Contact', href: "/admin/conferences/#{conference.short_title}/contact/edit") + expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference.short_title}/commercials") + expect(page).to_not have_link('Splashpage', href: "/admin/conferences/#{conference.short_title}/splashpage") + expect(page).to_not have_link('Venue', href: "/admin/conferences/#{conference.short_title}/venue") + expect(page).to_not have_link('Rooms', href: "/admin/conferences/#{conference.short_title}/venue/rooms") + expect(page).to_not have_link('Lodgings', href: "/admin/conferences/#{conference.short_title}/lodgings") + expect(page).to have_link('Program', href: "/admin/conferences/#{conference.short_title}/program") + expect(page).to_not have_link('Call for Papers', href: "/admin/conferences/#{conference.short_title}/program/cfps") + expect(page).to have_link('Events', href: "/admin/conferences/#{conference.short_title}/program/events") + expect(page).to have_link('Tracks', href: "/admin/conferences/#{conference.short_title}/program/tracks") + expect(page).to_not have_link('Event Types', href: "/admin/conferences/#{conference.short_title}/program/event_types") + expect(page).to_not have_link('Difficulty Levels', href: "/admin/conferences/#{conference.short_title}/program/difficulty_levels") + expect(page).to have_link('Schedules', href: "/admin/conferences/#{conference.short_title}/schedules") + expect(page).to have_link('Reports', href: "/admin/conferences/#{conference.short_title}/program/reports") + expect(page).to_not have_link('Registrations', href: "/admin/conferences/#{conference.short_title}/registrations") + expect(page).to_not have_link('Registration Period', href: "/admin/conferences/#{conference.short_title}/registration_period") + expect(page).to_not have_link('Questions', href: "/admin/conferences/#{conference.short_title}/questions") + expect(page).to_not have_text('Donations') + expect(page).to_not have_link('Sponsorship Levels', href: "/admin/conferences/#{conference.short_title}/sponsorship_levels") + expect(page).to_not have_link('Sponsors', href: "/admin/conferences/#{conference.short_title}/sponsors") + expect(page).to_not have_link('Tickets', href: "/admin/conferences/#{conference.short_title}/tickets") + expect(page).to_not have_text('Objectives') + expect(page).to_not have_link('Campaigns', href: "/admin/conferences/#{conference.short_title}/campaigns") + expect(page).to_not have_link('Goals', href: "/admin/conferences/#{conference.short_title}/targets") + expect(page).to_not have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails") + expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles") + expect(page).to_not have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources") + expect(page).to_not have_link('New Conference', href: '/admin/conferences/new') + + visit edit_admin_conference_path(conference.short_title) + expect(current_path).to eq root_path + + visit edit_admin_conference_contact_path(conference.short_title) + expect(current_path).to eq root_path + + visit admin_conference_commercials_path(conference.short_title) + expect(current_path).to eq admin_conference_commercials_path(conference.short_title) + + visit new_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq root_path + + visit edit_admin_conference_splashpage_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq root_path + + conference.venue = create(:venue) + visit edit_admin_conference_venue_path(conference.short_title) + expect(current_path).to eq root_path + + visit admin_conference_venue_rooms_path(conference.short_title) + expect(current_path).to eq root_path + + create(:room, venue: conference.venue) + visit edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first) + expect(current_path).to eq root_path + + visit admin_conference_lodgings_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_lodging_path(conference.short_title) + expect(current_path).to eq root_path + + create(:lodging, conference: conference) + visit edit_admin_conference_lodging_path(conference.short_title, conference.lodgings.first) + expect(current_path).to eq root_path + + visit new_admin_conference_program_path(conference.short_title) + expect(current_path).to eq root_path + + visit edit_admin_conference_program_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_program_cfp_path(conference.short_title) + expect(current_path).to eq root_path + + visit edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp) + expect(current_path).to eq root_path + + visit admin_conference_program_events_path(conference.short_title) + expect(current_path).to eq admin_conference_program_events_path(conference.short_title) + + create(:event, program: conference.program) + visit edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first) + expect(current_path).to eq root_path + + self_organized_track_event = create(:event, program: conference.program, track: self_organized_track) + visit edit_admin_conference_program_event_path(conference.short_title, self_organized_track_event) + expect(current_path).to eq(edit_admin_conference_program_event_path(conference.short_title, self_organized_track_event)) + + visit admin_conference_program_event_types_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_program_event_type_path(conference.short_title) + expect(current_path).to eq root_path + + visit edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first) + expect(current_path).to eq root_path + + visit admin_conference_program_difficulty_levels_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_program_difficulty_level_path(conference.short_title) + expect(current_path).to eq root_path + + visit edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first) + expect(current_path).to eq root_path + + visit admin_conference_schedules_path(conference.short_title) + expect(current_path).to eq admin_conference_schedules_path(conference.short_title) + + create(:schedule, program: conference.program) + visit admin_conference_schedule_path(conference.short_title, conference.program.schedules.first) + expect(current_path).to eq root_path + + self_organized_track_schedule = create(:schedule, program: conference.program, track: self_organized_track) + visit admin_conference_schedule_path(conference.short_title, self_organized_track_schedule) + expect(current_path).to eq admin_conference_schedule_path(conference.short_title, self_organized_track_schedule) + + visit admin_conference_program_reports_path(conference.short_title) + expect(current_path).to eq admin_conference_program_reports_path(conference.short_title) + + visit admin_conference_registrations_path(conference.short_title) + expect(current_path).to eq admin_conference_registrations_path(conference.short_title) + + create(:registration, user: create(:user), conference: conference) + visit edit_admin_conference_registration_path(conference.short_title, conference.registrations.first) + expect(current_path).to eq root_path + + visit new_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq root_path + + create(:registration_period, conference: conference) + visit edit_admin_conference_registration_period_path(conference.short_title) + expect(current_path).to eq root_path + + visit admin_conference_questions_path(conference.short_title) + expect(current_path).to eq root_path + + visit admin_conference_sponsorship_levels_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_sponsorship_level_path(conference.short_title) + expect(current_path).to eq root_path + + create(:sponsorship_level, conference: conference) + visit edit_admin_conference_sponsorship_level_path(conference.short_title, conference.sponsorship_levels.first) + expect(current_path).to eq root_path + + visit admin_conference_sponsors_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_sponsor_path(conference.short_title) + expect(current_path).to eq root_path + + create(:sponsor, conference: conference, sponsorship_level: conference.sponsorship_levels.first) + visit edit_admin_conference_sponsor_path(conference.short_title, conference.sponsors.first) + expect(current_path).to eq root_path + + visit admin_conference_tickets_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_ticket_path(conference.short_title) + expect(current_path).to eq root_path + + create(:ticket, conference: conference) + visit edit_admin_conference_ticket_path(conference.short_title, conference.tickets.first) + expect(current_path).to eq root_path + + visit admin_conference_campaigns_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_campaign_path(conference.short_title) + expect(current_path).to eq root_path + + create(:campaign, conference: conference) + visit edit_admin_conference_campaign_path(conference.short_title, conference.campaigns.first) + expect(current_path).to eq root_path + + visit admin_conference_targets_path(conference.short_title) + expect(current_path).to eq root_path + + visit new_admin_conference_target_path(conference.short_title) + expect(current_path).to eq root_path + + create(:target, conference: conference) + visit edit_admin_conference_target_path(conference.short_title, conference.targets.first) + expect(current_path).to eq root_path + + visit admin_conference_program_tracks_path(conference.short_title) + expect(current_path).to eq admin_conference_program_tracks_path(conference.short_title) + + visit new_admin_conference_program_track_path(conference.short_title) + expect(current_path).to eq root_path + + other_track = create(:track, program: conference.program) + visit admin_conference_program_track_path(conference.short_title, other_track) + expect(current_path).to eq root_path + + visit edit_admin_conference_program_track_path(conference.short_title, other_track) + expect(current_path).to eq root_path + + visit admin_conference_program_track_path(conference.short_title, self_organized_track) + expect(current_path).to eq admin_conference_program_track_path(conference.short_title, self_organized_track) + + visit edit_admin_conference_program_track_path(conference.short_title, self_organized_track) + expect(current_path).to eq root_path + + visit admin_conference_roles_path(conference.short_title) + expect(current_path).to eq admin_conference_roles_path(conference.short_title) + + visit admin_conference_emails_path(conference.short_title) + expect(current_path).to eq root_path + + visit admin_conference_resources_path(conference.short_title) + expect(current_path).to eq admin_conference_resources_path(conference.short_title) + + visit new_admin_conference_resource_path(conference.short_title) + expect(current_path).to eq new_admin_conference_resource_path(conference.short_title) + + create(:resource, conference: conference) + visit edit_admin_conference_resource_path(conference.short_title, conference.resources.first) + expect(current_path).to eq root_path + + visit admin_revision_history_path + expect(current_path).to eq root_path + end + end +end diff --git a/spec/features/tracks_spec.rb b/spec/features/tracks_spec.rb index d1972075..f753abc2 100644 --- a/spec/features/tracks_spec.rb +++ b/spec/features/tracks_spec.rb @@ -4,25 +4,29 @@ feature Track do let!(:conference) { create(:conference) } let!(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) } let!(:organizer) { create(:user, role_ids: [organizer_role.id]) } + let(:user) { create(:user) } - shared_examples 'tracks' do + shared_examples 'admin tracks' do scenario 'adds a track', feature: true, js: true do sign_in organizer - visit admin_conference_program_tracks_path(conference_id: conference.short_title) - click_link 'New Track' + expected = expect do + visit admin_conference_program_tracks_path(conference_id: conference.short_title) + click_link 'New Track' - fill_in 'track_name', with: 'Distribution' - page.find('#track_color').set('#B94D4D') - fill_in 'track_description', with: 'Events about our Linux distribution' - click_button 'Create Track' + fill_in 'track_name', with: 'Distribution' + fill_in 'track_short_name', with: 'Distribution' + page.find('#track_color').set('#B94D4D') + fill_in 'track_description', with: 'Events about our Linux distribution' + click_button 'Create Track' + end + expected.to change { Track.count }.by 1 expect(flash).to eq('Track successfully created.') within('table#tracks') do expect(page.has_content?('Distribution')).to be true expect(page.has_content?('Events about our Linux')).to be true - expect(page.assert_selector('tr', count: 2)).to be true end end @@ -30,40 +34,126 @@ feature Track do track = create(:track, program_id: conference.program.id) sign_in organizer - visit admin_conference_program_tracks_path(conference_id: conference.short_title) - - click_link 'Delete' - - expect(flash).to eq('Track successfully deleted.') - within('table#tracks') do - expect(page.has_content?(track.name)).to be false - expect(page.has_content?(track.description)).to be false - expect(page.assert_selector('tr', count: 1)).to be true + expected = expect do + visit admin_conference_program_tracks_path(conference_id: conference.short_title) + within('#tracks', visible: true) do + page.accept_confirm do + find_link('Delete').click + end + end end + + expected.to change { Track.count }.by(-1) + expect(flash).to eq('Track successfully deleted.') + expect(page.has_css?('table#tracks')).to be false + expect(page.has_content?(track.name)).to be false + expect(page.has_content?(track.description)).to be false end scenario 'updates a track', feature: true, js: true do create(:track, program_id: conference.program.id) sign_in organizer - visit admin_conference_program_tracks_path(conference_id: conference.short_title) - click_link 'Edit' + expected = expect do + visit admin_conference_program_tracks_path(conference_id: conference.short_title) + within('#tracks', visible: true) do + find_link('Edit').trigger('click') + end - fill_in 'track_name', with: 'Distribution' - page.find('#track_color').set('#B94D4D') - fill_in 'track_description', with: 'Events about our Linux distribution' - click_button 'Update Track' + fill_in 'track_name', with: 'Distribution' + fill_in 'track_short_name', with: 'Distribution' + page.find('#track_color').set('#B94D4D') + fill_in 'track_description', with: 'Events about our Linux distribution' + click_button 'Update Track' + end + expected.to_not(change { Track.count }) expect(flash).to eq('Track successfully updated.') within('table#tracks') do expect(page.has_content?('Distribution')).to be true expect(page.has_content?('Events about our Linux')).to be true - expect(page.assert_selector('tr', count: 2)).to be true + end + end + end + + shared_examples 'non admin tracks' do + scenario 'adds a track', feature: true, js: true do + + sign_in user + + expected = expect do + visit conference_program_tracks_path(conference_id: conference.short_title) + click_link 'New Track request' + + fill_in 'track_name', with: 'Distribution' + fill_in 'track_short_name', with: 'Distribution' + page.find('#track_color').set('#B94D4D') + fill_in 'track_description', with: 'Events about our Linux distribution' + fill_in 'track_relevance', with: 'Maintainer of super awesome distribution' + click_button 'Create Track' + end + + expected.to change { Track.count }.by 1 + expect(flash).to eq('Track request successfully created.') + within('table#tracks') do + expect(page.has_content?('Distribution')).to eq true + expect(page.has_content?('Events about our Linux dist...')).to eq true + end + end + + scenario 'withdraws a track', feature: true, js: true do + track = create(:track, :self_organized, program_id: conference.program.id, submitter: user) + sign_in user + + expected = expect do + visit conference_program_tracks_path(conference_id: conference.short_title) + + accept_confirm do + click_link 'Withdraw' + end + end + + expected.to_not(change { Track.count }) + expect(flash).to eq("Track #{track.name} withdrawn.") + within('table#tracks') do + expect(page.has_content?(track.name)).to eq true + expect(page.has_link?('Re-Submit')).to eq true + end + end + + scenario 'updates a track', feature: true, js: true do + create(:track, :self_organized, program_id: conference.program.id, submitter: user) + sign_in user + + expected = expect do + visit conference_program_tracks_path(conference_id: conference.short_title) + click_link 'Edit' + + fill_in 'track_name', with: 'Distribution' + fill_in 'track_short_name', with: 'Distribution' + page.find('#track_color').set('#B94D4D') + fill_in 'track_description', with: 'Events about our Linux distribution' + click_button 'Update Track' + end + + expected.to_not(change { Track.count }) + expect(flash).to eq('Track request successfully updated.') + within('table#tracks') do + expect(page.has_content?('Distribution')).to eq true + expect(page.has_content?('Events about our Linux dist...')).to eq true end end end describe 'organizer' do - it_behaves_like 'tracks' + it_behaves_like 'admin tracks' + end + + describe 'signed in user' do + before :each do + create(:cfp, cfp_type: 'tracks', program: conference.program) + end + + it_behaves_like 'non admin tracks' end end diff --git a/spec/features/user_ability_spec.rb b/spec/features/user_ability_spec.rb new file mode 100644 index 00000000..d56e7441 --- /dev/null +++ b/spec/features/user_ability_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +feature 'Has correct abilities' do + + let(:organization) { create(:organization) } + let(:conference) { create(:full_conference, organization: organization) } # user is cfp + let(:user) { create(:user) } + + context 'when user has no role' do + before do + sign_in user + end + + scenario 'for administration views' do + visit admin_conference_path(conference.short_title) + + expect(current_path).to eq root_path + expect(flash).to eq 'You are not authorized to access this page.' + end + end +end diff --git a/spec/features/versions_spec.rb b/spec/features/versions_spec.rb index ff73eee0..1bf114a0 100644 --- a/spec/features/versions_spec.rb +++ b/spec/features/versions_spec.rb @@ -35,23 +35,25 @@ feature 'Version' do scenario 'display changes in cfp', feature: true, versioning: true, js: true do cfp = create(:cfp, program: conference.program) cfp.update_attributes(start_date: (Date.today + 1).strftime('%d/%m/%Y'), end_date: (Date.today + 3).strftime('%d/%m/%Y')) + cfp_id = cfp.id cfp.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new cfp in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated start date and end date of cfp in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted cfp in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new cfp for events with ID #{cfp_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated start date and end date of cfp for events with ID #{cfp_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted cfp for events with ID #{cfp_id} in conference #{conference.short_title}") end scenario 'display changes in registration_period', feature: true, versioning: true, js: true do registration_period = create(:registration_period, conference: conference) registration_period.update_attributes(start_date: (Date.today + 1).strftime('%d/%m/%Y'), end_date: (Date.today + 3).strftime('%d/%m/%Y')) + registration_period_id = registration_period.id registration_period.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new registration period in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated start date and end date of registration period in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted registration period in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new registration period with ID #{registration_period_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated start date and end date of registration period with ID #{registration_period_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted registration period with ID #{registration_period_id} in conference #{conference.short_title}") end scenario 'display changes in conference', feature: true, versioning: true, js: true do @@ -61,35 +63,37 @@ feature 'Version' do visit admin_revision_history_path select '100', from: 'versionstable_length' - expect(page).to have_text('Someone (probably via the console) created new conference New Con') + expect(page).to have_text('Someone (probably via the console) created new conference NewCon') expect(page).to have_text('Someone (probably via the console) created new event type Talk in conference NewCon') expect(page).to have_text('Someone (probably via the console) created new event type Workshop in conference NewCon') - expect(page).to have_text('Someone (probably via the console) updated title and short title of conference New Con') + expect(page).to have_text('Someone (probably via the console) updated title and short title of conference NewCon') end scenario 'display changes in event_type', feature: true, versioning: true, js: true do event_type = create(:event_type, program: conference.program, name: 'Discussion') event_type.update_attributes(length: 90, maximum_abstract_length: 10000) + event_type_id = event_type.id event_type.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new event type Discussion in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated length and maximum abstract length of event type Discussion in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted event type Discussion in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new event type Discussion with ID #{event_type_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated length and maximum abstract length of event type Discussion with ID #{event_type_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted event type Discussion with ID #{event_type_id} in conference #{conference.short_title}") end scenario 'display changes in lodging', feature: true, versioning: true, js: true do lodging = create(:lodging, conference: conference, name: 'Hotel XYZ') lodging.update_attributes(description: 'Nice view,close to venue', website_link: 'http://www.example.com') + lodging_id = lodging.id lodging.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new lodging Hotel XYZ in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated description and website link of lodging Hotel XYZ in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted lodging Hotel XYZ in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new lodging Hotel XYZ with ID #{lodging_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated description and website link of lodging Hotel XYZ with ID #{lodging_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted lodging Hotel XYZ with ID #{lodging_id} in conference #{conference.short_title}") end - scenario 'display changes in role', feature: true, versioning: true, js: true do + scenario 'display changes in conference role', feature: true, versioning: true, js: true do visit edit_admin_conference_role_path(conference.short_title, 'cfp') fill_in 'role_description', with: 'For the members of the call for papers team' click_button 'Update Role' @@ -102,12 +106,13 @@ feature 'Version' do venue = create(:venue, conference: conference) room = create(:room, venue: venue, name: 'Auditorium') room.update_attributes(size: 120) + room_id = room.id room.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new room Auditorium in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated size of room Auditorium in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted room Auditorium in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new room Auditorium with ID #{room_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated size of room Auditorium with ID #{room_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted room Auditorium with ID #{room_id} in conference #{conference.short_title}") end scenario 'display changes in sponsor', feature: true, versioning: true, js: true do @@ -115,55 +120,60 @@ feature 'Version' do sponsor = create(:sponsor, conference: conference, name: 'SUSE', sponsorship_level: conference.sponsorship_levels.first) sponsor.update_attributes(website_url: 'https://www.suse.com/company/history', sponsorship_level: conference.sponsorship_levels.second) sponsor.destroy + sponsor_id = sponsor.id visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new sponsor SUSE in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated website url and sponsorship level of sponsor SUSE in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted sponsor SUSE in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new sponsor SUSE with ID #{sponsor_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated website url and sponsorship level of sponsor SUSE with ID #{sponsor_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted sponsor SUSE with ID #{sponsor_id} in conference #{conference.short_title}") end scenario 'display changes in sponsorship_level', feature: true, versioning: true, js: true do sponsorship_level = create(:sponsorship_level, conference: conference) sponsorship_level.update_attributes(title: 'Gold') + sponsorship_level_id = sponsorship_level.id sponsorship_level.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new sponsorship level Gold in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated title of sponsorship level Gold in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted sponsorship level Gold in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new sponsorship level Gold with ID #{sponsorship_level_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated title of sponsorship level Gold with ID #{sponsorship_level_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted sponsorship level Gold with ID #{sponsorship_level_id} in conference #{conference.short_title}") end scenario 'display changes in ticket', feature: true, versioning: true, js: true do ticket = create(:ticket, conference: conference, title: 'Gold') ticket.update_attributes(price: 50, description: 'Premium Ticket') + ticket_id = ticket.id ticket.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new ticket Gold in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated price cents and description of ticket Gold in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted ticket Gold in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new ticket Gold with ID #{ticket_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated price cents and description of ticket Gold with ID #{ticket_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted ticket Gold with ID #{ticket_id} in conference #{conference.short_title}") end scenario 'display changes in track', feature: true, versioning: true, js: true do track = create(:track, program: conference.program, name: 'Distribution') track.update_attributes(description: 'Events about Linux distributions') + track_id = track.id track.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new track Distribution in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated description of track Distribution in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted track Distribution in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new track Distribution with ID #{track_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated description of track Distribution with ID #{track_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted track Distribution with ID #{track_id} in conference #{conference.short_title}") end scenario 'display changes in venue', feature: true, versioning: true, js: true do venue = create(:venue, conference: conference, name: 'Example University') venue.update_attributes(website: 'www.example.com new', description: 'Just another beautiful venue') + venue_id = venue.id venue.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new venue Example University in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated website and description of venue Example University in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted venue Example University in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new venue Example University with ID #{venue_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated website and description of venue Example University with ID #{venue_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted venue Example University with ID #{venue_id} in conference #{conference.short_title}") end scenario 'display changes in event', feature: true, versioning: true, js: true do @@ -183,7 +193,9 @@ feature 'Version' do click_link 'Reject event' visit conference_program_proposals_path(conference_id: conference.short_title) - click_link 'Re-Submit' + within('#events') do + click_link 'Re-Submit' + end visit admin_conference_program_events_path(conference.short_title) click_button 'New' @@ -209,12 +221,13 @@ feature 'Version' do scenario 'display changes in difficulty levels', feature: true, versioning: true, js: true do difficulty_level = create(:difficulty_level, program: conference.program, title: 'Expert') difficulty_level.update_attributes(description: 'Only for Experts') + difficulty_level_id = difficulty_level.id difficulty_level.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new difficulty level Expert in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated description of difficulty level Expert in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted difficulty level Expert in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new difficulty level Expert with ID #{difficulty_level_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated description of difficulty level Expert with ID #{difficulty_level_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted difficulty level Expert with ID #{difficulty_level_id} in conference #{conference.short_title}") end scenario 'display changes in splashpages', feature: true, versioning: true, js: true do @@ -232,13 +245,14 @@ feature 'Version' do uncheck('Display social media') check('Make splash page public?') click_button 'Save Splashpage' + splashpage_id = conference.splashpage.id click_link 'Delete' visit admin_revision_history_path - expect(page).to have_text("#{organizer.name} created new splashpage in conference #{conference.short_title}") + expect(page).to have_text("#{organizer.name} created new splashpage with ID #{splashpage_id} in conference #{conference.short_title}") expect(page).to have_text("#{organizer.name} updated public, include program, include cfp, include venue, include tickets, include lodgings, - include sponsors and include social media of splashpage in conference #{conference.short_title}") - expect(page).to have_text("#{organizer.name} deleted splashpage in conference #{conference.short_title}") + include sponsors and include social media of splashpage with ID #{splashpage_id} in conference #{conference.short_title}") + expect(page).to have_text("#{organizer.name} deleted splashpage with ID #{splashpage_id} in conference #{conference.short_title}") end scenario 'displays users subscribe/unsubscribe to conferences', feature: true, versioning: true, js: true do @@ -249,10 +263,10 @@ feature 'Version' do PaperTrail::Version.last.item.destroy! visit admin_revision_history_path - expect(page).to have_text("#{organizer.name} subscribed to conference #{conference.title}") - expect(page).to have_text("#{organizer.name} unsubscribed from conference #{conference.title}") - expect(page).to have_text("Someone (probably via the console) subscribed #{organizer.name} to conference #{conference.title}") - expect(page).to have_text("Someone (probably via the console) unsubscribed #{organizer.name} from conference #{conference.title}") + expect(page).to have_text("#{organizer.name} subscribed to conference #{conference.short_title}") + expect(page).to have_text("#{organizer.name} unsubscribed from conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) subscribed #{organizer.name} to conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) unsubscribed #{organizer.name} from conference #{conference.short_title}") end scenario 'display changes in conference commercials', feature: true, versioning: true, js: true do @@ -286,19 +300,43 @@ feature 'Version' do expect(page).to have_text('Someone (probably via the console) created new commercial') visit admin_conference_program_event_path(conference.short_title, event_without_commercial) click_link 'History' - expect(event_commercial.id).not_to eq event_commercial.commercialable_id - expect(event_without_commercial.id).to eq event_commercial.id expect(page).to have_no_text('Someone (probably via the console) created new commercial') end - scenario 'display changes in users_role', feature: true, versioning: true, js: true do + scenario 'display changes in organization', feature: true, versioning: true, js: true do + admin = create(:admin) + sign_in admin + + visit new_admin_organization_path + fill_in 'organization_name', with: 'New org' + click_button 'Create Organization' + + visit admin_revision_history_path + expect(page).to have_text('created new organization New org') + end + + scenario 'display changes in users_role for organization role', feature: true, versioning: true, js: true do user = create(:user) + role = Role.find_by(resource_id: conference.organization.id, resource_type: 'Organization') + user.add_role :organization_admin, conference.organization + user_role = UsersRole.find_by(user_id: user.id, role_id: role.id) + user.remove_role :organization_admin, conference.organization + + visit admin_revision_history_path + expect(page).to have_text("added role organization_admin with ID #{user_role.id} to user #{user.name} in organization #{conference.organization.name}") + expect(page).to have_text("removed role organization_admin with ID #{user_role.id} from user #{user.name} in organization #{conference.organization.name}") + end + + scenario 'display changes in users_role for conference role', feature: true, versioning: true, js: true do + user = create(:user) + role = Role.find_by(name: 'cfp', resource_id: conference.id, resource_type: 'Conference') user.add_role :cfp, conference + user_role = UsersRole.find_by(user_id: user.id, role_id: role.id) user.remove_role :cfp, conference visit admin_revision_history_path - expect(page).to have_text("added role cfp to user #{user.name} in conference #{conference.short_title}") - expect(page).to have_text("removed role cfp from user #{user.name} in conference #{conference.short_title}") + expect(page).to have_text("added role cfp with ID #{user_role.id} to user #{user.name} in conference #{conference.short_title}") + expect(page).to have_text("removed role cfp with ID #{user_role.id} from user #{user.name} in conference #{conference.short_title}") end scenario 'display changes in email settings', feature: true, versioning: true, js: true do @@ -314,8 +352,8 @@ feature 'Version' do Registration.last.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) registered #{organizer.name} to conference #{conference.title}") - expect(page).to have_text("Someone (probably via the console) unregistered #{organizer.name} from conference #{conference.title}") + expect(page).to have_text("Someone (probably via the console) registered #{organizer.name} to conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) unregistered #{organizer.name} from conference #{conference.short_title}") end scenario 'display changes in event registration', feature: true, versioning: true, js: true do @@ -337,12 +375,13 @@ feature 'Version' do scenario 'display changes in target', feature: true, versioning: true, js: true do target = create(:target, conference: conference) target.update_attributes(due_date: Date.today, target_count: 1000) + target_id = target.id target.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new target 1000 Submissions by #{Date.today} in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated due date and target count of target 1000 Submissions by #{Date.today} in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted target 1000 Submissions by #{Date.today} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new target 1000 Submissions by #{Date.today} with ID #{target_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated due date and target count of target 1000 Submissions by #{Date.today} with ID #{target_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted target 1000 Submissions by #{Date.today} with ID #{target_id} in conference #{conference.short_title}") end scenario 'display changes in comment', feature: true, versioning: true, js: true do @@ -378,12 +417,13 @@ feature 'Version' do scenario 'display changes in campaign', feature: true, versioning: true, js: true do campaign = create(:campaign, conference: conference, name: 'Test Campaign', utm_campaign: 'campaign') campaign.update_attributes(utm_source: 'source', utm_medium: 'medium', utm_term: 'term', utm_content: 'content') + campaign_id = campaign.id campaign.destroy visit admin_revision_history_path - expect(page).to have_text("Someone (probably via the console) created new campaign Test Campaign in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) updated utm source, utm medium, utm term and utm content of campaign Test Campaign in conference #{conference.short_title}") - expect(page).to have_text("Someone (probably via the console) deleted campaign Test Campaign in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) created new campaign Test Campaign with ID #{campaign_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) updated utm source, utm medium, utm term and utm content of campaign Test Campaign with ID #{campaign_id} in conference #{conference.short_title}") + expect(page).to have_text("Someone (probably via the console) deleted campaign Test Campaign with ID #{campaign_id} in conference #{conference.short_title}") end scenario 'display password reset requests', feature: true, versioning: true, js: true do diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb index dfc337e6..e0c2d3fd 100644 --- a/spec/helpers/users_helper_spec.rb +++ b/spec/helpers/users_helper_spec.rb @@ -4,7 +4,7 @@ describe UsersHelper, type: :helper do describe 'show_roles' do it 'formats the hash passed' do - roles = { 'organizer' => ['oSC16', 'oSC15'], 'cfp' => ['oSC16'] } + roles = { 'organizer' => %w[oSC16 oSC15], 'cfp' => ['oSC16'] } expect(show_roles(roles)).to eq 'Organizer (oSC16, oSC15), Cfp (oSC16)' end end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 6ddc44e6..7936ddbb 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -9,52 +9,41 @@ describe 'User' do subject(:ability){ Ability.new(user) } let(:user){ nil } - let!(:my_conference) { create(:full_conference) } - let!(:my_cfp) { create(:cfp, program: my_conference.program) } - let(:my_venue) { my_conference.venue || create(:venue, conference: my_conference) } - let(:my_registration) { create(:registration, conference: my_conference, user: admin) } + let!(:organization) { create(:organization) } + let!(:my_conference) { create(:full_conference, organization: organization) } - let(:other_registration) { create(:registration, conference: conference_public) } - let(:my_event) { create(:event_full, program: my_conference.program) } let(:my_room) { create(:room, venue: my_conference.venue) } - let!(:my_event_scheduled) { create(:event_full, program: my_conference.program, room_id: my_room.id) } - let(:other_event) { create(:event_full, program: conference_public.program) } let(:conference_not_public) { create(:conference, splashpage: create(:splashpage, public: false)) } let(:conference_public) { create(:full_conference, splashpage: create(:splashpage, public: true)) } - let!(:conference_public_cfp) { create(:cfp, program: conference_public.program) } let(:event_confirmed) { create(:event, state: 'confirmed') } let(:event_unconfirmed) { create(:event) } let(:commercial_event_confirmed) { create(:commercial, commercialable: event_confirmed) } let(:commercial_event_unconfirmed) { create(:commercial, commercialable: event_unconfirmed) } - let(:resource) { create(:resource, conference: my_conference)} let(:registration) { create(:registration) } - let(:program_with_cfp) { create(:program, cfp: create(:cfp)) } + let(:program_with_cfp) { create(:program, :with_cfp) } let(:program_without_cfp) { create(:program) } + let(:program_with_call_for_tracks) { create(:cfp, cfp_type: 'tracks').program } let(:conference_with_open_registration) { create(:conference) } let!(:open_registration_period) { create(:registration_period, conference: conference_with_open_registration, start_date: Date.current - 6.days) } let(:conference_with_closed_registration) { create(:conference) } let!(:closed_registration_period) { create(:registration_period, conference: conference_with_closed_registration, start_date: Date.current - 6.days, end_date: Date.current - 6.days) } - let!(:my_schedule) { create(:schedule, program: my_conference.program) } - let!(:other_schedule) { create(:schedule, program: conference_public.program) } - - let!(:my_event_schedule) { create(:event_schedule, schedule: my_schedule) } - let!(:other_event_schedule) { create(:event_schedule, schedule: other_schedule) } # Test abilities for not signed in users context 'when user is not signed in' do + it{ should be_able_to(:index, Organization)} it{ should be_able_to(:index, Conference)} it{ should be_able_to(:show, conference_public)} it{ should_not be_able_to(:show, conference_not_public)} it do - conference_public.program.schedule_public = true - conference_public.program.save - should be_able_to(:schedule, conference_public) + conference_public.program.schedule_public = true + conference_public.program.save + should be_able_to(:schedule, conference_public) end it{ should_not be_able_to(:schedule, conference_not_public)} @@ -94,6 +83,10 @@ describe 'User' do let(:user_event_with_cfp) { create(:event, users: [user], program: program_with_cfp) } let(:user_commercial) { create(:commercial, commercialable: user_event_with_cfp) } + let(:user_self_organized_track) { create(:track, :self_organized, submitter: user) } + let(:accepted_user_self_organized_track) { create(:track, :self_organized, submitter: user, state: 'accepted') } + let(:confirmed_user_self_organized_track) { create(:track, :self_organized, submitter: user, state: 'confirmed') } + let(:other_self_organized_track) { create(:track, :self_organized) } it{ should be_able_to(:manage, user) } @@ -113,7 +106,6 @@ describe 'User' do it{ should be_able_to(:update, user_event_with_cfp) } it{ should be_able_to(:show, user_event_with_cfp) } - it{ should be_able_to(:delete, user_event_with_cfp) } it{ should_not be_able_to(:new, Event.new(program: program_without_cfp)) } it{ should_not be_able_to(:create, Event.new(program: program_without_cfp)) } # TODO: At moment it's not possible to manually add someone else as event_user @@ -126,330 +118,31 @@ describe 'User' do it{ should be_able_to(:create, user_event_with_cfp.commercials.new) } it{ should be_able_to(:manage, user_commercial) } it{ should_not be_able_to(:manage, commercial_event_unconfirmed) } - end - context 'user #is_admin?' do - let(:venue) { my_conference.venue } - let(:room) { create(:room, venue: venue) } - let!(:event) { create(:event_full, program: my_conference.program, room_id: room.id) } - let(:user) { create(:admin) } - it{ should be_able_to(:manage, :all) } - it{ should_not be_able_to(:destroy, my_conference.program) } - it{ should_not be_able_to(:destroy, my_venue) } - end + it{ should be_able_to(:new, Track.new(program: program_with_call_for_tracks)) } + it{ should be_able_to(:create, Track.new(program: program_with_call_for_tracks)) } + it{ should_not be_able_to(:new, Track.new(program: program_without_cfp)) } + it{ should_not be_able_to(:create, Track.new(program: program_without_cfp)) } - shared_examples 'user with any role' do - before do - @other_conference = create(:conference) - end + it{ should be_able_to(:index, user_self_organized_track) } + it{ should be_able_to(:show, user_self_organized_track) } + it{ should be_able_to(:restart, user_self_organized_track) } + it{ should be_able_to(:confirm, user_self_organized_track) } + it{ should be_able_to(:withdraw, user_self_organized_track) } + it{ should_not be_able_to(:index, other_self_organized_track) } + it{ should_not be_able_to(:show, other_self_organized_track) } + it{ should_not be_able_to(:restart, other_self_organized_track) } + it{ should_not be_able_to(:confirm, other_self_organized_track) } + it{ should_not be_able_to(:withdraw, other_self_organized_track) } - %w(organizer cfp info_desk volunteers_coordinator).each do |role| - it{ should_not be_able_to(:toggle_user, Role.find_by(name: role, resource: @other_conference)) } - it{ should_not be_able_to(:update, Role.find_by(name: role, resource: @other_conference)) } - it{ should_not be_able_to(:edit, Role.find_by(name: role, resource: @other_conference)) } - it{ should be_able_to(:show, Role.find_by(name: role, resource: @other_conference)) } - it{ should be_able_to(:index, Role.find_by(name: role, resource: @other_conference)) } - end - end - - shared_examples 'user with non-organizer role' do |role_name| - %w(organizer cfp info_desk volunteers_coordinator).each do |role| - if role == role_name - it{ should be_able_to(:toggle_user, Role.find_by(name: role, resource: my_conference)) } - else - it{ should_not be_able_to(:toggle_user, Role.find_by(name: role, resource: my_conference)) } - end - it{ should_not be_able_to(:update, Role.find_by(name: role, resource: my_conference)) } - it{ should_not be_able_to(:edit, Role.find_by(name: role, resource: my_conference)) } - it{ should be_able_to(:show, Role.find_by(name: role, resource: my_conference)) } - it{ should be_able_to(:index, Role.find_by(name: role, resource: my_conference)) } - end - end - - context 'when user has the role organizer' do - let(:role) { Role.find_by(name: 'organizer', resource: my_conference) } - let(:user) { create(:user, role_ids: [role.id]) } - - it{ should_not be_able_to(:destroy, my_conference.program) } - it 'when there is a room assigned to an event' do - should_not be_able_to(:destroy, my_venue) - end - - it 'when there are no rooms used' do - my_event_scheduled.room_id = nil - my_event_scheduled.save! - my_event_scheduled.reload - should be_able_to(:destroy, my_venue) - end - - it{ should be_able_to(:new, Conference) } - it{ should be_able_to(:create, Conference) } - it{ should be_able_to(:manage, my_conference) } - it{ should_not be_able_to(:manage, conference_public) } - it{ should be_able_to(:manage, my_conference.splashpage) } - it{ should_not be_able_to(:manage, conference_public.splashpage) } - it{ should be_able_to(:manage, my_conference.contact) } - it{ should_not be_able_to(:manage, conference_public.contact) } - it{ should be_able_to(:manage, my_conference.email_settings) } - it{ should_not be_able_to(:manage, conference_public.email_settings) } - it{ should be_able_to(:manage, my_conference.campaigns.first) } - it{ should_not be_able_to(:manage, conference_public.campaigns.first) } - it{ should be_able_to(:manage, my_conference.targets.first) } - it{ should_not be_able_to(:manage, conference_public.targets.first) } - it{ should be_able_to(:manage, my_conference.commercials.first) } - it{ should_not be_able_to(:manage, conference_public.commercials.first) } - it{ should be_able_to(:manage, my_conference.registration_period) } - it{ should_not be_able_to(:manage, conference_public.registration_period) } - it{ should be_able_to(:manage, my_conference.questions.first) } - it{ should_not be_able_to(:manage, conference_public.questions.first) } - it{ should be_able_to(:manage, my_conference.program.cfp) } - it{ should_not be_able_to(:manage, conference_public.program.cfp) } - it{ should be_able_to(:manage, my_schedule) } - it{ should_not be_able_to(:manage, other_schedule) } - it{ should be_able_to(:manage, my_event_schedule) } - it{ should_not be_able_to(:manage, other_event_schedule) } - it{ should be_able_to(:manage, my_conference.venue) } - it{ should_not be_able_to(:manage, conference_public.venue) } - it{ should be_able_to(:manage, my_conference.lodgings.first) } - it{ should_not be_able_to(:manage, conference_public.lodgings.first) } - it{ should be_able_to(:manage, my_conference.sponsors.first) } - it{ should_not be_able_to(:manage, conference_public.sponsors.first) } - it{ should be_able_to(:manage, my_conference.sponsorship_levels.first) } - it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } - it{ should be_able_to(:manage, my_conference.tickets.first) } - it{ should_not be_able_to(:manage, conference_public.tickets.first) } - - it{ should be_able_to(:manage, my_registration) } - it{ should_not be_able_to(:manage, other_registration) } - - it{ should be_able_to(:manage, my_event) } - it{ should_not be_able_to(:manage, other_event) } - it{ should be_able_to(:manage, my_event.event_type) } - it{ should_not be_able_to(:manage, other_event.event_type) } - it{ should be_able_to(:manage, my_event.track) } - it{ should_not be_able_to(:manage, other_event.track) } - it{ should be_able_to(:manage, my_event.difficulty_level) } - it{ should_not be_able_to(:manage, other_event.difficulty_level) } - it{ should be_able_to(:manage, my_event.commercials.first) } - it{ should_not be_able_to(:manage, other_event.commercials.first) } - it{ should be_able_to(:index, my_event.comment_threads.first) } - it{ should_not be_able_to(:index, other_event.comment_threads.first) } - - it{ should be_able_to(:manage, resource)} - - %w(organizer cfp info_desk volunteers_coordinator).each do |role| - it{ should be_able_to(:toggle_user, Role.find_by(name: role, resource: my_conference)) } - it{ should be_able_to(:edit, Role.find_by(name: role, resource: my_conference)) } - it{ should be_able_to(:update, Role.find_by(name: role, resource: my_conference)) } - it{ should be_able_to(:show, Role.find_by(name: role, resource: my_conference)) } - it{ should be_able_to(:index, Role.find_by(name: role, resource: my_conference)) } - end - - it_behaves_like 'user with any role' - end - - context 'when user has the role cfp' do - let(:role) { Role.find_by(name: 'cfp', resource: my_conference) } - let(:user) { create(:user, role_ids: [role.id]) } - - it{ should_not be_able_to(:new, Conference.new) } - it{ should_not be_able_to(:create, Conference.new) } - it{ should_not be_able_to(:manage, my_conference) } - it{ should_not be_able_to(:manage, conference_public) } - it{ should_not be_able_to(:manage, my_conference.splashpage) } - it{ should_not be_able_to(:manage, conference_public.splashpage) } - it{ should_not be_able_to(:manage, my_conference.contact) } - it{ should_not be_able_to(:manage, conference_public.contact) } - it{ should be_able_to(:manage, my_conference.email_settings) } - it{ should_not be_able_to(:manage, conference_public.email_settings) } - it{ should_not be_able_to(:manage, my_conference.campaigns.first) } - it{ should_not be_able_to(:manage, conference_public.campaigns.first) } - it{ should_not be_able_to(:manage, my_conference.targets.first) } - it{ should_not be_able_to(:manage, conference_public.targets.first) } - it{ should_not be_able_to(:manage, my_conference.commercials.first) } - it{ should_not be_able_to(:manage, conference_public.commercials.first) } - it{ should_not be_able_to(:manage, my_conference.registration_period) } - it{ should_not be_able_to(:manage, conference_public.registration_period) } - it{ should_not be_able_to(:manage, my_conference.questions.first) } - it{ should_not be_able_to(:manage, conference_public.questions.first) } - it{ should be_able_to(:manage, my_conference.program.cfp) } - it{ should_not be_able_to(:manage, conference_public.program.cfp) } - it{ should be_able_to(:manage, my_schedule) } - it{ should_not be_able_to(:manage, other_schedule) } - it{ should_not be_able_to(:manage, my_event_schedule) } - it{ should_not be_able_to(:manage, other_event_schedule) } - it{ should_not be_able_to(:manage, my_conference.venue) } - it{ should be_able_to(:show, my_conference.venue) } - it{ should_not be_able_to(:manage, conference_public.venue) } - it{ should_not be_able_to(:manage, my_conference.lodgings.first) } - it{ should_not be_able_to(:manage, conference_public.lodgings.first) } - it{ should_not be_able_to(:manage, my_conference.sponsors.first) } - it{ should_not be_able_to(:manage, conference_public.sponsors.first) } - it{ should_not be_able_to(:manage, my_conference.sponsorship_levels.first) } - it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } - it{ should_not be_able_to(:manage, my_conference.tickets.first) } - it{ should_not be_able_to(:manage, conference_public.tickets.first) } - - it{ should_not be_able_to(:manage, my_registration) } - it{ should_not be_able_to(:manage, other_registration) } - - it{ should be_able_to(:manage, my_event) } - it{ should_not be_able_to(:manage, other_event) } - it{ should be_able_to(:manage, my_event.event_type) } - it{ should_not be_able_to(:manage, other_event.event_type) } - it{ should be_able_to(:manage, my_event.track) } - it{ should_not be_able_to(:manage, other_event.track) } - it{ should be_able_to(:manage, my_event.difficulty_level) } - it{ should_not be_able_to(:manage, other_event.difficulty_level) } - it{ should be_able_to(:manage, my_event.commercials.first) } - it{ should_not be_able_to(:manage, other_event.commercials.first) } - it{ should be_able_to(:index, my_event.comment_threads.first) } - it{ should_not be_able_to(:index, other_event.comment_threads.first) } - - it{ should_not be_able_to(:manage, resource)} - it{ should be_able_to(:index, resource)} - it{ should be_able_to(:show, resource)} - it{ should be_able_to(:update, resource)} - - it_behaves_like 'user with any role' - it_behaves_like 'user with non-organizer role', 'cfp' - end - - context 'when user has the role info_desk' do - let(:role) { Role.find_by(name: 'info_desk', resource: my_conference) } - let(:user) { create(:user, role_ids: [role.id]) } - - it{ should_not be_able_to(:new, Conference.new) } - it{ should_not be_able_to(:create, Conference.new) } - it{ should_not be_able_to(:manage, my_conference) } - it{ should_not be_able_to(:manage, conference_public) } - it{ should_not be_able_to(:manage, my_conference.splashpage) } - it{ should_not be_able_to(:manage, conference_public.splashpage) } - it{ should_not be_able_to(:manage, my_conference.contact) } - it{ should_not be_able_to(:manage, conference_public.contact) } - it{ should_not be_able_to(:manage, my_conference.email_settings) } - it{ should_not be_able_to(:manage, conference_public.email_settings) } - it{ should_not be_able_to(:manage, my_conference.campaigns.first) } - it{ should_not be_able_to(:manage, conference_public.campaigns.first) } - it{ should_not be_able_to(:manage, my_conference.targets.first) } - it{ should_not be_able_to(:manage, conference_public.targets.first) } - it{ should_not be_able_to(:manage, my_conference.commercials.first) } - it{ should_not be_able_to(:manage, conference_public.commercials.first) } - it{ should_not be_able_to(:manage, my_conference.registration_period) } - it{ should_not be_able_to(:manage, conference_public.registration_period) } - it{ should be_able_to(:manage, my_conference.questions.first) } - it{ should_not be_able_to(:manage, conference_public.questions.first) } - it{ should_not be_able_to(:manage, my_conference.program.cfp) } - it{ should_not be_able_to(:manage, conference_public.program.cfp) } - it{ should_not be_able_to(:manage, my_schedule) } - it{ should_not be_able_to(:manage, other_schedule) } - it{ should_not be_able_to(:manage, my_event_schedule) } - it{ should_not be_able_to(:manage, other_event_schedule) } - it{ should_not be_able_to(:manage, my_conference.venue) } - it{ should_not be_able_to(:show, my_conference.venue) } - it{ should_not be_able_to(:manage, conference_public.venue) } - it{ should_not be_able_to(:manage, my_conference.lodgings.first) } - it{ should_not be_able_to(:manage, conference_public.lodgings.first) } - it{ should_not be_able_to(:manage, my_conference.sponsors.first) } - it{ should_not be_able_to(:manage, conference_public.sponsors.first) } - it{ should_not be_able_to(:manage, my_conference.sponsorship_levels.first) } - it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } - it{ should_not be_able_to(:manage, my_conference.tickets.first) } - it{ should_not be_able_to(:manage, conference_public.tickets.first) } - - it{ should be_able_to(:manage, my_registration) } - it{ should_not be_able_to(:manage, other_registration) } - - it{ should_not be_able_to(:manage, my_event) } - it{ should_not be_able_to(:manage, other_event) } - it{ should_not be_able_to(:manage, my_event.event_type) } - it{ should_not be_able_to(:manage, other_event.event_type) } - it{ should_not be_able_to(:manage, my_event.track) } - it{ should_not be_able_to(:manage, other_event.track) } - it{ should_not be_able_to(:manage, my_event.difficulty_level) } - it{ should_not be_able_to(:manage, other_event.difficulty_level) } - it{ should_not be_able_to(:manage, my_event.commercials.first) } - it{ should_not be_able_to(:manage, other_event.commercials.first) } - it{ should_not be_able_to(:index, my_event.comment_threads.first) } - it{ should_not be_able_to(:index, other_event.comment_threads.first) } - - it{ should_not be_able_to(:manage, resource)} - it{ should be_able_to(:index, resource)} - it{ should be_able_to(:show, resource)} - it{ should be_able_to(:update, resource)} - - it_behaves_like 'user with any role' - it_behaves_like 'user with non-organizer role', 'info_desk' - end - - context 'when user has the role volunteers_coordinator' do - let(:role) { Role.find_by(name: 'volunteers_coordinator', resource: my_conference) } - let(:user) { create(:user, role_ids: [role.id]) } - - it{ should_not be_able_to(:new, Conference.new) } - it{ should_not be_able_to(:create, Conference.new) } - it{ should_not be_able_to(:manage, my_conference) } - it{ should_not be_able_to(:manage, conference_public) } - it{ should_not be_able_to(:manage, my_conference.splashpage) } - it{ should_not be_able_to(:manage, conference_public.splashpage) } - it{ should_not be_able_to(:manage, my_conference.contact) } - it{ should_not be_able_to(:manage, conference_public.contact) } - it{ should_not be_able_to(:manage, my_conference.email_settings) } - it{ should_not be_able_to(:manage, conference_public.email_settings) } - it{ should_not be_able_to(:manage, my_conference.campaigns.first) } - it{ should_not be_able_to(:manage, conference_public.campaigns.first) } - it{ should_not be_able_to(:manage, my_conference.targets.first) } - it{ should_not be_able_to(:manage, conference_public.targets.first) } - it{ should_not be_able_to(:manage, my_conference.commercials.first) } - it{ should_not be_able_to(:manage, conference_public.commercials.first) } - it{ should_not be_able_to(:manage, my_conference.registration_period) } - it{ should_not be_able_to(:manage, conference_public.registration_period) } - it{ should_not be_able_to(:manage, my_conference.questions.first) } - it{ should_not be_able_to(:manage, conference_public.questions.first) } - it{ should_not be_able_to(:manage, my_conference.program.cfp) } - it{ should_not be_able_to(:manage, conference_public.program.cfp) } - it{ should_not be_able_to(:manage, my_schedule) } - it{ should_not be_able_to(:manage, other_schedule) } - it{ should_not be_able_to(:manage, my_event_schedule) } - it{ should_not be_able_to(:manage, other_event_schedule) } - it{ should_not be_able_to(:manage, my_conference.venue) } - it{ should_not be_able_to(:show, my_conference.venue) } - it{ should_not be_able_to(:manage, conference_public.venue) } - it{ should_not be_able_to(:manage, my_conference.lodgings.first) } - it{ should_not be_able_to(:manage, conference_public.lodgings.first) } - it{ should_not be_able_to(:manage, my_conference.sponsors.first) } - it{ should_not be_able_to(:manage, conference_public.sponsors.first) } - it{ should_not be_able_to(:manage, my_conference.sponsorship_levels.first) } - it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } - it{ should_not be_able_to(:manage, my_conference.tickets.first) } - it{ should_not be_able_to(:manage, conference_public.tickets.first) } - - it{ should_not be_able_to(:manage, registration) } - it{ should_not be_able_to(:manage, other_registration) } - - it{ should_not be_able_to(:manage, my_event) } - it{ should_not be_able_to(:manage, other_event) } - it{ should_not be_able_to(:manage, my_event.event_type) } - it{ should_not be_able_to(:manage, other_event.event_type) } - it{ should_not be_able_to(:manage, my_event.track) } - it{ should_not be_able_to(:manage, other_event.track) } - it{ should_not be_able_to(:manage, my_event.difficulty_level) } - it{ should_not be_able_to(:manage, other_event.difficulty_level) } - it{ should_not be_able_to(:manage, my_event.commercials.first) } - it{ should_not be_able_to(:manage, other_event.commercials.first) } - it{ should_not be_able_to(:index, my_event.comment_threads.first) } - it{ should_not be_able_to(:index, other_event.comment_threads.first) } - - it{ should_not be_able_to(:manage, resource)} - it{ should be_able_to(:index, resource)} - it{ should be_able_to(:show, resource)} - it{ should be_able_to(:update, resource)} - - it 'should be_able to :manage Vposition' - it 'should be_able to :manage Vday' - - it_behaves_like 'user with any role' - it_behaves_like 'user with non-organizer role', 'volunteers_coordinator' + it{ should be_able_to(:edit, user_self_organized_track) } + it{ should be_able_to(:update, user_self_organized_track) } + it{ should_not be_able_to(:edit, accepted_user_self_organized_track) } + it{ should_not be_able_to(:update, accepted_user_self_organized_track) } + it{ should_not be_able_to(:edit, confirmed_user_self_organized_track) } + it{ should_not be_able_to(:update, confirmed_user_self_organized_track) } + it{ should_not be_able_to(:edit, other_self_organized_track) } + it{ should_not be_able_to(:update, other_self_organized_track) } end end end diff --git a/spec/models/admin_ability_spec.rb b/spec/models/admin_ability_spec.rb new file mode 100644 index 00000000..344f63ba --- /dev/null +++ b/spec/models/admin_ability_spec.rb @@ -0,0 +1,548 @@ +require 'spec_helper' +require 'cancan/matchers' + +describe 'User with admin role' do + describe 'Abilities' do + let!(:admin) { create(:admin) } + + # see https://github.com/CanCanCommunity/cancancan/wiki/Testing-Abilities + subject(:ability){ AdminAbility.new(user) } + let(:user){ nil } + + let!(:organization) { create(:organization) } + let!(:my_conference) { create(:full_conference, organization: organization) } + let!(:registration_ticket) { create(:registration_ticket, conference: my_conference) } + let(:my_venue) { my_conference.venue || create(:venue, conference: my_conference) } + let(:my_registration) { create(:registration, conference: my_conference, user: admin) } + + let(:other_registration) { create(:registration, conference: conference_public) } + let(:my_event) { create(:event_full, program: my_conference.program) } + let(:my_room) { create(:room, venue: my_conference.venue) } + let!(:my_event_scheduled) { create(:event_full, program: my_conference.program, room_id: my_room.id) } + let(:other_event) { create(:event_full, program: conference_public.program) } + + let(:conference_not_public) { create(:conference, splashpage: create(:splashpage, public: false)) } + let(:conference_public) { create(:full_conference, splashpage: create(:splashpage, public: true)) } + + let(:event_confirmed) { create(:event, state: 'confirmed') } + let(:event_unconfirmed) { create(:event) } + + let(:commercial_event_confirmed) { create(:commercial, commercialable: event_confirmed) } + let(:commercial_event_unconfirmed) { create(:commercial, commercialable: event_unconfirmed) } + let(:resource) { create(:resource, conference: my_conference) } + let(:registration) { create(:registration) } + + let(:program_with_cfp) { create(:program, :with_cfp) } + let(:program_without_cfp) { create(:program) } + let(:conference_with_open_registration) { create(:conference) } + let!(:open_registration_period) { create(:registration_period, conference: conference_with_open_registration, start_date: Date.current - 6.days) } + let(:conference_with_closed_registration) { create(:conference) } + let!(:closed_registration_period) { create(:registration_period, conference: conference_with_closed_registration, start_date: Date.current - 6.days, end_date: Date.current - 6.days) } + + let!(:my_schedule) { create(:schedule, program: my_conference.program) } + let!(:other_schedule) { create(:schedule, program: conference_public.program) } + + let!(:my_event_schedule) { create(:event_schedule, schedule: my_schedule) } + let!(:other_event_schedule) { create(:event_schedule, schedule: other_schedule) } + + let!(:my_self_organized_track) { create(:track, :self_organized, program: my_conference.program, state: 'confirmed') } + + context 'user #is_admin?' do + let(:venue) { my_conference.venue } + let(:room) { create(:room, venue: venue) } + let!(:event) { create(:event_full, program: my_conference.program, room_id: room.id) } + let(:user) { create(:admin) } + it{ should be_able_to(:manage, :all) } + it{ should_not be_able_to(:destroy, my_conference.program) } + it{ should_not be_able_to(:destroy, my_venue) } + end + + shared_examples 'user with any role' do + let!(:other_organization) { create(:organization) } + let!(:other_conference) { create(:conference, organization: other_organization) } + + it{ should_not be_able_to(:update, Role.find_by(name: 'organization_admin', resource: other_organization)) } + it{ should_not be_able_to(:edit, Role.find_by(name: 'organization_admin', resource: other_organization)) } + it{ should be_able_to(:admins, organization) } + + it{ should_not be_able_to(:new, User.new) } + it{ should_not be_able_to(:create, User.new) } + it{ should_not be_able_to(:manage, User) } + + %w[organizer cfp info_desk volunteers_coordinator].each do |role| + it{ should_not be_able_to(:toggle_user, Role.find_by(name: role, resource: other_conference)) } + it{ should_not be_able_to(:update, Role.find_by(name: role, resource: other_conference)) } + it{ should_not be_able_to(:edit, Role.find_by(name: role, resource: other_conference)) } + it{ should be_able_to(:show, Role.find_by(name: role, resource: other_conference)) } + it{ should be_able_to(:index, Role.find_by(name: role, resource: other_conference)) } + end + + context 'accesses track organizers' do + before :each do + other_self_organized_track = create(:track, :self_organized) + @other_track_organizer_role = Role.where(name: 'track_organizer', resource: other_self_organized_track).first_or_create + end + + it{ should_not be_able_to(:toggle_user, @other_track_organizer_role) } + it{ should_not be_able_to(:update, @other_track_organizer_role) } + it{ should_not be_able_to(:edit, @other_track_organizer_role) } + it{ should be_able_to(:show, @other_track_organizer_role) } + it{ should be_able_to(:index, @other_track_organizer_role) } + end + end + + shared_examples 'user with non-organizer role' do |role_name| + %w[organizer cfp info_desk volunteers_coordinator].each do |role| + if role == role_name + it{ should be_able_to(:toggle_user, Role.find_by(name: role, resource: my_conference)) } + else + it{ should_not be_able_to(:toggle_user, Role.find_by(name: role, resource: my_conference)) } + end + it{ should_not be_able_to(:update, Role.find_by(name: role, resource: my_conference)) } + it{ should_not be_able_to(:edit, Role.find_by(name: role, resource: my_conference)) } + it{ should be_able_to(:show, Role.find_by(name: role, resource: my_conference)) } + it{ should be_able_to(:index, Role.find_by(name: role, resource: my_conference)) } + end + + context 'accesses track organizers' do + before :each do + @track_organizer_role = Role.where(name: 'track_organizer', resource: my_self_organized_track).first_or_create + end + + if role_name == 'track_organizer' + it{ should be_able_to(:toggle_user, @track_organizer_role) } + else + it{ should_not be_able_to(:toggle_user, @track_organizer_role) } + end + it{ should_not be_able_to(:update, @track_organizer_role) } + it{ should_not be_able_to(:edit, @track_organizer_role) } + it{ should be_able_to(:show, @track_organizer_role) } + it{ should be_able_to(:index, @track_organizer_role) } + end + end + + context 'when user has the role organization_admin' do + let(:role) { Role.find_by(name: 'organization_admin', resource: organization) } + let(:user) { create(:user, role_ids: [role.id]) } + let(:other_organization) { create(:organization) } + let(:other_conference) { create(:conference, organization: other_organization) } + + it{ should be_able_to(:assign_org_admins, organization) } + it{ should be_able_to(:unassign_org_admins, organization) } + it{ should be_able_to(:manage, my_conference) } + it{ should be_able_to(:read, organization) } + it{ should be_able_to(:update, organization) } + it{ should be_able_to(:destroy, organization) } + it{ should be_able_to(:new, Conference.new) } + it{ should be_able_to(:create, Conference.new(organization_id: organization.id)) } + it{ should_not be_able_to(:manage, other_conference) } + it{ should_not be_able_to(:create, Conference.new(organization_id: other_organization.id)) } + it{ should_not be_able_to(:new, Organization.new) } + it{ should_not be_able_to(:create, Organization.new) } + + it_behaves_like 'user with any role' + end + + context 'when user has the role organizer' do + let(:role) { Role.find_by(name: 'organizer', resource: my_conference) } + let(:user) { create(:user, role_ids: [role.id]) } + + it{ should_not be_able_to(:destroy, my_conference.program) } + it 'when there is a room assigned to an event' do + should_not be_able_to(:destroy, my_venue) + end + + it 'when there are no rooms used' do + my_event_scheduled.room_id = nil + my_event_scheduled.save! + my_event_scheduled.reload + should be_able_to(:destroy, my_venue) + end + + it{ should_not be_able_to(:new, Organization.new) } + it{ should_not be_able_to(:create, Organization.new) } + it{ should_not be_able_to(:new, Conference.new) } + it{ should_not be_able_to(:create, Conference.new) } + it{ should be_able_to(:read, my_conference) } + it{ should be_able_to(:update, my_conference) } + it{ should be_able_to(:destroy, my_conference) } + it{ should_not be_able_to(:manage, conference_public) } + it{ should be_able_to(:manage, my_conference.splashpage) } + it{ should_not be_able_to(:manage, conference_public.splashpage) } + it{ should be_able_to(:manage, my_conference.contact) } + it{ should_not be_able_to(:manage, conference_public.contact) } + it{ should be_able_to(:manage, my_conference.email_settings) } + it{ should_not be_able_to(:manage, conference_public.email_settings) } + it{ should be_able_to(:manage, my_conference.campaigns.first) } + it{ should_not be_able_to(:manage, conference_public.campaigns.first) } + it{ should be_able_to(:manage, my_conference.targets.first) } + it{ should_not be_able_to(:manage, conference_public.targets.first) } + it{ should be_able_to(:manage, my_conference.commercials.first) } + it{ should_not be_able_to(:manage, conference_public.commercials.first) } + it{ should be_able_to(:manage, my_conference.registration_period) } + it{ should_not be_able_to(:manage, conference_public.registration_period) } + it{ should be_able_to(:manage, my_conference.questions.first) } + it{ should_not be_able_to(:manage, conference_public.questions.first) } + it{ should be_able_to(:manage, my_conference.program.cfp) } + it{ should_not be_able_to(:manage, conference_public.program.cfp) } + it{ should be_able_to(:manage, my_schedule) } + it{ should_not be_able_to(:manage, other_schedule) } + it{ should be_able_to(:manage, my_event_schedule) } + it{ should_not be_able_to(:manage, other_event_schedule) } + it{ should be_able_to(:manage, my_conference.venue) } + it{ should_not be_able_to(:manage, conference_public.venue) } + it{ should be_able_to(:manage, my_conference.lodgings.first) } + it{ should_not be_able_to(:manage, conference_public.lodgings.first) } + it{ should be_able_to(:manage, my_conference.sponsors.first) } + it{ should_not be_able_to(:manage, conference_public.sponsors.first) } + it{ should be_able_to(:manage, my_conference.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } + it{ should be_able_to(:manage, my_conference.tickets.first) } + it{ should_not be_able_to(:manage, conference_public.tickets.first) } + + it{ should be_able_to(:manage, my_registration) } + it{ should_not be_able_to(:manage, other_registration) } + + it{ should be_able_to(:manage, my_event) } + it{ should_not be_able_to(:manage, other_event) } + it{ should be_able_to(:manage, my_event.event_type) } + it{ should_not be_able_to(:manage, other_event.event_type) } + it{ should be_able_to(:manage, my_event.track) } + it{ should_not be_able_to(:manage, other_event.track) } + it{ should be_able_to(:manage, my_event.difficulty_level) } + it{ should_not be_able_to(:manage, other_event.difficulty_level) } + it{ should be_able_to(:manage, my_event.commercials.first) } + it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should be_able_to(:index, my_event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } + + it{ should be_able_to(:manage, resource) } + + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } + + %w[organizer cfp info_desk volunteers_coordinator].each do |role| + it{ should be_able_to(:toggle_user, Role.find_by(name: role, resource: my_conference)) } + it{ should be_able_to(:edit, Role.find_by(name: role, resource: my_conference)) } + it{ should be_able_to(:update, Role.find_by(name: role, resource: my_conference)) } + it{ should be_able_to(:show, Role.find_by(name: role, resource: my_conference)) } + it{ should be_able_to(:index, Role.find_by(name: role, resource: my_conference)) } + end + + context 'can manage track organizers' do + before :each do + @track_organizer_role = Role.where(name: 'track_organizer', resource: my_self_organized_track).first_or_create + end + + it{ should be_able_to(:toggle_user, @track_organizer_role) } + it{ should be_able_to(:edit, @track_organizer_role) } + it{ should be_able_to(:update, @track_organizer_role) } + it{ should be_able_to(:show, @track_organizer_role) } + it{ should be_able_to(:index, @track_organizer_role) } + end + + it_behaves_like 'user with any role' + end + + context 'when user has the role cfp' do + let(:role) { Role.find_by(name: 'cfp', resource: my_conference) } + let(:user) { create(:user, role_ids: [role.id]) } + + it{ should_not be_able_to(:new, Conference.new) } + it{ should_not be_able_to(:create, Conference.new) } + it{ should_not be_able_to(:manage, my_conference) } + it{ should_not be_able_to(:manage, conference_public) } + it{ should_not be_able_to(:manage, my_conference.splashpage) } + it{ should_not be_able_to(:manage, conference_public.splashpage) } + it{ should_not be_able_to(:manage, my_conference.contact) } + it{ should_not be_able_to(:manage, conference_public.contact) } + it{ should be_able_to(:manage, my_conference.email_settings) } + it{ should_not be_able_to(:manage, conference_public.email_settings) } + it{ should_not be_able_to(:manage, my_conference.campaigns.first) } + it{ should_not be_able_to(:manage, conference_public.campaigns.first) } + it{ should_not be_able_to(:manage, my_conference.targets.first) } + it{ should_not be_able_to(:manage, conference_public.targets.first) } + it{ should_not be_able_to(:manage, my_conference.commercials.first) } + it{ should_not be_able_to(:manage, conference_public.commercials.first) } + it{ should_not be_able_to(:manage, my_conference.registration_period) } + it{ should_not be_able_to(:manage, conference_public.registration_period) } + it{ should_not be_able_to(:manage, my_conference.questions.first) } + it{ should_not be_able_to(:manage, conference_public.questions.first) } + it{ should be_able_to(:manage, my_conference.program.cfp) } + it{ should_not be_able_to(:manage, conference_public.program.cfp) } + it{ should be_able_to(:manage, my_schedule) } + it{ should_not be_able_to(:manage, other_schedule) } + it{ should_not be_able_to(:manage, my_event_schedule) } + it{ should_not be_able_to(:manage, other_event_schedule) } + it{ should_not be_able_to(:manage, my_conference.venue) } + it{ should be_able_to(:show, my_conference.venue) } + it{ should_not be_able_to(:manage, conference_public.venue) } + it{ should_not be_able_to(:manage, my_conference.lodgings.first) } + it{ should_not be_able_to(:manage, conference_public.lodgings.first) } + it{ should_not be_able_to(:manage, my_conference.sponsors.first) } + it{ should_not be_able_to(:manage, conference_public.sponsors.first) } + it{ should_not be_able_to(:manage, my_conference.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, my_conference.tickets.first) } + it{ should_not be_able_to(:manage, conference_public.tickets.first) } + + it{ should_not be_able_to(:manage, my_registration) } + it{ should_not be_able_to(:manage, other_registration) } + + it{ should be_able_to(:manage, my_event) } + it{ should_not be_able_to(:manage, other_event) } + it{ should be_able_to(:manage, my_event.event_type) } + it{ should_not be_able_to(:manage, other_event.event_type) } + it{ should be_able_to(:manage, my_event.track) } + it{ should_not be_able_to(:manage, other_event.track) } + it{ should be_able_to(:manage, my_event.difficulty_level) } + it{ should_not be_able_to(:manage, other_event.difficulty_level) } + it{ should be_able_to(:manage, my_event.commercials.first) } + it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should be_able_to(:index, my_event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } + + it{ should_not be_able_to(:manage, resource) } + it{ should be_able_to(:index, resource) } + it{ should be_able_to(:show, resource) } + it{ should be_able_to(:update, resource) } + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } + + it_behaves_like 'user with any role' + it_behaves_like 'user with non-organizer role', 'cfp' + end + + context 'when user has the role info_desk' do + let(:role) { Role.find_by(name: 'info_desk', resource: my_conference) } + let(:user) { create(:user, role_ids: [role.id]) } + + it{ should_not be_able_to(:new, Conference.new) } + it{ should_not be_able_to(:create, Conference.new) } + it{ should_not be_able_to(:manage, my_conference) } + it{ should_not be_able_to(:manage, conference_public) } + it{ should_not be_able_to(:manage, my_conference.splashpage) } + it{ should_not be_able_to(:manage, conference_public.splashpage) } + it{ should_not be_able_to(:manage, my_conference.contact) } + it{ should_not be_able_to(:manage, conference_public.contact) } + it{ should_not be_able_to(:manage, my_conference.email_settings) } + it{ should_not be_able_to(:manage, conference_public.email_settings) } + it{ should_not be_able_to(:manage, my_conference.campaigns.first) } + it{ should_not be_able_to(:manage, conference_public.campaigns.first) } + it{ should_not be_able_to(:manage, my_conference.targets.first) } + it{ should_not be_able_to(:manage, conference_public.targets.first) } + it{ should_not be_able_to(:manage, my_conference.commercials.first) } + it{ should_not be_able_to(:manage, conference_public.commercials.first) } + it{ should_not be_able_to(:manage, my_conference.registration_period) } + it{ should_not be_able_to(:manage, conference_public.registration_period) } + it{ should be_able_to(:manage, my_conference.questions.first) } + it{ should_not be_able_to(:manage, conference_public.questions.first) } + it{ should_not be_able_to(:manage, my_conference.program.cfp) } + it{ should_not be_able_to(:manage, conference_public.program.cfp) } + it{ should_not be_able_to(:manage, my_schedule) } + it{ should_not be_able_to(:manage, other_schedule) } + it{ should_not be_able_to(:manage, my_event_schedule) } + it{ should_not be_able_to(:manage, other_event_schedule) } + it{ should_not be_able_to(:manage, my_conference.venue) } + it{ should_not be_able_to(:show, my_conference.venue) } + it{ should_not be_able_to(:manage, conference_public.venue) } + it{ should_not be_able_to(:manage, my_conference.lodgings.first) } + it{ should_not be_able_to(:manage, conference_public.lodgings.first) } + it{ should_not be_able_to(:manage, my_conference.sponsors.first) } + it{ should_not be_able_to(:manage, conference_public.sponsors.first) } + it{ should_not be_able_to(:manage, my_conference.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, my_conference.tickets.first) } + it{ should_not be_able_to(:manage, conference_public.tickets.first) } + + it{ should be_able_to(:manage, my_registration) } + it{ should_not be_able_to(:manage, other_registration) } + + it{ should_not be_able_to(:manage, my_event) } + it{ should_not be_able_to(:manage, other_event) } + it{ should_not be_able_to(:manage, my_event.event_type) } + it{ should_not be_able_to(:manage, other_event.event_type) } + it{ should_not be_able_to(:manage, my_event.track) } + it{ should_not be_able_to(:manage, other_event.track) } + it{ should_not be_able_to(:manage, my_event.difficulty_level) } + it{ should_not be_able_to(:manage, other_event.difficulty_level) } + it{ should_not be_able_to(:manage, my_event.commercials.first) } + it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should_not be_able_to(:index, my_event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } + + it{ should_not be_able_to(:manage, resource) } + it{ should be_able_to(:index, resource) } + it{ should be_able_to(:show, resource) } + it{ should be_able_to(:update, resource) } + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } + + it_behaves_like 'user with any role' + it_behaves_like 'user with non-organizer role', 'info_desk' + end + + context 'when user has the role volunteers_coordinator' do + let(:role) { Role.find_by(name: 'volunteers_coordinator', resource: my_conference) } + let(:user) { create(:user, role_ids: [role.id]) } + + it{ should_not be_able_to(:new, Conference.new) } + it{ should_not be_able_to(:create, Conference.new) } + it{ should_not be_able_to(:manage, my_conference) } + it{ should_not be_able_to(:manage, conference_public) } + it{ should_not be_able_to(:manage, my_conference.splashpage) } + it{ should_not be_able_to(:manage, conference_public.splashpage) } + it{ should_not be_able_to(:manage, my_conference.contact) } + it{ should_not be_able_to(:manage, conference_public.contact) } + it{ should_not be_able_to(:manage, my_conference.email_settings) } + it{ should_not be_able_to(:manage, conference_public.email_settings) } + it{ should_not be_able_to(:manage, my_conference.campaigns.first) } + it{ should_not be_able_to(:manage, conference_public.campaigns.first) } + it{ should_not be_able_to(:manage, my_conference.targets.first) } + it{ should_not be_able_to(:manage, conference_public.targets.first) } + it{ should_not be_able_to(:manage, my_conference.commercials.first) } + it{ should_not be_able_to(:manage, conference_public.commercials.first) } + it{ should_not be_able_to(:manage, my_conference.registration_period) } + it{ should_not be_able_to(:manage, conference_public.registration_period) } + it{ should_not be_able_to(:manage, my_conference.questions.first) } + it{ should_not be_able_to(:manage, conference_public.questions.first) } + it{ should_not be_able_to(:manage, my_conference.program.cfp) } + it{ should_not be_able_to(:manage, conference_public.program.cfp) } + it{ should_not be_able_to(:manage, my_schedule) } + it{ should_not be_able_to(:manage, other_schedule) } + it{ should_not be_able_to(:manage, my_event_schedule) } + it{ should_not be_able_to(:manage, other_event_schedule) } + it{ should_not be_able_to(:manage, my_conference.venue) } + it{ should_not be_able_to(:show, my_conference.venue) } + it{ should_not be_able_to(:manage, conference_public.venue) } + it{ should_not be_able_to(:manage, my_conference.lodgings.first) } + it{ should_not be_able_to(:manage, conference_public.lodgings.first) } + it{ should_not be_able_to(:manage, my_conference.sponsors.first) } + it{ should_not be_able_to(:manage, conference_public.sponsors.first) } + it{ should_not be_able_to(:manage, my_conference.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, my_conference.tickets.first) } + it{ should_not be_able_to(:manage, conference_public.tickets.first) } + + it{ should_not be_able_to(:manage, registration) } + it{ should_not be_able_to(:manage, other_registration) } + + it{ should_not be_able_to(:manage, my_event) } + it{ should_not be_able_to(:manage, other_event) } + it{ should_not be_able_to(:manage, my_event.event_type) } + it{ should_not be_able_to(:manage, other_event.event_type) } + it{ should_not be_able_to(:manage, my_event.track) } + it{ should_not be_able_to(:manage, other_event.track) } + it{ should_not be_able_to(:manage, my_event.difficulty_level) } + it{ should_not be_able_to(:manage, other_event.difficulty_level) } + it{ should_not be_able_to(:manage, my_event.commercials.first) } + it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should_not be_able_to(:index, my_event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } + + it{ should_not be_able_to(:manage, resource) } + it{ should be_able_to(:index, resource) } + it{ should be_able_to(:show, resource) } + it{ should be_able_to(:update, resource) } + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } + + it 'should be_able to :manage Vposition' + it 'should be_able to :manage Vday' + + it_behaves_like 'user with any role' + it_behaves_like 'user with non-organizer role', 'volunteers_coordinator' + end + + context 'when user has the role track_organizer' do + + let(:role) { Role.where(name: 'track_organizer', resource: my_self_organized_track).first_or_create } + let(:user) { create(:user, role_ids: [role.id]) } + let(:new_track) { build(:track, program: my_conference.program) } + let(:new_event) { build(:event, program: my_conference.program) } + let(:new_schedule) { build(:schedule, program: my_conference.program) } + let(:new_track_schedule) { build(:schedule, program: my_conference.program, track: new_track) } + let(:my_self_organized_track_event) { create(:event, program: my_conference.program, track: my_self_organized_track) } + let(:my_self_organized_track_event_commercial) { create(:commercial, commercialable: my_self_organized_track_event) } + let(:my_self_organized_track_schedule) { create(:schedule, program: my_conference.program, track: my_self_organized_track) } + let(:my_self_organized_track_event_schedule) { create(:event_schedule, event: my_self_organized_track_event, schedule: my_self_organized_track_schedule, room: my_self_organized_track.room) } + + it{ should_not be_able_to(:new, Conference.new) } + it{ should_not be_able_to(:create, Conference.new) } + it{ should_not be_able_to(:manage, my_conference) } + it{ should_not be_able_to(:manage, conference_public) } + it{ should_not be_able_to(:manage, my_conference.splashpage) } + it{ should_not be_able_to(:manage, conference_public.splashpage) } + it{ should_not be_able_to(:manage, my_conference.contact) } + it{ should_not be_able_to(:manage, conference_public.contact) } + it{ should_not be_able_to(:manage, my_conference.email_settings) } + it{ should_not be_able_to(:manage, conference_public.email_settings) } + it{ should_not be_able_to(:manage, my_conference.campaigns.first) } + it{ should_not be_able_to(:manage, conference_public.campaigns.first) } + it{ should_not be_able_to(:manage, my_conference.targets.first) } + it{ should_not be_able_to(:manage, conference_public.targets.first) } + it{ should_not be_able_to(:manage, my_conference.commercials.first) } + it{ should_not be_able_to(:manage, conference_public.commercials.first) } + it{ should_not be_able_to(:manage, my_conference.registration_period) } + it{ should_not be_able_to(:manage, conference_public.registration_period) } + it{ should_not be_able_to(:manage, my_conference.questions.first) } + it{ should_not be_able_to(:manage, conference_public.questions.first) } + it{ should_not be_able_to(:manage, my_conference.program.cfp) } + it{ should_not be_able_to(:manage, conference_public.program.cfp) } + it{ should_not be_able_to(:manage, my_schedule) } + it{ should_not be_able_to(:manage, other_schedule) } + it{ should_not be_able_to(:manage, my_event_schedule) } + it{ should_not be_able_to(:manage, other_event_schedule) } + it{ should_not be_able_to(:manage, my_conference.venue) } + it{ should_not be_able_to(:show, my_conference.venue) } + it{ should_not be_able_to(:manage, conference_public.venue) } + it{ should_not be_able_to(:manage, my_conference.lodgings.first) } + it{ should_not be_able_to(:manage, conference_public.lodgings.first) } + it{ should_not be_able_to(:manage, my_conference.sponsors.first) } + it{ should_not be_able_to(:manage, conference_public.sponsors.first) } + it{ should_not be_able_to(:manage, my_conference.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) } + it{ should_not be_able_to(:manage, my_conference.tickets.first) } + it{ should_not be_able_to(:manage, conference_public.tickets.first) } + + it{ should_not be_able_to(:manage, registration) } + it{ should_not be_able_to(:manage, other_registration) } + + it{ should_not be_able_to(:manage, my_event) } + it{ should_not be_able_to(:manage, other_event) } + it{ should_not be_able_to(:manage, my_event.event_type) } + it{ should_not be_able_to(:manage, other_event.event_type) } + it{ should_not be_able_to(:manage, my_event.track) } + it{ should_not be_able_to(:manage, other_event.track) } + it{ should_not be_able_to(:manage, my_event.difficulty_level) } + it{ should_not be_able_to(:manage, other_event.difficulty_level) } + it{ should_not be_able_to(:manage, my_event.commercials.first) } + it{ should_not be_able_to(:manage, other_event.commercials.first) } + it{ should_not be_able_to(:index, my_event.comment_threads.first) } + it{ should_not be_able_to(:index, other_event.comment_threads.first) } + + it{ should_not be_able_to(:manage, resource) } + + it{ should be_able_to(:show, my_conference.program) } + it{ should be_able_to(:update, new_track) } + it{ should be_able_to(:manage, my_self_organized_track) } + it{ should_not be_able_to(:edit, my_self_organized_track) } + it{ should_not be_able_to(:update, my_self_organized_track) } + + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } + + it{ should be_able_to(:update, new_event) } + it{ should be_able_to(:manage, my_self_organized_track_event) } + it{ should be_able_to(:manage, my_self_organized_track_event_commercial) } + + it{ should be_able_to(:update, new_schedule) } + it{ should be_able_to(:new, new_track_schedule) } + it{ should be_able_to(:manage, my_self_organized_track_schedule) } + it{ should be_able_to(:manage, my_self_organized_track_event_schedule) } + + it_behaves_like 'user with any role' + it_behaves_like 'user with non-organizer role', 'track_organizer' + end + end +end diff --git a/spec/models/booth_spec.rb b/spec/models/booth_spec.rb new file mode 100644 index 00000000..287653ac --- /dev/null +++ b/spec/models/booth_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe 'Booth' do + subject { create(:booth) } + let!(:conference) { create(:conference) } + + describe 'validation' do + it 'has a valid factory' do + expect(build(:booth)).to be_valid + end + + it { is_expected.to validate_presence_of(:reasoning) } + it { is_expected.to validate_presence_of(:description) } + it { is_expected.to validate_presence_of(:responsibles) } + it { is_expected.to validate_presence_of(:submitter_relationship) } + it { is_expected.to validate_presence_of(:website_url) } + + it 'is not valid without a title' do + is_expected.to validate_presence_of(:title) + end + end + + describe 'association' do + it { is_expected.to belong_to(:conference) } + it { is_expected.to have_many(:booth_requests) } + end + + describe '#transition_possible?(transition)' do + shared_examples 'transition_possible?(transition)' do |state, transition, expected| + it "returns #{expected} for #{transition} transition, when the booth is #{state}}" do + my_booth = create(:booth, state: state) + expect(my_booth.transition_possible?(transition.to_sym)).to eq expected + end + end + + states = [:new, :withdrawn, :to_accept, :accepted, :to_reject, :rejected, :canceled, :confirmed] + transitions = [:restart, :withdraw, :accept, :reject, :to_accept, :to_reject, :cancel] + + states_transitions = { new: { restart: false, withdraw: true, accept: true, to_accept: true, to_reject: true, reject: true, cancel: false, confirm: false }, + withdrawn: { restart: true, withdraw: false, accept: false, to_accept: false, to_reject: false, reject: false, cancel: false, confirm: false }, + to_accept: { restart: false, withdraw: true, accept: true, to_accept: false, to_reject: true, reject: false, cancel: true, confirm: false }, + to_reject: { restart: false, withdraw: true, accept: false, to_accept: true, to_reject: false, reject: true, cancel: true, confirm: false }, + accepted: { restart: false, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true, confirm: true }, + rejected: { restart: true, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true, confirm: false }, + canceled: { restart: true, withdraw: false, accept: false, to_accept: false, to_reject: false, reject: false, cancel: false, confirm: false }, + confirmed: { restart: false, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true, confirm: false } } + + states.each do |state| + transitions.each do |transition| + it_behaves_like 'transition_possible?(transition)', state, transition, states_transitions[state.to_sym][transition.to_sym] + end + end + end + +end diff --git a/spec/models/cfp_spec.rb b/spec/models/cfp_spec.rb index 3f8e7a0f..6c437d35 100644 --- a/spec/models/cfp_spec.rb +++ b/spec/models/cfp_spec.rb @@ -1,8 +1,38 @@ require 'spec_helper' describe Cfp do + subject { create(:cfp) } let!(:conference) { create(:conference, end_date: Date.today) } - let!(:cfp) { build(:cfp, start_date: Date.today - 2, end_date: Date.today - 1, program_id: conference.program.id) } + let!(:cfp) { create(:cfp, cfp_type: 'events', start_date: Date.today - 2, end_date: Date.today - 1, program_id: conference.program.id) } + + describe 'validations' do + it { is_expected.to validate_presence_of(:cfp_type) } + it { is_expected.to validate_inclusion_of(:cfp_type).in_array(Cfp::TYPES) } + it { is_expected.to validate_uniqueness_of(:cfp_type).scoped_to(:program_id).case_insensitive } + end + + describe '.for_events' do + it 'returns the cfp for events when it exists' do + expect(conference.program.cfps.for_events).to be_a Cfp + expect(conference.program.cfps.for_events.cfp_type).to eq('events') + end + + it 'returns nil when the cfp for events doesn\'t exist' do + cfp.destroy! + expect(conference.program.cfps.for_events).to eq nil + end + end + + describe '.for_tracks' do + it 'returns the cfp for tracks when it exists' do + call_for_tracks = create(:cfp, cfp_type: 'tracks', program: conference.program, end_date: Date.today) + expect(conference.program.cfps.for_tracks).to eq call_for_tracks + end + + it 'returns nil when the cfp for tracks doesn\'t exist' do + expect(conference.program.cfps.for_tracks).to eq nil + end + end describe '#before_end_of_conference' do describe 'fails to save cfp' do @@ -97,4 +127,28 @@ describe Cfp do end end end + + describe '#open?' do + context 'returns false' do + it 'when start and end dates are in the past' do + cfp.start_date = Date.current - 3 + cfp.end_date = Date.current - 1 + expect(cfp.open?).to eq(false) + end + + it 'when start and end dates are in the future' do + cfp.start_date = Date.current + 1 + cfp.end_date = Date.current + 3 + expect(cfp.open?).to eq(false) + end + end + + context 'returns true' do + it 'when start date is in the past and end date is in the future' do + cfp.start_date = Date.current - 1 + cfp.end_date = Date.current + 1 + expect(cfp.open?).to eq(true) + end + end + end end diff --git a/spec/models/conference_spec.rb b/spec/models/conference_spec.rb index b5a6d5f0..26070134 100755 --- a/spec/models/conference_spec.rb +++ b/spec/models/conference_spec.rb @@ -48,7 +48,7 @@ describe Conference do subject.start_date = Date.today + 6.weeks subject.end_date = Date.today + 7.weeks subject.save - subject.program.cfp = create(:cfp, start_date: Date.today - 3.weeks) + create(:cfp, start_date: Date.today - 3.weeks, program: subject.program) create(:event, program: subject.program, created_at: Date.today) options = {} @@ -115,7 +115,7 @@ describe Conference do } subject.events_per_week = db_data subject.save - subject.program.cfp = create(:cfp, start_date: Date.today - 3.weeks) + create(:cfp, start_date: Date.today - 3.weeks, program: subject.program) create(:event, program: subject.program, created_at: Date.today) unconfirmed = create(:event, program: subject.program) @@ -186,7 +186,7 @@ describe Conference do subject.events_per_week = db_data subject.save - subject.program.cfp = create(:cfp, start_date: Date.today - 2.weeks) + create(:cfp, start_date: Date.today - 2.weeks, program: subject.program) create(:event, program: subject.program, created_at: Date.today - 2.weeks) @@ -203,7 +203,7 @@ describe Conference do subject.start_date = Date.today + 6.weeks subject.end_date = Date.today + 7.weeks subject.save - subject.program.cfp = create(:cfp, start_date: Date.today) + create(:cfp, start_date: Date.today, program: subject.program) create(:event, program: subject.program) result = { @@ -220,7 +220,7 @@ describe Conference do subject.start_date = Date.today + 6.weeks subject.end_date = Date.today + 7.weeks subject.save - subject.program.cfp = create(:cfp, start_date: Date.today - 3.weeks) + create(:cfp, start_date: Date.today - 3.weeks, program: subject.program) create(:event, program: subject.program, created_at: Date.today) unconfirmed = create(:event, program: subject.program) @@ -258,7 +258,7 @@ describe Conference do subject.events_per_week = db_data subject.save - subject.program.cfp = create(:cfp, start_date: Date.today - 3.weeks) + create(:cfp, start_date: Date.today - 3.weeks, program: subject.program) create(:event, program: subject.program, created_at: Date.today - 3.weeks) @@ -606,8 +606,8 @@ describe Conference do describe 'tracks_distribution' do before do subject.email_settings = create(:email_settings) - @track_one = create(:track, name: 'Track One', color: '#000000') - @track_two = create(:track, name: 'Track Two', color: '#ffffff') + @track_one = create(:track, name: 'Track One', color: '#000000', program: subject.program) + @track_two = create(:track, name: 'Track Two', color: '#ffffff', program: subject.program) end describe '#tracks_distribution' do @@ -992,7 +992,6 @@ describe Conference do end it 'calculates correct for new conference' do - subject.program.cfp = nil subject.venue = nil subject.program.tracks = [] subject.program.event_types = [] @@ -1006,7 +1005,6 @@ describe Conference do subject.registration_period = create(:registration_period, start_date: subject.end_date - 14, end_date: subject.end_date, conference: subject) - subject.program.cfp = nil subject.venue = nil subject.program.event_types = [] subject.program.tracks = [] @@ -1024,7 +1022,7 @@ describe Conference do subject.registration_period = create(:registration_period, start_date: subject.end_date - 14, end_date: subject.end_date, conference: subject) - subject.program.cfp = create(:cfp) + create(:cfp, program: subject.program) subject.venue = nil subject.program.tracks = [] subject.program.event_types = [] @@ -1043,7 +1041,7 @@ describe Conference do subject.registration_period = create(:registration_period, start_date: subject.end_date - 14, end_date: subject.end_date, conference: subject) - subject.program.cfp = create(:cfp) + create(:cfp, program: subject.program) subject.venue = create(:venue, conference: subject) subject.venue.rooms = [] subject.program.tracks = [] @@ -1063,7 +1061,7 @@ describe Conference do subject.registration_period = create(:registration_period, start_date: Date.today, end_date: Date.today + 14, conference: subject) - subject.program.cfp = create(:cfp) + create(:cfp, program: subject.program) subject.venue = create(:venue, conference: subject) subject.venue.rooms = [create(:room, venue: subject.venue)] subject.program.tracks = [] @@ -1087,7 +1085,7 @@ describe Conference do subject.registration_period = create(:registration_period, start_date: Date.today, end_date: Date.today + 14, conference: subject) - subject.program.cfp = create(:cfp) + create(:cfp, program: subject.program) subject.program.event_types = [] subject.program.difficulty_levels = [] subject.splashpage = create(:splashpage, public: false) @@ -1109,7 +1107,7 @@ describe Conference do subject.registration_period = create(:registration_period, start_date: Date.today, end_date: Date.today + 14, conference: subject) - subject.program.cfp = create(:cfp) + create(:cfp, program: subject.program) subject.venue = create(:venue, conference: subject) subject.venue.rooms = [create(:room, venue: subject.venue)] subject.program.difficulty_levels = [] @@ -1133,7 +1131,7 @@ describe Conference do subject.registration_period = create(:registration_period, start_date: Date.today, end_date: Date.today + 14, conference: subject) - subject.program.cfp = create(:cfp) + create(:cfp, program: subject.program) subject.venue = create(:venue, conference: subject) subject.venue.rooms = [create(:room, venue: subject.venue)] subject.splashpage = create(:splashpage, public: true) @@ -1176,34 +1174,34 @@ describe Conference do describe '#cfp_weeks' do it 'calculates new year' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2013, 12, 30) cfp.end_date = Date.new(2013, 12, 30) + 6 - subject.program.cfp = cfp + cfp.save! expect(subject.cfp_weeks).to eq(1) end it 'is one if start and end are 6 days apart' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 6 - subject.program.cfp = cfp + cfp.save! expect(subject.cfp_weeks).to eq(1) end it 'is one if start and end are the same date' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) - subject.program.cfp = cfp + cfp.save! expect(subject.cfp_weeks).to eq(1) end it 'is two if start and end are 10 days apart' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 10 - subject.program.cfp = cfp + cfp.save! expect(subject.cfp_weeks).to eq(2) end end @@ -1211,36 +1209,36 @@ describe Conference do describe '#get_submissions_per_week' do it 'does calculate correct if cfp start date is altered' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 21 - subject.program.cfp = cfp + cfp.save! subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) - 7)] expect(subject.get_submissions_per_week).to eq([1, 1, 1, 1, 1]) end it 'does calculate correct if cfp end date is altered' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 21 - subject.program.cfp = cfp + cfp.save! subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 28)] expect(subject.get_submissions_per_week).to eq([0, 0, 0, 0, 1]) end it 'pads with zeros if there are no submissions' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 21 - subject.program.cfp = cfp + cfp.save! expect(subject.get_submissions_per_week).to eq([0, 0, 0, 0]) end it 'summarized correct if there are no submissions in one week' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 28 - subject.program.cfp = cfp + cfp.save! subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 7)] subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 14)] subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 28)] @@ -1248,20 +1246,20 @@ describe Conference do end it 'summarized correct if there are submissions every week except the first' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 21 - subject.program.cfp = cfp + cfp.save! subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 7)] subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 14)] expect(subject.get_submissions_per_week).to eq([0, 1, 2, 2]) end it 'summarized correct if there are submissions every week' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 21 - subject.program.cfp = cfp + cfp.save! subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26))] subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 7)] subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 14)] @@ -1269,29 +1267,29 @@ describe Conference do end it 'pads left' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 21 - subject.program.cfp = cfp + cfp.save! subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 21)] expect(subject.get_submissions_per_week).to eq([0, 0, 0, 1]) end it 'pads middle' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 21 - subject.program.cfp = cfp + cfp.save! subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26))] subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26) + 21)] expect(subject.get_submissions_per_week).to eq([1, 1, 1, 2]) end it 'pads right' do - cfp = create(:cfp) + cfp = create(:cfp, program: subject.program) cfp.start_date = Date.new(2014, 05, 26) cfp.end_date = Date.new(2014, 05, 26) + 21 - subject.program.cfp = cfp + cfp.save! subject.program.events += [create(:event, created_at: Date.new(2014, 05, 26))] expect(subject.get_submissions_per_week).to eq([1, 1, 1, 1]) end @@ -1460,7 +1458,7 @@ describe Conference do context 'open cfp' do before do - subject.program.cfp = create(:cfp) + create(:cfp, program: subject.program) end it '#registration_open? is true' do @@ -1575,6 +1573,10 @@ describe Conference do should validate_presence_of(:end_hour) end + it 'is not valid without a ticket_layout' do + should validate_presence_of(:ticket_layout) + end + it 'is not valid with a duplicate short title' do should validate_uniqueness_of(:short_title) end @@ -1630,7 +1632,7 @@ describe Conference do end describe 'after_create' do - let(:conference) { Conference.new(title: 'ABC', short_title: 'XYZ', start_date: Date.today, end_date: Date.today + 10, timezone: 'GMT') } + let(:conference) { create(:conference) } it 'calls back to create free ticket' do conference.save @@ -1658,4 +1660,48 @@ describe Conference do expect{ conference.save }.to change{ EventSchedule.count }.from(2).to(1) end end + + describe '#revision' do + let(:track) { create(:track, program: subject.program) } + let(:event) { create(:event, program: subject.program, track: track) } + let(:venue) { create(:venue, conference: subject) } + let(:room) { create(:room, venue: venue) } + + it 'for change in conference' do + subject.title = 'changed' + expect{ subject.save }.to change { subject.revision }.by(1) + end + + it 'for change in event' do + event.title = 'changed' + expect{ event.save }.to change { subject.revision }.by(1) + end + + it 'for change in track' do + track.name = 'changed' + expect{ track.save }.to change { subject.revision }.by(1) + end + + it 'for change in room' do + room.name = 'changed' + expect{ room.save }.to change { subject.revision }.by(1) + end + end + + describe '.upcoming' do + let!(:upcoming_conference) { create(:conference) } + let!(:past_conference) { create(:conference, start_date: Date.current - 1.days, end_date: Date.current - 1.days) } + subject { Conference.upcoming } + + it { is_expected.to eq [upcoming_conference] } + end + + describe '.past' do + let!(:upcoming_conference) { create(:conference) } + let!(:past_conference1) { create(:conference, start_date: Date.current - 1.days, end_date: Date.current - 1.days) } + let!(:past_conference2) { create(:conference, start_date: Date.current - 2.days, end_date: Date.current - 1.days) } + subject { Conference.past } + + it { is_expected.to eq [past_conference1, past_conference2] } + end end diff --git a/spec/models/email_settings_spec.rb b/spec/models/email_settings_spec.rb index 949f5762..c4ab7830 100644 --- a/spec/models/email_settings_spec.rb +++ b/spec/models/email_settings_spec.rb @@ -46,9 +46,10 @@ describe EmailSettings do context 'conference has cfp' do before do - conference.program.update_attributes(cfp: create(:cfp, - start_date: Date.new(2014, 04, 29), - end_date: Date.new(2014, 05, 06))) + create(:cfp, + start_date: Date.new(2014, 04, 29), + end_date: Date.new(2014, 05, 06), + program: conference.program) cfp_dates_hash = { 'cfp_start_date' => Date.new(2014, 04, 29), 'cfp_end_date' => Date.new(2014, 05, 06) } expected_hash.merge!(cfp_dates_hash) end diff --git a/spec/models/event_schedule_spec.rb b/spec/models/event_schedule_spec.rb index 6d1325b7..91db4b64 100644 --- a/spec/models/event_schedule_spec.rb +++ b/spec/models/event_schedule_spec.rb @@ -45,5 +45,102 @@ describe EventSchedule do end end end + + describe '#same_room_as_track' do + before :each do + conference = create(:conference) + conference.venue = create(:venue) + @room = create(:room, venue: conference.venue) + @track = create(:track, program: conference.program, room: @room) + @event = create(:event, program: conference.program, track: @track) + end + + context 'is valid' do + it 'when scheduled in the track\'s room' do + event_schedule = build(:event_schedule, event: @event, room: @room) + expect(event_schedule.valid?).to eq true + end + + it 'when the track doesn\'t have a room' do + @track.room = nil + @track.save! + event_schedule = build(:event_schedule, event: @event) + expect(event_schedule.valid?).to eq true + end + end + + context 'is invalid' do + it 'when scheduled in different room than the track\'s' do + event_schedule = build(:event_schedule, event: @event) + expect(event_schedule.valid?).to eq false + expect(event_schedule.errors[:room]).to eq ["must be the same as the track's room (#{@room.name})"] + end + end + end + + describe '#during_track' do + before :each do + conference = create(:conference, start_date: Date.current - 1.day, start_hour: 0, end_hour: 24) + conference.venue = create(:venue) + @room = create(:room, venue: conference.venue) + @track = create(:track, program: conference.program, room: @room, start_date: Date.current, end_date: Date.current) + @event = create(:event, program: conference.program, track: @track) + end + + context 'is valid' do + it 'when scheduled during the track\'s time slot' do + event_schedule = build(:event_schedule, event: @event, room: @room, start_time: Date.current + 3.hours) + expect(event_schedule.valid?).to eq true + end + end + + context 'is invalid' do + it 'when scheduled before the track\'s start date' do + event_schedule = build(:event_schedule, event: @event, room: @room, start_time: Date.current - 1.hour) + expect(event_schedule.valid?).to eq false + expect(event_schedule.errors[:start_time]).to eq ["can't be before the track's start date (#{@track.start_date})"] + end + + it 'when event ends after the track\'s end date' do + event_schedule = build(:event_schedule, event: @event, room: @room, start_time: Date.current + 1.day - 10.minutes) + expect(event_schedule.valid?).to eq false + expect(event_schedule.errors[:end_time]).to eq ["can't be after the track's end date (#{@track.end_date})"] + end + end + end + + describe '#valid_schedule' do + before :each do + conference.venue = create(:venue) + @room = create(:room, venue: conference.venue) + track = create(:track, :self_organized, program: conference.program, room: @room, state: 'confirmed', name: 'My awesome track') + @event = create(:event, program: conference.program, track: track) + end + + context 'is valid' do + it 'when the event belongs to a self-organized track and is scheduled in one of its track\'s schedules' do + schedule = create(:schedule, program: conference.program, track: @event.track) + event_schedule = build(:event_schedule, event: @event, room: @room, schedule: schedule) + expect(event_schedule.valid?).to eq true + expect(event_schedule.errors[:schedule]).to eq [] + end + + it 'when the event doesn\'t belong to a self-organized track' do + @event.track = nil + @event.save! + event_schedule = build(:event_schedule, event: @event, room: @room) + expect(event_schedule.valid?).to eq true + expect(event_schedule.errors[:schedule]).to eq [] + end + end + + context 'is invalid' do + it 'when the event belongs to a self_organized track but isn\'t scheduled in one of its schedules' do + event_schedule = build(:event_schedule, event: @event, room: @room) + expect(event_schedule.valid?).to eq false + expect(event_schedule.errors[:schedule]).to eq ['must be one of My awesome track track\'s schedules'] + end + end + end end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index e1c2297c..0a84aedd 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -96,6 +96,56 @@ describe Event do end end end + + describe '#valid_track' do + context 'is valid' do + it 'when the track belongs to the same program and is confirmed' do + track = create(:track, state: 'confirmed', program: conference.program) + event = build(:event, program: conference.program, track: track) + expect(event.valid?).to eq true + end + end + + context 'is invalid' do + it 'when the track doesn\'t have the same program' do + track = create(:track, state: 'confirmed') + event = build(:event, program: conference.program, track: track) + expect(event.valid?).to eq false + expect(event.errors[:track]).to eq ['is invalid'] + end + + it 'when the track is unconfirmed' do + track = create(:track, program: conference.program) + allow(track).to receive(:confirmed?).and_return(false) + event = build(:event, program: conference.program, track: track) + expect(event.valid?).to eq false + expect(event.errors[:track]).to eq ['is invalid'] + end + end + end + end + + describe '#comments_count' do + context 'has a valid counter cache' do + before do + create(:comment, commentable: event) + end + + it 'successfully increments comments_count' do + expected = expect do + create(:comment, commentable: event) + end + expected.to change { event.comments_count }.by(1) + end + + it 'successfully decrements comments_count' do + expected = expect do + event.comment_threads.last.destroy + event.reload + end + expected.to change { event.comments_count }.by(-1) + end + end end describe 'scope ' do @@ -325,4 +375,30 @@ describe Event do expect(other_event.week).to eq 48 end end + + describe '#selected_schedule_id' do + before :each do + conference.program.selected_schedule = create(:schedule, program: conference.program) + end + + context 'returns the program\'s selected_schedule_id' do + it 'when it doesn\'t have a track' do + create(:event_schedule, event: event, schedule: conference.program.selected_schedule) + expect(event.send(:selected_schedule_id)).to eq conference.program.selected_schedule_id + end + + it 'when it belongs to a regular track' do + event.track = create(:track, program: conference.program) + expect(event.send(:selected_schedule_id)).to eq conference.program.selected_schedule_id + end + end + + context 'returns the track\'s selected_schedule_id' do + it 'when it belongs to a self-organized track' do + event.track = create(:track, :self_organized, program: conference.program, state: 'confirmed') + event.track.selected_schedule = create(:schedule, program: conference.program, track: event.track) + expect(event.send(:selected_schedule_id)).to eq event.track.selected_schedule_id + end + end + end end diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb new file mode 100644 index 00000000..ca84f532 --- /dev/null +++ b/spec/models/organization_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe Organization do + let(:organization) { create(:organization) } + + describe 'validation' do + it 'is not valid without a name' do + should validate_presence_of(:name) + end + end + + describe 'associations' do + it { should have_many(:conferences).dependent(:destroy) } + end +end diff --git a/spec/models/physical_ticket_spec.rb b/spec/models/physical_ticket_spec.rb new file mode 100644 index 00000000..9910a4a3 --- /dev/null +++ b/spec/models/physical_ticket_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe PhysicalTicket do + + describe 'association' do + it { is_expected.to belong_to :ticket_purchase } + end + + describe 'validations' do + it 'has a valid factory' do + expect(build(:physical_ticket)).to be_valid + end + end +end diff --git a/spec/models/program_spec.rb b/spec/models/program_spec.rb index ac720def..1c8eabd9 100644 --- a/spec/models/program_spec.rb +++ b/spec/models/program_spec.rb @@ -7,7 +7,7 @@ describe Program do describe 'association' do it { is_expected.to belong_to :conference } - it { is_expected.to have_one(:cfp).dependent(:destroy) } + it { is_expected.to have_many(:cfps).dependent(:destroy) } it { is_expected.to have_many(:schedules).dependent(:destroy) } it { is_expected.to have_many(:event_types).dependent(:destroy) } it { is_expected.to have_many(:tracks).dependent(:destroy) } @@ -141,7 +141,7 @@ describe Program do describe '#cfp_open?' do describe 'returns true' do it 'when there is an open Call for Papers for the conference' do - create(:cfp, start_date: Date.today - 2, end_date: Date.today, program_id: program.id) + create(:cfp, start_date: Date.current - 2, end_date: Date.current, program_id: program.id) expect(program.cfp_open?).to be true end end @@ -152,7 +152,7 @@ describe Program do end it 'when the Call for Papers period is over' do - build(:cfp, start_date: Date.today - 2, end_date: Date.today - 1, program_id: program.id) + create(:cfp, start_date: Date.current - 2, end_date: Date.current - 1, program_id: program.id) expect(program.cfp_open?).to be false end end @@ -240,4 +240,47 @@ describe Program do end end + describe '#cfp' do + it 'returns the cfp for events' do + create(:cfp, cfp_type: 'events', program: program, end_date: Date.current + 1) + expect(program.cfp).to be_a Cfp + expect(program.cfp.cfp_type).to eq('events') + end + + it 'returns nil if the program doesn\'t have a cfp' do + expect(program.cfp).to eq(nil) + end + end + + describe '#remaining_cfp_types' do + it 'returns an array without the \'events\' type, when the cfp for events exists' do + create(:cfp, cfp_type: 'events', program: program) + expect(program.remaining_cfp_types).to be_a Array + expect(program.remaining_cfp_types.include?('events')).to eq false + end + + it 'returns an array without the \'booths\' type, when the cfp for booths exists' do + create(:cfp, cfp_type: 'booths', program: program) + expect(program.remaining_cfp_types).to be_a Array + expect(program.remaining_cfp_types.include?('booths')).to eq false + end + + it 'returns an array without the \'tracks\' type, when the cfp for tracks exists' do + create(:cfp, cfp_type: 'tracks', program: program) + expect(program.remaining_cfp_types).to be_a Array + expect(program.remaining_cfp_types.include?('tracks')).to eq false + end + + it 'returns an empty array when cfps for all the types exist' do + create(:cfp, cfp_type: 'events', program: program) + create(:cfp, cfp_type: 'booths', program: program) + create(:cfp, cfp_type: 'tracks', program: program) + expect(program.remaining_cfp_types).to eq([]) + end + + it 'returns all the possible cfp types when there is no cfp' do + expect(program.remaining_cfp_types).to eq(Cfp::TYPES) + expect(program.remaining_cfp_types). to eq(%w[events booths tracks]) + end + end end diff --git a/spec/models/registration_period_spec.rb b/spec/models/registration_period_spec.rb index ef28bab1..52fb3755 100644 --- a/spec/models/registration_period_spec.rb +++ b/spec/models/registration_period_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe RegistrationPeriod do let!(:conference) { create(:conference, start_date: Date.today, end_date: Date.today + 6) } + let!(:registration_ticket) { create(:registration_ticket, conference: conference) } let!(:registration_period) { create(:registration_period, start_date: Date.today - 2, end_date: Date.today - 1, conference: conference) } describe 'validations' do diff --git a/spec/models/registration_spec.rb b/spec/models/registration_spec.rb index 772ccda1..e7824c08 100644 --- a/spec/models/registration_spec.rb +++ b/spec/models/registration_spec.rb @@ -20,7 +20,7 @@ describe 'Registration' do describe 'registration_limit_not_exceed' do it 'is not valid when limit exceeded' do conference.registration_limit = 1 - expect { create(:registration, conference: conference, user: user) }.to raise_error + expect { create(:registration, conference: conference, user: user) }.to raise_error('Validation failed: User already Registered!, Registration limit exceeded') expect(user.registrations.size).to be 1 end end @@ -29,7 +29,6 @@ describe 'Registration' do describe 'association' do it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:conference) } - it { is_expected.to have_and_belong_to_many(:events) } it { is_expected.to have_and_belong_to_many(:qanswers) } it { is_expected.to have_and_belong_to_many(:vchoices) } it { is_expected.to have_many(:events_registrations) } diff --git a/spec/models/room_spec.rb b/spec/models/room_spec.rb index 469fb117..f135f91d 100644 --- a/spec/models/room_spec.rb +++ b/spec/models/room_spec.rb @@ -12,6 +12,7 @@ describe Room do describe 'association' do it { should belong_to(:venue) } it { should have_many(:event_schedules).dependent(:destroy) } + it { should have_many(:tracks) } end describe 'callback' do diff --git a/spec/models/schedule_spec.rb b/spec/models/schedule_spec.rb index 6d1f7346..2dc1b826 100644 --- a/spec/models/schedule_spec.rb +++ b/spec/models/schedule_spec.rb @@ -4,6 +4,7 @@ describe Schedule do describe 'association' do it { should belong_to(:program) } + it { should belong_to(:track) } it { should have_many(:event_schedules).dependent(:destroy) } it { should have_many(:events).through(:event_schedules) } end diff --git a/spec/models/ticket_purchase_spec.rb b/spec/models/ticket_purchase_spec.rb index 0bb33f8e..bfca5efc 100644 --- a/spec/models/ticket_purchase_spec.rb +++ b/spec/models/ticket_purchase_spec.rb @@ -35,6 +35,21 @@ describe TicketPurchase do should allow_value(1).for(:quantity) end + describe 'one_registration_ticket_per_user' do + let(:registration_ticket) { create(:registration_ticket) } + let(:ticket_purchase) { build(:ticket_purchase, ticket: registration_ticket, quantity: 1) } + + it 'it is valid, if quantity for registration tickets is less than or equal to one' do + expect(ticket_purchase.valid?).to eq true + end + + it 'it is not valid, if quantity for registration tickets is greater than to one' do + ticket_purchase.quantity = 4 + + expect(ticket_purchase.valid?).to eq false + expect(ticket_purchase.errors[:quantity]).to eq ['cannot be greater than one for registration tickets.'] + end + end end describe 'self#purchase' do @@ -54,7 +69,6 @@ describe TicketPurchase do expect(TicketPurchase.count).to eq(1) expect(purchase.quantity).to eq(10) expect(message.blank?).to be true - end it 'creates a purchase for one ticket' do diff --git a/spec/models/ticket_scanning_spec.rb b/spec/models/ticket_scanning_spec.rb new file mode 100644 index 00000000..3d978f4f --- /dev/null +++ b/spec/models/ticket_scanning_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe TicketScanning do + let(:conference) { create(:conference) } + let(:user) { create(:user) } + let(:registration) { create(:registration, conference: conference, user: user) } + let(:registration_ticket) { create(:registration_ticket, conference: conference) } + let(:paid_ticket_purchase) { create(:ticket_purchase, conference: conference, user: user, ticket: registration_ticket, quantity: 1) } + let(:physical_ticket) { create(:physical_ticket, ticket_purchase: paid_ticket_purchase) } + let(:ticket_scanning) { create(:ticket_scanning, physical_ticket: physical_ticket) } + + describe 'before_create' do + it 'marks user as present' do + expect(registration.attended).to eq(false) + ticket_scanning + registration.reload + expect(registration.attended).to eq(true) + end + end +end diff --git a/spec/models/track_spec.rb b/spec/models/track_spec.rb new file mode 100644 index 00000000..8f53c732 --- /dev/null +++ b/spec/models/track_spec.rb @@ -0,0 +1,551 @@ +require 'spec_helper' + +describe Track do + subject { create(:track) } + let(:track) { create(:track) } + let(:self_organized_track) { create(:track, :self_organized) } + + describe 'association' do + it { is_expected.to belong_to(:program) } + it { is_expected.to belong_to(:submitter).class_name('User') } + it { is_expected.to belong_to(:room) } + it { is_expected.to belong_to(:selected_schedule).class_name('Schedule') } + it { is_expected.to have_many(:events) } + it { is_expected.to have_many(:schedules) } + end + + describe 'validation' do + it 'has a valid factory' do + expect(build(:track)).to be_valid + end + + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to allow_value('#ABCDEF').for(:color) } + it { is_expected.to allow_value('#124689').for(:color) } + it { is_expected.to validate_presence_of(:short_name) } + it { is_expected.to allow_value('My_track_name').for(:short_name) } + it { is_expected.to_not allow_value('My track name').for(:short_name) } + it { is_expected.to validate_uniqueness_of(:short_name).scoped_to(:program_id) } + it { is_expected.to validate_presence_of(:state) } + it { is_expected.to validate_inclusion_of(:state).in_array(%w[new to_accept accepted confirmed to_reject rejected canceled withdrawn]) } + it { is_expected.to validate_inclusion_of(:cfp_active).in_array([true, false]) } + + context 'when self_organized_and_accepted_or_confirmed? returns true' do + before :each do + allow(subject).to receive(:self_organized_and_accepted_or_confirmed?).and_return(true) + end + + it { is_expected.to validate_presence_of(:start_date) } + it { is_expected.to validate_presence_of(:end_date) } + it { is_expected.to validate_presence_of(:room) } + end + + context 'when self_organized_and_accepted_or_confirmed? returns false' do + before :each do + allow(subject).to receive(:self_organized_and_accepted_or_confirmed?).and_return(false) + end + + it { is_expected.to_not validate_presence_of(:start_date) } + it { is_expected.to_not validate_presence_of(:end_date) } + it { is_expected.to_not validate_presence_of(:room) } + end + + context 'when self_organized? returns true' do + before :each do + allow(subject).to receive(:self_organized?).and_return(true) + end + + it { is_expected.to validate_presence_of(:relevance) } + it { is_expected.to validate_presence_of(:description) } + end + + context 'when self_organized? returns false' do + before :each do + allow(subject).to receive(:self_organized?).and_return(false) + end + + it { is_expected.to_not validate_presence_of(:relevance) } + it { is_expected.to_not validate_presence_of(:description) } + end + + describe '#dates_within_conference_dates' do + before :each do + @conference = create(:conference, start_date: 1.day.ago, end_date: 2.days.from_now) + end + + context 'is valid' do + it 'when the track\'s dates are between the conference\'s dates' do + track = build(:track, start_date: @conference.start_date, end_date: @conference.end_date, program: @conference.program) + expect(track.valid?).to eq true + end + end + + context 'is invalid' do + it 'when the track\'s start date is before the conference\'s start date' do + track = build(:track, start_date: 2.days.ago, end_date: Date.tomorrow, program: @conference.program) + expect(track.valid?).to eq false + expect(track.errors[:start_date]).to eq ["can't be outside of the conference's dates (#{1.day.ago.to_date}-#{2.days.from_now.to_date})"] + end + + it 'when the track\'s start date is after the conference\'s end date' do + track = build(:track, start_date: 3.days.from_now, end_date: 4.days.from_now, program: @conference.program) + expect(track.valid?).to eq false + expect(track.errors[:start_date]).to eq ["can't be outside of the conference's dates (#{1.day.ago.to_date}-#{2.days.from_now.to_date})"] + end + + it 'when the track\'s end date is before the conference\'s start date' do + track = build(:track, start_date: 3.days.ago, end_date: 2.days.ago, program: @conference.program) + expect(track.valid?).to eq false + expect(track.errors[:end_date]).to eq ["can't be outside of the conference's dates (#{1.day.ago.to_date}-#{2.days.from_now.to_date})"] + end + + it 'when the track\'s end date is after the conference\'s end date' do + track = build(:track, start_date: Date.today, end_date: 3.days.from_now, program: @conference.program) + expect(track.valid?).to eq false + expect(track.errors[:end_date]).to eq ["can't be outside of the conference's dates (#{1.day.ago.to_date}-#{2.days.from_now.to_date})"] + end + end + end + + describe '#start_date_before_end_date' do + before :each do + @conference = create(:conference, start_date: 1.day.ago, end_date: 2.days.from_now) + end + + context 'is valid' do + it 'when the track\'s start date is before its end date' do + track = build(:track, start_date: Date.today, end_date: Date.tomorrow, program: @conference.program) + expect(track.valid?).to eq true + end + end + + context 'is invalid' do + it 'when the track\'s start date is after it\'s end date' do + track = build(:track, start_date: 1.day.from_now, end_date: 1.day.ago) + expect(track.valid?).to eq false + expect(track.errors[:start_date]).to eq ['can\'t be after the end date'] + end + end + end + + describe '#valid_room' do + before :each do + @conference = create(:conference) + @conference.venue = create(:venue, name: 'The venue') + end + + context 'is valid' do + it 'when the track\'s room belongs to the venue of the conference' do + room = create(:room, venue: @conference.venue) + track = build(:track, :self_organized, state: 'accepted', program: @conference.program, room: room) + expect(track.valid?).to eq true + end + end + + context 'is invalid' do + it 'when the track\'s room doesn\'t belong to the venue of the track\'s conference' do + other_conference = create(:conference) + other_conference.venue = create(:venue) + room = create(:room, venue: other_conference.venue) + track = build(:track, :self_organized, state: 'accepted', program: @conference.program, room: room) + expect(track.valid?).to eq false + expect(track.errors[:room]).to eq ['must be a room of The venue'] + end + end + end + + describe '#overlapping' do + before :each do + @conference = create(:conference, start_date: Date.current - 1.day, end_date: Date.current + 2.days) + @conference.venue = create(:venue) + @room = create(:room, venue: @conference.venue) + end + + context 'is valid' do + it 'when the tracks are in different rooms at the same time' do + other_room = create(:room, venue: @conference.venue) + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: other_room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + expect(track.valid?).to eq true + end + + it 'when it ends before the other tracks in the same room' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current - 1.day, end_date: Date.current - 1.day) + expect(track.valid?).to eq true + end + + it 'when it starts after the other tracks in the same room' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current + 1.day, end_date: Date.current + 1.day) + expect(track.valid?).to eq true + end + end + + context 'is invalid' do + it 'when it starts and/or ends with another track in the same room' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + + it 'when it starts before another track and ends after the other starts and before it ends' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current + 2.days) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current - 1.day, end_date: Date.current + 1.day) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + + it 'when it starts after another track and before it ends and ends after the other' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current + 2.days) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current + 1.day, end_date: Date.current + 3.days) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + + it 'when it starts after another track and ends before the other' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current + 2.days) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current + 1.day, end_date: Date.current + 1.day) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + + it 'when it starts before another track and ends after the other' do + create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) + track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current - 1.day, end_date: Date.current + 1.day) + expect(track.valid?).to eq false + expect(track.errors[:track]).to eq ['has overlapping dates with a confirmed or accepted track in the same room'] + end + end + end + end + + describe 'scope' do + describe '#accepted' do + before :each do + @program = create(:program) + end + + context 'includes' do + it 'when track is accepted' do + accepted_track = create(:track, state: 'accepted', program: @program) + expect(@program.tracks.accepted.include?(accepted_track)).to eq true + end + end + + context 'excludes' do + %w[new to_accept confirmed to_reject rejected canceled withdrawn].each do |state| + it "when track is #{state.humanize}" do + not_accepted_track = create(:track, state: state, program: @program) + expect(@program.tracks.accepted.include?(not_accepted_track)).to eq false + end + end + end + end + + describe '#confirmed' do + before :each do + @program = create(:program) + end + + context 'includes' do + it 'tracks with state \'confirmed\'' do + confirmed_track = create(:track, state: 'confirmed', program: @program) + expect(@program.tracks.confirmed.include?(confirmed_track)).to eq true + end + end + + context 'excludes' do + %w[new to_accept accepted to_reject rejected canceled withdrawn].each do |state| + it "tracks with state '#{state}'" do + unconfirmed_track = create(:track, state: state, program: @program) + expect(@program.tracks.confirmed.include?(unconfirmed_track)).to eq false + end + end + end + end + + describe '#cfp_active' do + before :each do + @program = create(:program) + @cfp_active_track = create(:track, cfp_active: true, program: @program) + @non_cfp_active_track = create(:track, cfp_active: false, program: @program) + end + + it 'include tracks with the cfp_active flag enabled' do + expect(@program.tracks.cfp_active.include?(@cfp_active_track)).to eq true + end + + it 'excludes tracks with the cfp_active flag disabled' do + expect(@program.tracks.cfp_active.include?(@non_cfp_active_track)).to eq false + end + end + + describe '#self_organized' do + before :each do + @program = create(:program) + track.program = @program + track.save! + self_organized_track.program = @program + self_organized_track.save! + end + + it 'includes self-organized tracks' do + expect(@program.tracks.self_organized.include?(self_organized_track)).to eq true + end + + it 'excludes regular tracks' do + expect(@program.tracks.self_organized.include?(track)).to eq false + end + end + end + + describe '#self_organized?' do + it 'returns true when it has a submitter' do + expect(self_organized_track.submitter).to be_a User + expect(self_organized_track.self_organized?).to eq true + end + + it 'returns false when it doesn\'t have a submitter' do + expect(track.submitter).to eq nil + expect(track.self_organized?).to eq false + end + end + + describe '#transition_possible?' do + shared_examples 'transition_possible?' do |state, transition, expected| + it "returns #{expected} for #{transition} event, when the track's state is #{state}}" do + my_self_organized_track = create(:track, :self_organized, state: state) + expect(my_self_organized_track.transition_possible?(transition.to_sym)).to eq expected + end + end + + states = [:new, :to_accept, :accepted, :confirmed, :to_reject, :rejected, :canceled, :withdrawn] + transitions = [:restart, :to_accept, :accept, :confirm, :to_reject, :reject, :cancel, :withdraw] + + states_transitions = { new: { restart: false, to_accept: true, accept: true, confirm: false, to_reject: true, reject: true, cancel: false, withdraw: true }, + to_accept: { restart: false, to_accept: false, accept: true, confirm: false, to_reject: true, reject: false, cancel: true, withdraw: true }, + accepted: { restart: false, to_accept: false, accept: false, confirm: true, to_reject: false, reject: false, cancel: true, withdraw: true }, + confirmed: { restart: false, to_accept: false, accept: false, confirm: false, to_reject: false, reject: false, cancel: true, withdraw: true }, + to_reject: { restart: false, to_accept: true, accept: false, confirm: false, to_reject: false, reject: true, cancel: true, withdraw: true }, + rejected: { restart: true, to_accept: false, accept: false, confirm: false, to_reject: false, reject: false, cancel: false, withdraw: false }, + canceled: { restart: true, to_accept: false, accept: false, confirm: false, to_reject: false, reject: false, cancel: false, withdraw: false }, + withdrawn: { restart: true, to_accept: false, accept: false, confirm: false, to_reject: false, reject: false, cancel: false, withdraw: false } } + + states.each do |state| + transitions.each do |transition| + it_behaves_like 'transition_possible?', state, transition, states_transitions[state.to_sym][transition.to_sym] + end + end + end + + describe '#assign_role_to_submitter' do + before :each do + Role.where(name: 'track_organizer', resource: self_organized_track).first_or_create + @submitter = self_organized_track.submitter + end + + it 'gives the role of the track organizer to the submitter of the track' do + expect(@submitter.has_role?(:track_organizer, self_organized_track)).to eq false + self_organized_track.assign_role_to_submitter + expect(@submitter.has_role?(:track_organizer, self_organized_track)).to eq true + end + + it 'is executed when the track is confirmed' do + self_organized_track.state = 'accepted' + self_organized_track.save! + expect(@submitter.has_role?(:track_organizer, self_organized_track)).to eq false + self_organized_track.confirm + expect(@submitter.has_role?(:track_organizer, self_organized_track)).to eq true + end + end + + describe '#revoke_role_and_cleanup' do + before :each do + Role.where(name: 'track_organizer', resource: self_organized_track).first_or_create + @a_track_organizer = create(:user) + self_organized_track.state = 'confirmed' + self_organized_track.cfp_active = true + self_organized_track.save! + @a_track_organizer.add_role 'track_organizer', self_organized_track + @event_of_self_organized_track = create(:event, program: self_organized_track.program, track: self_organized_track, state: 'confirmed') + @schedule_of_self_organized_track = create(:schedule, program: self_organized_track.program, track: self_organized_track) + end + + it 'revokes the role of the track organizer' do + expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq true + self_organized_track.revoke_role_and_cleanup + expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false + end + + it 'destroys the track\'s schedules' do + expect(Schedule.find(@schedule_of_self_organized_track.id)).to eq @schedule_of_self_organized_track + self_organized_track.revoke_role_and_cleanup + expect(Schedule.find_by(id: @schedule_of_self_organized_track.id)).to eq nil + end + + it 'removes the track from the events that have it set' do + expect(@event_of_self_organized_track.track).to eq self_organized_track + self_organized_track.revoke_role_and_cleanup + @event_of_self_organized_track.reload + expect(@event_of_self_organized_track.track).to eq nil + end + + it 'sets the state of the track\'s events to new' do + expect(@event_of_self_organized_track.state).to eq 'confirmed' + self_organized_track.revoke_role_and_cleanup + @event_of_self_organized_track.reload + expect(@event_of_self_organized_track.state).to eq 'new' + end + + it 'is executed when the track is canceled' do + self_organized_track.state = 'confirmed' + self_organized_track.save! + self_organized_track.cancel + expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false + @event_of_self_organized_track.reload + expect(@event_of_self_organized_track.track).to eq nil + end + + it 'is executed when the track is withdrawn' do + self_organized_track.withdraw + expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false + @event_of_self_organized_track.reload + expect(@event_of_self_organized_track.track).to eq nil + end + end + + describe '#accepted?' do + context 'returns true' do + it 'when the state is "accepted"' do + self_organized_track.state = 'accepted' + self_organized_track.save! + expect(self_organized_track.accepted?).to eq true + end + end + + context 'returns false' do + %w[new to_accept confirmed to_reject rejected canceled withdrawn].each do |state| + it "when the state is \"#{state}\"" do + self_organized_track.state = state + self_organized_track.save! + expect(self_organized_track.accepted?).to eq false + end + end + end + end + + describe '#confirmed?' do + context 'returns true' do + it 'when the state is "confirmed"' do + self_organized_track.state = 'confirmed' + self_organized_track.save! + expect(self_organized_track.confirmed?).to eq true + end + end + + context 'returns false' do + %w[new to_accept accepted to_reject rejected canceled withdrawn].each do |state| + it "when the state is \"#{state}\"" do + self_organized_track.state = state + self_organized_track.save! + expect(self_organized_track.confirmed?).to eq false + end + end + end + end + + # accepted? and confirmed? are mutually exclusive (they can't be both true) + describe '#self_organized_and_accepted_or_confirmed?' do + context 'returns true' do + context 'when self_organized? returns true' do + before :each do + allow(track).to receive(:self_organized?).and_return(true) + end + + context 'accepted? returns true and confirmed? returns false' do + before :each do + allow(track).to receive(:accepted?).and_return(true) + allow(track).to receive(:confirmed?).and_return(false) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq true } + end + + context 'accepted? returns false and confirmed? returns true' do + before :each do + allow(track).to receive(:accepted?).and_return(false) + allow(track).to receive(:confirmed?).and_return(true) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq true } + end + end + end + + context 'returns false' do + context 'when self_organized? returns true' do + before :each do + allow(track).to receive(:self_organized?).and_return(true) + end + + context 'accepted? returns false and confirmed? returns false' do + before :each do + allow(track).to receive(:accepted?).and_return(false) + allow(track).to receive(:confirmed?).and_return(false) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq false } + end + end + + context 'when self_organized? returns false' do + before :each do + allow(track).to receive(:self_organized?).and_return(false) + end + + context 'accepted? returns false and confirmed? returns false' do + before :each do + allow(track).to receive(:accepted?).and_return(false) + allow(track).to receive(:confirmed?).and_return(false) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq false } + end + + context 'accepted? returns true and confirmed? returns false' do + before :each do + allow(track).to receive(:accepted?).and_return(true) + allow(track).to receive(:confirmed?).and_return(false) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq false } + end + + context 'accepted? returns false and confirmed? returns true' do + before :each do + allow(track).to receive(:accepted?).and_return(false) + allow(track).to receive(:confirmed?).and_return(true) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq false } + end + end + end + end + + describe '#create_organizer_role' do + it 'creates the role of the track organizer' do + expect(Role.find_by(name: 'track_organizer', resource: self_organized_track)).to eq nil + self_organized_track.send(:create_organizer_role) + expect(Role.find_by(name: 'track_organizer', resource: self_organized_track).description).to eq 'For the organizers of the Track' + end + + it 'is executed when the track is accepted' do + expect(Role.find_by(name: 'track_organizer', resource: self_organized_track)).to eq nil + self_organized_track.accept + expect(Role.find_by(name: 'track_organizer', resource: self_organized_track).description).to eq 'For the organizers of the Track' + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d0517212..e742b993 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -282,7 +282,7 @@ describe User do it 'returns hash of role and conference' do expected_hash = { - 'organizer' => ['oSC16', 'oSC15'], + 'organizer' => %w[oSC16 oSC15], 'cfp' => ['oSC16'] } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index fc56e829..d9283117 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -59,7 +59,7 @@ RSpec.configure do |config| Capybara.javascript_driver = :poltergeist Capybara.register_driver :poltergeist do |app| - Capybara::Poltergeist::Driver.new(app, phantomjs: Phantomjs.path, js_errors: false) + Capybara::Poltergeist::Driver.new(app, phantomjs: Phantomjs.path, js_errors: false, window_size: [1920, 1080]) end # Includes helpers and connect them to specific types of tests @@ -82,6 +82,14 @@ RSpec.configure do |config| # Types of tests (controller, feature, model) will # be inferred from subfolder name config.infer_spec_type_from_file_location! + + # Enable this if you like to see what you're debugging + # config.after(:example) do |example| + # if example.exception + # save_and_open_screenshot + # save_and_open_page + # end + # end end OmniAuth.config.test_mode = true diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index 3e6c8404..25bf528a 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -1,6 +1,16 @@ RSpec.configure do |config| - config.before(:each) do + config.before(:suite) do DatabaseCleaner.clean_with(:truncation) Rails.application.load_seed end + + config.before(:each) do |example| + DatabaseCleaner.strategy = example.metadata[:js] == true ? :truncation : :transaction + DatabaseCleaner.start + end + + config.after(:each) do |example| + DatabaseCleaner.clean + Rails.application.load_seed if example.metadata[:js] == true + end end diff --git a/spec/support/factory_girl.rb b/spec/support/factory_girl.rb index c46a5105..d745aece 100644 --- a/spec/support/factory_girl.rb +++ b/spec/support/factory_girl.rb @@ -4,9 +4,15 @@ RSpec.configure do |config| config.before(:suite) do if ENV['OSEM_FACTORY_LINT'] != 'false' - mock_commercial_request - FactoryGirl.lint + DatabaseCleaner.strategy = :transaction + DatabaseCleaner.clean_with(:truncation) + begin + DatabaseCleaner.start + mock_commercial_request + FactoryGirl.lint + ensure + DatabaseCleaner.clean + end end end - end diff --git a/spec/support/save_feature_failures.rb b/spec/support/save_feature_failures.rb index fe6edf66..2e0d33a9 100644 --- a/spec/support/save_feature_failures.rb +++ b/spec/support/save_feature_failures.rb @@ -5,7 +5,7 @@ RSpec.configure do |config| config.after(:each, type: :feature) do example_filename = RSpec.current_example.full_description example_filename = example_filename.tr(' ', '_') - example_filename = example_filename + '.html' + example_filename += '.html' example_filename = File.expand_path(example_filename, Capybara.save_and_open_page_path) if RSpec.current_example.exception.present? save_page(example_filename) diff --git a/travis_script.sh b/travis_script.sh new file mode 100755 index 00000000..77ca2329 --- /dev/null +++ b/travis_script.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# This script runs the test suites for the CI build + +# Be verbose and fail script on the first error +set -xe + +# By default: all test runs +if [ -z $1 ]; then + TEST_SUITE="all" +else + TEST_SUITE="$1" +fi + +case $TEST_SUITE in + linters) + bundle exec rubocop -Dc .rubocop.yml + bundle exec haml-lint app/views + ;; + rspec) + bundle exec rspec --color --format documentation + ;; + *) + bundle exec rubocop -Dc .rubocop.yml + bundle exec haml-lint app/views + bundle exec rspec --color --format documentation + ;; +esac