diff --git a/.codeclimate.yml b/.codeclimate.yml
new file mode 100644
index 000000000..aacf2e734
--- /dev/null
+++ b/.codeclimate.yml
@@ -0,0 +1,6 @@
+version: "2"
+plugins:
+ rubocop:
+ enabled: true
+ channel: rubocop-1-56-3
+
diff --git a/.codecov.yml b/.codecov.yml
deleted file mode 100644
index db2472009..000000000
--- a/.codecov.yml
+++ /dev/null
@@ -1 +0,0 @@
-comment: off
diff --git a/.flayignore b/.flayignore
new file mode 100644
index 000000000..17bc5d29f
--- /dev/null
+++ b/.flayignore
@@ -0,0 +1,4 @@
+spec/**/*.rb
+features/step_definitions/*.rb
+features/support/*.rb
+lib/**/*.rake
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 501ce4df0..b82f589c4 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,19 +1,31 @@
-**Checklist**
+### [Pivotal Tracker Link][tracker]
-- [ ] I have read the [Contribution & Best practices Guide](https://github.com/openSUSE/osem/blob/master/CONTRIBUTING.md).
-- [ ] My branch is up-to-date with the upstream `master` branch.
-- [ ] The tests pass locally with my changes.
-- [ ] I have added tests that prove my fix is effective or that my feature works(if appropriate).
-- [ ] I have added necessary documentation (if appropriate).
+
+[tracker]: https://www.pivotaltracker.com/story/show/your-story-id
-**Short description of what this resolves/which [issues](https://github.com/openSUSE/osem/issues) does this fix?:**
+## What this PR does:
+
-
+This pull request fixes|implements (pick one...) ______.
--
+### Include screenshots, videos, etc.
-**Changes proposed in this pull request:**
+#### Who authored this PR?
+
-
--
+### How should this PR be tested?
+
+* Is there a deploy we can view?
+* What do the specs/features test?
+* Are there edge cases to watch out for?
+
+#### Are there any complications to deploying this?
+
+
+
+### Checklist:
+
+- [ ] Has this been deployed to a staging environment or reviewed by a customer?
+- [ ] Tag someone for code review (either a coach / team member)
+- [ ] I have renamed the branch to match PivotTracker's suggested one (necessary for BlueJay)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000..de025eadd
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,67 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ main ]
+ schedule:
+ - cron: '39 5 * * 6'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'javascript' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
+ # Learn more:
+ # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # âšī¸ Command-line programs to run using the OS shell.
+ # đ https://git.io/JvXDl
+
+ # âī¸ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/next-rails.yml b/.github/workflows/next-rails.yml
index 43f03260b..187eb2ed1 100644
--- a/.github/workflows/next-rails.yml
+++ b/.github/workflows/next-rails.yml
@@ -3,7 +3,7 @@ name: Next-rails
on:
pull_request:
branches:
- - master
+ - main
workflow_dispatch:
jobs:
@@ -26,7 +26,7 @@ jobs:
echo "BUNDLE_CACHE_PATH=vendor/cache.next" >> $GITHUB_ENV
- uses: ruby/setup-ruby@v1
with:
- ruby-version: 3.1.4
+ ruby-version: 3.2.2
bundler-cache: true
- name: Prepare spec
run: |
diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml
index 7da756c6a..4c7af5acf 100644
--- a/.github/workflows/spec.yml
+++ b/.github/workflows/spec.yml
@@ -2,51 +2,85 @@ name: Specs
on:
push:
- branches:
- - master
pull_request:
- branches:
- - master
workflow_dispatch:
jobs:
linters:
+ continue-on-error: true
runs-on: ubuntu-latest
+ env:
+ PRONTO_PULL_REQUEST_ID: ${{ github.event.pull_request.number }}
+ PRONTO_GITHUB_ACCESS_TOKEN: "${{ github.token }}"
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
- ruby-version: 3.1.4
+ ruby-version: 3.2.2
bundler-cache: true
+ # - name: Run Pronto
+ # run: bundle exec pronto run
+ # run: pronto run -f github_combined_status github_pr_review -c origin/${{ github.base_ref }}
- run: bundle exec rubocop
- run: bundle exec haml-lint app/views
spec:
- needs: linters
+ continue-on-error: true
runs-on: ubuntu-latest
name: spec
env:
OSEM_DB_ADAPTER: sqlite3
RAILS_ENV: test
+ CCTR: ./cc-test-reporter
+ CCTR_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
strategy:
matrix:
- suite: [models, features, controllers, ability, leftovers]
+ suite: [models, features, controllers, ability, leftovers, cucumber]
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
- ruby-version: 3.1.4
+ ruby-version: 3.2.2
bundler-cache: true
+ - name: Use Node.js
+ uses: actions/setup-node@v1
+ with:
+ node-version: '16.x'
+ - run: sudo apt-get install xvfb
+ - name: Install JavaScript libraries via npm
+ run: npm install
+ - name: set up CodeClimate test-reporter
+ run: |
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > $CCTR
+ chmod +x $CCTR
+ $CCTR before-build
- name: Prepare spec
run: |
rm -f osem_test osem_development
bundle exec rake db:setup --trace
bundle exec bin/rails webdrivers:chromedriver:update
bundle exec rake factory_bot:lint RAILS_ENV=test
+ # TODO: Not all suites need xvfb
- name: spec/${{ matrix.suite }}
- run: bundle exec rake spec:${{ matrix.suite }}
- - name: coverage upload ${{ matrix.suite }}
- uses: codacy/codacy-coverage-reporter-action@master
- if: github.ref == 'refs/heads/master'
+ run: |
+ xvfb-run --auto-servernum bundle exec rake spec:${{ matrix.suite }}
+ $CCTR format-coverage --output coverage/codeclimate.${{ matrix.suite }}.json --input-type simplecov
+ # - name: coverage upload ${{ matrix.suite }}
+ # uses: codacy/codacy-coverage-reporter-action@v1
+ # if: github.ref == 'refs/heads/master' && always()
+ # with:
+ # project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
+ # coverage-reports: coverage/coverage.xml
+ - name: Upload Capybara Failure Screenshots
+ uses: actions/upload-artifact@v3
+ if: always()
with:
- project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
- coverage-reports: coverage/coverage.xml
+ name: capybara-screenshots
+ path: tmp/capybara/
+ retention-days: 7
+
+ - name: Publish code coverage
+ run: |
+ export GIT_BRANCH="${GITHUB_REF/refs\/heads\//}"
+ $CCTR sum-coverage coverage/codeclimate.*.json
+ $CCTR upload-coverage --id "6d21ff1a59b134f3741779d50325f7bd5183cbe6b205051573d955705148960f"
+ $CCTR after-build --id "6d21ff1a59b134f3741779d50325f7bd5183cbe6b205051573d955705148960f"
diff --git a/.gitignore b/.gitignore
index 3d1ffb4d5..551ac3e65 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,27 @@
+# Global, for Macs
+*~
+
+# Compiled Python files
+*.pyc
+
+# Folder view configuration files
+.DS_Store
+Desktop.ini
+
+# Thumbnail cache files
+._*
+Thumbs.db
+
+# Files that might appear on external disks
+.Spotlight-V100
+.Trashes
+
+
+# Legacy from before Feb 2021
/db/test.sqlite3-journal
config/application.rb
config/config.yml
+config/local_env.yml
config/secrets.yml
config/master.key
config/credentials.yml.enc
@@ -25,22 +46,88 @@ capybara-*.html
**.orig
rerun.txt
pickle-email-*.html
-*~
/public/assets
/bundle
/doc/app
.vagrant/
-.env
-.env.production
-.env.development
-.env.test
-.env.local
-.envrc
+.env*
docker-compose.override.yml
-.DS_Store
+
.byebug_history
.buildconfig
osem_development
osem_test
+
+# From GitHub, for Ruby
+# https://github.com/github/gitignore/blob/master/Ruby.gitignore
+*.gem
+*.rbc
+/.config
+/coverage/
+/InstalledFiles
+/pkg/
+/spec/reports/
+/spec/examples.txt
+/test/tmp/
+/test/version_tmp/
+
+# ensure the folder exists for puma
+!tmp/pids/.keep
+
+# Ignore Byebug command history file.
+.byebug_history
+
+## Specific to RubyMotion:
+.dat*
+.repl_history
+build/
+*.bridgesupport
+build-iPhoneOS/
+build-iPhoneSimulator/
+
+## Specific to RubyMotion (use of CocoaPods):
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# vendor/Pods/
+
+## Documentation cache and generated files:
+/.yardoc/
+/_yardoc/
+/doc/
+/rdoc/
+
+## Environment normalization:
+/.bundle/
+/vendor/bundle
+/lib/bundler/man/
+
+# for a library or gem, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# Gemfile.lock
+# .ruby-version
+# .ruby-gemset
+
+# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
+.rvmrc
+
+# Used by RuboCop. Remote config files pulled in from inherit_from directive.
+.rubocop-https?--*
+
+# Ignore Javascript package binaries
+/node_modules
+
+# Elastic Beanstalk Files
+.elasticbeanstalk/*
+!.elasticbeanstalk/*.cfg.yml
+!.elasticbeanstalk/*.global.yml
+
+# common db things from heroku
+latest.dump*
.ackrc
spec/support/deprecation_shitlist.json
+.vs/*
+
+cc-test-reporter
diff --git a/.haml-lint.yml b/.haml-lint.yml
index 013b144fe..6a143078d 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -1 +1,5 @@
inherits_from: .haml-lint_todo.yml
+
+linters:
+ UnnecessaryStringOutput:
+ enabled: false
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
index 76ec92e79..7b80c2d84 100644
--- a/.haml-lint_todo.yml
+++ b/.haml-lint_todo.yml
@@ -12,15 +12,21 @@ linters:
RuboCop:
enabled: false
+ ConsecutiveComments:
+ exclude:
+ # These seems like a bug in haml-lint.
+ - "app/views/admin/conferences/show.html.haml"
+ - "app/views/proposals/index.html.haml"
+
# Offense count: 24
ConsecutiveSilentScripts:
+ max_consecutive: 3
exclude:
- "app/views/admin/booths/_change_state_dropdown.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: 981
LineLength:
@@ -111,26 +117,19 @@ linters:
SpaceBeforeScript:
enabled: false
- # Offense count: 1
- ImplicitDiv:
- exclude:
- - "app/views/admin/registrations/index.html.haml"
-
# Offense count: 9
MultilinePipe:
exclude:
- "app/views/admin/schedules/_day_tab.html.haml"
- "app/views/admin/schedules/_event.html.haml"
- - "app/views/schedules/_carousel.html.haml"
- "app/views/schedules/_event.html.haml"
- "app/views/schedules/_schedule_item.html.haml"
- # Offense count: 9
+ # Offense count: 4
ClassAttributeWithStaticValue:
exclude:
- "app/views/admin/survey_questions/_form.html.haml"
- "app/views/admin/surveys/_survey_question.html.haml"
- - "app/views/conferences/_gallery.html.haml"
- "app/views/layouts/_navigation.html.haml"
- "app/views/schedules/events.html.haml"
diff --git a/.rubocop.yml b/.rubocop.yml
index e6c0a8538..53babd565 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -12,22 +12,86 @@ inherit_mode:
- Exclude
AllCops:
+ SuggestExtensions: false
UseCache: true
CacheRootDirectory: tmp/rubocop_cache_rails_dir
MaxFilesInCache: 4000
+ NewCops: enable
Exclude:
- db/schema.rb
- NewCops: enable
+ - features/step_definitions/web_steps.rb
+ - features/support/selectors.rb
+ - lib/tasks/*.rake
+ - db/migrate/*.rb
+ - bin/*
#################### Style ###########################
+Style/CommentAnnotation:
+ Keywords:
+ - TODO-SNAPCON
+ - TODO
+ - OPTIMIZE
+ - HACK
+ - REVIEW
+
+Style/HashSyntax:
+ EnforcedShorthandSyntax: never
+
+Style/SymbolArray:
+ Enabled: false
+
Layout/HashAlignment:
EnforcedHashRocketStyle: table
EnforcedColonStyle: table
+Layout/EndOfLine:
+ EnforcedStyle: lf
+
+Lint/AmbiguousRegexpLiteral:
+ Exclude:
+ - features/step_definitions/*
+
##################### Metrics ##################################
##################### Rails ##################################
+# TODO: Would be good to enable, but leads to spec failures.
+Rails:
+ Enabled: false
+ Exclude:
+ - spec/**/*.rb
+
+# TODO: This would be good to enable at some point...specs need work.
+Rails/Date:
+ Enabled: false
+
+Rails/FilePath:
+ Enabled: false
+
+Rails/I18nLocaleTexts:
+ Enabled: false
+
+# TODO-SNAPCON: Investigate if that makes sense to enable.
+Rails/InverseOf:
+ Enabled: false
+
+Rails/HasAndBelongsToMany:
+ Enabled: false
+
+Rails/HasManyOrHasOneDependent:
+ Enabled: false
+
+Rails/OutputSafety:
+ Enabled: false
+
+Rails/RenderInline:
+ Enabled: false
+
+Rails/SkipsModelValidations:
+ Enabled: false
+
+Rails/UniqueValidationWithoutIndex:
+ Enabled: false
##################### RSpec ##################################
@@ -35,4 +99,3 @@ RSpec/DescribeClass:
Exclude:
- "spec/views/**/*"
- "spec/ability/*"
-
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 7e343ea1b..e32398240 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,6 +1,10 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
+<<<<<<< HEAD
# on 2024-03-04 16:04:49 UTC using RuboCop version 1.61.0.
+=======
+# on 2024-02-13 04:01:02 UTC using RuboCop version 1.60.2.
+>>>>>>> main
# 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
@@ -14,6 +18,7 @@ Bundler/OrderedGems:
Exclude:
- 'Gemfile'
+<<<<<<< HEAD
# Offense count: 161
# Configuration parameters: EnforcedStyle.
# SupportedStyles: link_or_button, strict
@@ -38,6 +43,9 @@ Capybara/CurrentPathExpectation:
- 'spec/features/user_ability_spec.rb'
# Offense count: 3
+=======
+# Offense count: 1
+>>>>>>> main
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: have_no, not_to
@@ -59,11 +67,12 @@ Capybara/RSpec/HaveSelector:
- 'spec/features/track_organizer_ability_spec.rb'
- 'spec/features/voting_spec.rb'
-# Offense count: 82
+# Offense count: 98
# This cop supports safe autocorrection (--autocorrect).
Capybara/SpecificFinders:
Enabled: false
+<<<<<<< HEAD
# Offense count: 1
Capybara/SpecificMatcher:
Exclude:
@@ -447,6 +456,8 @@ Layout/TrailingEmptyLines:
- 'lib/tasks/event_attatchments.rake'
- 'lib/tasks/roles.rake'
+=======
+>>>>>>> main
# Offense count: 13
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedMethods, AllowedPatterns.
@@ -460,25 +471,6 @@ Lint/AmbiguousBlockAssociation:
- 'spec/controllers/schedules_controller_spec.rb'
- 'spec/models/user_spec.rb'
-# Offense count: 13
-# This cop supports safe autocorrection (--autocorrect).
-Lint/AmbiguousOperatorPrecedence:
- Exclude:
- - 'app/controllers/application_controller.rb'
- - 'app/helpers/conference_helper.rb'
- - 'app/models/admin_ability.rb'
- - 'app/models/commercial.rb'
- - 'app/models/conference.rb'
- - 'app/models/track.rb'
- - 'app/pdfs/ticket_pdf.rb'
-
-# Offense count: 1
-# Configuration parameters: AllowedMethods.
-# AllowedMethods: enums
-Lint/ConstantDefinitionInBlock:
- Exclude:
- - 'lib/tasks/data.rake'
-
# Offense count: 5
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches.
Lint/DuplicateBranch:
@@ -486,18 +478,13 @@ Lint/DuplicateBranch:
- 'app/helpers/format_helper.rb'
- 'app/uploaders/picture_uploader.rb'
-# Offense count: 2
-Lint/DuplicateHashKey:
- Exclude:
- - 'db/migrate/20140801164901_move_conference_media_to_commercial.rb'
- - 'db/migrate/20140801170430_move_event_media_to_commercial.rb'
-
# Offense count: 4
Lint/IneffectiveAccessModifier:
Exclude:
- 'app/models/commercial.rb'
- 'app/models/conference.rb'
+<<<<<<< HEAD
# Offense count: 4
# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/NonAtomicFileOperation:
@@ -532,45 +519,43 @@ Lint/UriRegexp:
- 'app/models/contact.rb'
# Offense count: 127
+=======
+# Offense count: 111
+>>>>>>> main
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 72
-# Offense count: 28
+# Offense count: 19
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
# AllowedMethods: refine
Metrics/BlockLength:
- Max: 211
+ Max: 227
-# Offense count: 1
-# Configuration parameters: CountBlocks.
-Metrics/BlockNesting:
- Max: 4
-
-# Offense count: 14
+# Offense count: 13
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
- Max: 652
+ Max: 270
# Offense count: 26
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/CyclomaticComplexity:
- Max: 16
+ Max: 15
-# Offense count: 151
+# Offense count: 127
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
- Max: 56
+ Max: 55
-# Offense count: 5
+# Offense count: 4
# Configuration parameters: CountComments, CountAsOne.
Metrics/ModuleLength:
- Max: 174
+ Max: 168
-# Offense count: 23
+# Offense count: 25
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity:
- Max: 19
+ Max: 17
# Offense count: 13
Naming/AccessorMethodName:
@@ -604,14 +589,6 @@ Naming/PredicateName:
- 'app/models/comment.rb'
- 'app/models/contact.rb'
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: PreferredName.
-Naming/RescuedExceptionsVariableName:
- Exclude:
- - 'app/models/commercial.rb'
- - 'app/models/payment.rb'
-
# Offense count: 9
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
# SupportedStyles: snake_case, normalcase, non_integer
@@ -623,6 +600,12 @@ Naming/VariableNumber:
- 'spec/models/payment_spec.rb'
- 'spec/models/ticket_purchase_spec.rb'
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Performance/Casecmp:
+ Exclude:
+ - 'config/environments/production.rb'
+
# Offense count: 1
# Configuration parameters: MinSize.
Performance/CollectionLiteralInLoop:
@@ -636,12 +619,11 @@ Performance/InefficientHashSearch:
- 'app/controllers/admin/versions_controller.rb'
- 'app/helpers/versions_helper.rb'
-# Offense count: 2
+# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Performance/MapCompact:
Exclude:
- 'app/datatables/registration_datatable.rb'
- - 'lib/tasks/events_registrations.rake'
# Offense count: 2
# This cop supports unsafe autocorrection (--autocorrect-all).
@@ -649,11 +631,12 @@ Performance/StringInclude:
Exclude:
- 'app/models/commercial.rb'
-# Offense count: 28
+# Offense count: 32
RSpec/AnyInstance:
Exclude:
- 'spec/controllers/admin/rooms_controller_spec.rb'
- 'spec/controllers/admin/sponsorship_levels_controller_spec.rb'
+ - 'spec/controllers/admin/tickets_controller_spec.rb'
- 'spec/controllers/admin/tracks_controller_spec.rb'
- 'spec/controllers/admin/users_controller_spec.rb'
- 'spec/controllers/conference_registration_controller_spec.rb'
@@ -667,119 +650,32 @@ RSpec/BeEmpty:
- 'spec/controllers/conference_registration_controller_spec.rb'
- 'spec/models/conference_spec.rb'
-# Offense count: 161
+# Offense count: 12
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnabledMethods.
RSpec/Capybara/FeatureMethods:
- Enabled: false
+ Exclude:
+ - 'spec/features/proposals_spec.rb'
+ - 'spec/features/user_spec.rb'
+ - 'spec/features/voting_spec.rb'
-# Offense count: 318
+# Offense count: 338
# Configuration parameters: Prefixes, AllowedPatterns.
# Prefixes: when, with, without
RSpec/ContextWording:
Enabled: false
-# Offense count: 73
+# Offense count: 141
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SkipBlocks, EnforcedStyle.
# SupportedStyles: described_class, explicit
RSpec/DescribedClass:
- Exclude:
- - 'spec/mailers/mailbot_spec.rb'
- - 'spec/models/cfp_spec.rb'
- - 'spec/models/conference_spec.rb'
- - 'spec/models/role_spec.rb'
- - 'spec/models/sponsorship_level_spec.rb'
- - 'spec/models/survey_question_spec.rb'
- - 'spec/models/ticket_purchase_spec.rb'
- - 'spec/models/ticket_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/serializers/conference_serializer_spec.rb'
- - 'spec/serializers/event_serializer_spec.rb'
- - 'spec/serializers/room_serializer_spec.rb'
- - 'spec/serializers/speaker_serializer_spec.rb'
- - 'spec/serializers/track_serializer_spec.rb'
-
-# Offense count: 11
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowConsecutiveOneLiners.
-RSpec/EmptyLineAfterExample:
- Exclude:
- - 'spec/ability/ability_spec.rb'
- - 'spec/controllers/admin/comments_controller_spec.rb'
- - 'spec/controllers/admin/registration_periods_controller_spec.rb'
- - 'spec/controllers/admin/users_controller_spec.rb'
- - 'spec/helpers/events_helper_spec.rb'
- - 'spec/models/event_spec.rb'
- - 'spec/models/survey_spec.rb'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-RSpec/EmptyLineAfterExampleGroup:
- Exclude:
- - 'spec/controllers/admin/users_controller_spec.rb'
-
-# Offense count: 11
-# This cop supports safe autocorrection (--autocorrect).
-RSpec/EmptyLineAfterFinalLet:
- Exclude:
- - 'spec/controllers/admin/event_schedules_controller_spec.rb'
- - 'spec/controllers/admin/users_controller_spec.rb'
- - 'spec/controllers/application_controller_spec.rb'
- - 'spec/features/conference_spec.rb'
- - 'spec/models/conference_spec.rb'
- - 'spec/models/payment_spec.rb'
- - 'spec/models/ticket_spec.rb'
-
-# Offense count: 16
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowConsecutiveOneLiners.
-RSpec/EmptyLineAfterHook:
- Exclude:
- - 'spec/controllers/admin/booths_controller_spec.rb'
- - 'spec/controllers/admin/comments_controller_spec.rb'
- - 'spec/controllers/admin/organizations_controller_spec.rb'
- - 'spec/controllers/admin/ticket_scannings_controller_spec.rb'
- - 'spec/controllers/admin/users_controller_spec.rb'
- - 'spec/features/organization_spec.rb'
- - 'spec/features/roles_spec.rb'
- - 'spec/models/conference_spec.rb'
- - 'spec/models/payment_spec.rb'
- - 'spec/models/program_spec.rb'
-
-# Offense count: 8
-# This cop supports safe autocorrection (--autocorrect).
-RSpec/EmptyLineAfterSubject:
- Exclude:
- - 'spec/ability/ability_spec.rb'
- - 'spec/models/booth_spec.rb'
- - 'spec/models/cfp_spec.rb'
- - 'spec/models/event_spec.rb'
- - 'spec/models/program_spec.rb'
- - 'spec/models/registration_spec.rb'
- - 'spec/models/survey_spec.rb'
- - 'spec/models/track_spec.rb'
+ Enabled: false
-# Offense count: 215
+# Offense count: 248
# Configuration parameters: CountAsOne.
RSpec/ExampleLength:
- Max: 187
-
-# Offense count: 15
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples.
-# DisallowedExamples: works
-RSpec/ExampleWording:
- Exclude:
- - 'spec/controllers/admin/organizations_controller_spec.rb'
- - 'spec/controllers/admin/registration_periods_controller_spec.rb'
- - 'spec/helpers/application_helper_spec.rb'
- - 'spec/helpers/events_helper_spec.rb'
- - 'spec/helpers/format_helper_spec.rb'
- - 'spec/models/conference_spec.rb'
- - 'spec/models/event_spec.rb'
- - 'spec/models/ticket_purchase_spec.rb'
- - 'spec/models/ticket_spec.rb'
+ Max: 222
# Offense count: 37
# This cop supports unsafe autocorrection (--autocorrect-all).
@@ -817,18 +713,20 @@ RSpec/FilePath:
- 'spec/models/comment_spec.rb'
- 'spec/models/openid.rb'
-# Offense count: 172
+# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: implicit, each, example
RSpec/HookArgument:
- Enabled: false
+ Exclude:
+ - 'spec/features/voting_spec.rb'
# Offense count: 2
RSpec/IdenticalEqualityAssertion:
Exclude:
- 'spec/controllers/admin/conferences_controller_spec.rb'
+<<<<<<< HEAD
# Offense count: 140
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
@@ -869,17 +767,27 @@ RSpec/ImplicitSubject:
# Offense count: 19
# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns.
+=======
+# Offense count: 53
+# Configuration parameters: Max.
+>>>>>>> main
RSpec/IndexedLet:
Exclude:
- 'spec/controllers/admin/reports_controller_spec.rb'
- 'spec/controllers/admin/roles_controller_spec.rb'
+ - 'spec/controllers/schedules_controller_spec.rb'
+ - 'spec/features/proposals_spec.rb'
+ - 'spec/features/splashpage_spec.rb'
- 'spec/features/voting_spec.rb'
+ - 'spec/helpers/conference_helper_spec.rb'
- 'spec/models/conference_spec.rb'
+ - 'spec/models/event_schedule_spec.rb'
- 'spec/models/ticket_purchase_spec.rb'
- 'spec/models/ticket_spec.rb'
- 'spec/models/user_spec.rb'
+ - 'spec/services/full_calendar_formatter_spec.rb'
-# Offense count: 321
+# Offense count: 344
# Configuration parameters: AssignmentOnly.
RSpec/InstanceVariable:
Exclude:
@@ -899,15 +807,7 @@ RSpec/InstanceVariable:
- 'spec/models/track_spec.rb'
- 'spec/models/user_spec.rb'
-# Offense count: 4
-# This cop supports safe autocorrection (--autocorrect).
-RSpec/LeadingSubject:
- Exclude:
- - 'spec/ability/ability_spec.rb'
- - 'spec/models/conference_spec.rb'
- - 'spec/models/ticket_spec.rb'
-
-# Offense count: 61
+# Offense count: 64
RSpec/LetSetup:
Enabled: false
@@ -930,65 +830,66 @@ RSpec/MultipleDescribes:
Exclude:
- 'spec/models/conference_spec.rb'
-# Offense count: 270
+# Offense count: 298
RSpec/MultipleExpectations:
Max: 97
-# Offense count: 249
+# Offense count: 272
# Configuration parameters: AllowSubject.
RSpec/MultipleMemoizedHelpers:
Max: 32
-# Offense count: 396
+# Offense count: 438
# Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
# SupportedStyles: always, named_only
RSpec/NamedSubject:
Exclude:
+ - 'spec/ability/ability_spec.rb'
+ - 'spec/models/booth_spec.rb'
- 'spec/models/conference_spec.rb'
+ - 'spec/models/event_type_spec.rb'
+ - 'spec/models/organization_spec.rb'
+ - 'spec/models/program_spec.rb'
+ - 'spec/models/registration_period_spec.rb'
- 'spec/models/registration_spec.rb'
- 'spec/models/room_spec.rb'
+ - 'spec/models/sponsor_spec.rb'
+ - 'spec/models/sponsorship_level_spec.rb'
+ - 'spec/models/ticket_purchase_spec.rb'
+ - 'spec/models/ticket_spec.rb'
- 'spec/models/track_spec.rb'
-# Offense count: 208
+# Offense count: 219
# Configuration parameters: AllowedGroups.
RSpec/NestedGroups:
Max: 7
-# Offense count: 3
+# Offense count: 4
# Configuration parameters: AllowedPatterns.
# AllowedPatterns: ^expect_, ^assert_
RSpec/NoExpectationExample:
Exclude:
- 'spec/controllers/admin/conferences_controller_spec.rb'
- 'spec/controllers/admin/registration_periods_controller_spec.rb'
+ - 'spec/features/proposals_spec.rb'
- 'spec/features/voting_spec.rb'
-# Offense count: 83
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: not_to, to_not
-RSpec/NotToNot:
- Enabled: false
-
# Offense count: 1
RSpec/OverwritingSetup:
Exclude:
- 'spec/controllers/admin/booths_controller_spec.rb'
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-RSpec/Rails/AvoidSetupHook:
+# Offense count: 11
+RSpec/PendingWithoutReason:
Exclude:
+ - 'spec/ability/ability_spec.rb'
+ - 'spec/controllers/admin/conferences_controller_spec.rb'
+ - 'spec/datatables/user_datatable_spec.rb'
+ - 'spec/features/proposals_spec.rb'
- 'spec/features/versions_spec.rb'
- - 'spec/helpers/events_helper_spec.rb'
-
-# Offense count: 2
-# This cop supports unsafe autocorrection (--autocorrect-all).
-RSpec/Rails/HaveHttpStatus:
- Exclude:
- - 'spec/controllers/admin/event_schedules_controller_spec.rb'
+ - 'spec/models/user_spec.rb'
-# Offense count: 11
+# Offense count: 12
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Inferences.
RSpec/Rails/InferredSpecType:
@@ -997,6 +898,7 @@ RSpec/Rails/InferredSpecType:
- 'spec/controllers/admin/programs_controller_spec.rb'
- 'spec/controllers/application_controller_spec.rb'
- 'spec/controllers/conference_registration_controller_spec.rb'
+ - 'spec/features/omniauth_spec.rb'
- 'spec/helpers/application_helper_spec.rb'
- 'spec/helpers/conference_helper_spec.rb'
- 'spec/helpers/date_time_helper_spec.rb'
@@ -1026,21 +928,6 @@ RSpec/RepeatedExampleGroupBody:
Exclude:
- 'spec/models/conference_spec.rb'
-# Offense count: 10
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: and_return, block
-RSpec/ReturnFromStub:
- Exclude:
- - 'spec/helpers/events_helper_spec.rb'
-
-# Offense count: 19
-# This cop supports safe autocorrection (--autocorrect).
-RSpec/ScatteredLet:
- Exclude:
- - 'spec/ability/ability_spec.rb'
- - 'spec/models/payment_spec.rb'
-
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
RSpec/ScatteredSetup:
@@ -1077,473 +964,94 @@ RSpec/VoidExpect:
Exclude:
- 'spec/models/conference_spec.rb'
-# Offense count: 24
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: ExpectedOrder, Include.
-# ExpectedOrder: index, show, new, edit, create, update, destroy
-# Include: app/controllers/**/*.rb
-Rails/ActionOrder:
- Enabled: false
-
-# Offense count: 4
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Rails/ActiveRecordAliases:
+# Offense count: 3
+Security/Open:
Exclude:
- - 'db/migrate/20141104131625_generate_username.rb'
- - 'db/migrate/20141117214230_move_banner_description_to_conference.rb'
- - 'db/migrate/20141118153918_change_venue_conference_association.rb'
- - 'db/migrate/20141118162030_change_lodging_association_to_conference.rb'
+ - 'app/pdfs/ticket_pdf.rb'
-# Offense count: 3
+# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Include.
-# Include: app/models/**/*.rb
-Rails/ActiveRecordCallbacksOrder:
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: separated, grouped
+Style/AccessorGrouping:
Exclude:
- - 'app/models/event.rb'
- - 'app/models/track.rb'
- - 'app/models/venue.rb'
+ - 'app/models/payment.rb'
-# Offense count: 1
+# Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all).
-Rails/ApplicationController:
+Style/ArrayIntersect:
Exclude:
- - 'app/controllers/api/base_controller.rb'
+ - 'app/helpers/application_helper.rb'
+ - 'app/models/admin_ability.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
-Rails/ApplicationMailer:
+# Configuration parameters: MinBranchesCount.
+Style/CaseLikeIf:
Exclude:
- - 'app/mailers/mailbot.rb'
+ - 'app/views/admin/events/events.xlsx.axlsx'
-# Offense count: 129
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Rails/ApplicationRecord:
- Enabled: false
+<<<<<<< HEAD
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: is_a?, kind_of?
+Style/ClassCheck:
+ Exclude:
+ - 'app/models/email_settings.rb'
-# Offense count: 10
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent.
-Rails/Blank:
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+Style/ColonMethodCall:
Exclude:
- - 'app/controllers/conferences_controller.rb'
- - 'app/controllers/users/omniauth_callbacks_controller.rb'
- - 'app/models/program.rb'
- - 'app/models/user.rb'
- - 'spec/factories/event_schedule.rb'
+ - 'app/models/commercial.rb'
+ - 'app/models/contact.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
-Rails/CompactBlank:
+Style/CombinableLoops:
Exclude:
- - 'app/controllers/surveys_controller.rb'
+ - 'db/migrate/20140820093735_migrating_supporter_registrations_to_ticket_users.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
-Rails/ContentTag:
+# Configuration parameters: Keywords, RequireColon.
+# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE
+Style/CommentAnnotation:
Exclude:
- - 'app/helpers/application_helper.rb'
+ - 'spec/controllers/admin/versions_controller_spec.rb'
-# Offense count: 13
-# Configuration parameters: Include.
-# Include: db/migrate/*.rb
-Rails/CreateTableWithTimestamps:
- Exclude:
- - 'db/migrate/20121223115117_create_rooms_table.rb'
- - 'db/migrate/20121223120413_create_event_types.rb'
- - 'db/migrate/20130202130737_create_supporter_level_table.rb'
- - 'db/migrate/20130202130923_create_table_supporter_registrations.rb'
- - 'db/migrate/20130216070725_create_social_events_table.rb'
- - 'db/migrate/20131228214532_create_vchoices.rb'
- - 'db/migrate/20140109191145_create_qanswers.rb'
- - 'db/migrate/20140623100942_create_visits.rb'
- - 'db/migrate/20140623101032_create_ahoy_events.rb'
- - 'db/migrate/20160309182642_remove_social_events_table.rb'
- - 'db/migrate/20160628093634_create_survey_questions.rb'
- - 'db/migrate/20170129075434_create_resources_table.rb'
- - 'db/migrate/20170529215453_create_organizations.rb'
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
+# SupportedStyles: assign_to_condition, assign_inside_condition
+Style/ConditionalAssignment:
+ Exclude:
+ - 'app/helpers/format_helper.rb'
+ - 'db/migrate/20140610165551_migrate_data_person_to_user.rb'
+ - 'db/migrate/20140820124117_undo_wrong_migration20140801080705_add_users_to_events.rb'
+# Offense count: 518
+=======
# Offense count: 103
-# Configuration parameters: EnforcedStyle, AllowToTime.
-# SupportedStyles: strict, flexible
-Rails/Date:
+>>>>>>> main
+# Configuration parameters: AllowedConstants.
+Style/Documentation:
Enabled: false
-# Offense count: 3
+<<<<<<< HEAD
+# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforceForPrefixed.
-Rails/Delegate:
+Style/EmptyCaseCondition:
Exclude:
- - 'app/models/event.rb'
- - 'app/models/room.rb'
- - 'app/models/track.rb'
+ - 'app/helpers/format_helper.rb'
+ - 'app/helpers/versions_helper.rb'
-# Offense count: 4
+# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Severity.
-Rails/DuplicateAssociation:
+Style/EmptyLiteral:
Exclude:
- - 'app/models/program.rb'
- - 'app/models/user.rb'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-Rails/DurationArithmetic:
- Exclude:
- - 'spec/models/program_spec.rb'
- - 'spec/models/user_spec.rb'
-
-# Offense count: 3
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: Whitelist, AllowedMethods, AllowedReceivers.
-# Whitelist: find_by_sql, find_by_token_for
-# AllowedMethods: find_by_sql, find_by_token_for
-# AllowedReceivers: Gem::Specification, page
-Rails/DynamicFindBy:
- Exclude:
- - 'app/controllers/admin/events_controller.rb'
- - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb'
-
-# Offense count: 5
-# This cop supports safe autocorrection (--autocorrect).
-Rails/EagerEvaluationLogMessage:
- Exclude:
- - 'app/controllers/admin/events_controller.rb'
- - 'app/controllers/application_controller.rb'
- - 'app/controllers/proposals_controller.rb'
- - 'app/models/event.rb'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Include.
-# Include: app/models/**/*.rb
-Rails/EnumHash:
- Exclude:
- - 'app/models/conference.rb'
- - 'app/models/survey.rb'
- - 'app/models/survey_question.rb'
-
-# Offense count: 7
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: slashes, arguments
-Rails/FilePath:
- Exclude:
- - 'app/pdfs/ticket_pdf.rb'
- - 'config/initializers/carrierwave.rb'
- - 'lib/tasks/migrate_config.rake'
- - 'spec/features/lodgings_spec.rb'
- - 'spec/features/sponsor_spec.rb'
- - 'spec/support/deprecation_shitlist.rb'
-
-# Offense count: 6
-# 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: 24
-# Configuration parameters: Include.
-# Include: app/models/**/*.rb
-Rails/HasManyOrHasOneDependent:
- Enabled: false
-
-# Offense count: 5
-# Configuration parameters: Include.
-# Include: app/helpers/**/*.rb
-Rails/HelperInstanceVariable:
- Exclude:
- - 'app/helpers/application_helper.rb'
- - 'app/helpers/events_helper.rb'
- - 'app/helpers/format_helper.rb'
-
-# Offense count: 8
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: numeric, symbolic
-Rails/HttpStatus:
- Exclude:
- - 'app/controllers/admin/commercials_controller.rb'
- - 'app/controllers/admin/event_schedules_controller.rb'
- - 'app/controllers/admin/programs_controller.rb'
- - 'app/controllers/admin/tracks_controller.rb'
- - 'app/controllers/admin/venue_commercials_controller.rb'
- - 'app/controllers/commercials_controller.rb'
-
-# Offense count: 100
-Rails/I18nLocaleTexts:
- Enabled: false
-
-# Offense count: 7
-# Configuration parameters: IgnoreScopes, Include.
-# Include: app/models/**/*.rb
-Rails/InverseOf:
- Exclude:
- - 'app/models/booth.rb'
- - 'app/models/conference.rb'
- - 'app/models/event.rb'
- - 'app/models/user.rb'
-
-# Offense count: 1
-# Configuration parameters: Include.
-# Include: app/controllers/**/*.rb, app/mailers/**/*.rb
-Rails/LexicallyScopedActionFilter:
- Exclude:
- - 'app/controllers/registrations_controller.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Rails/LinkToBlank:
- Exclude:
- - 'app/helpers/format_helper.rb'
-
-# Offense count: 1
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: Include.
-# Include: app/mailers/**/*.rb
-Rails/MailerName:
- Exclude:
- - 'app/mailers/mailbot.rb'
-
-# Offense count: 3
-Rails/OutputSafety:
- Exclude:
- - 'app/helpers/events_helper.rb'
- - 'app/models/commercial.rb'
-
-# Offense count: 6
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: conservative, aggressive
-Rails/PluckInWhere:
- Exclude:
- - 'app/models/ability.rb'
- - 'app/models/admin_ability.rb'
-
-# Offense count: 16
-# This cop supports safe autocorrection (--autocorrect).
-Rails/PluralizationGrammar:
- Exclude:
- - 'spec/models/conference_spec.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-Rails/Presence:
- Exclude:
- - 'app/controllers/schedules_controller.rb'
- - 'app/models/user.rb'
-
-# Offense count: 15
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: NotNilAndNotEmpty, NotBlank, UnlessBlank.
-Rails/Present:
- Exclude:
- - 'app/models/cfp.rb'
- - 'app/models/email_settings.rb'
- - 'app/models/event.rb'
- - 'app/models/program.rb'
- - 'app/models/venue.rb'
-
-# Offense count: 6
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: Include.
-# Include: **/Rakefile, **/*.rake
-Rails/RakeEnvironment:
- Exclude:
- - 'lib/tasks/dump_db.rake'
- - 'lib/tasks/spec.rake'
-
-# Offense count: 21
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Rails/RedundantPresenceValidationOnBelongsTo:
- Enabled: false
-
-# Offense count: 2
-Rails/RenderInline:
- Exclude:
- - 'app/controllers/conferences_controller.rb'
- - 'app/controllers/schedules_controller.rb'
-
-# Offense count: 10
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: Include.
-# Include: spec/controllers/**/*.rb, spec/requests/**/*.rb, test/controllers/**/*.rb, test/integration/**/*.rb
-Rails/ResponseParsedBody:
- Exclude:
- - 'spec/controllers/api/v1/conferences_controller_spec.rb'
- - 'spec/controllers/api/v1/events_controller_spec.rb'
- - 'spec/controllers/api/v1/rooms_controller_spec.rb'
- - 'spec/controllers/api/v1/speakers_controller_spec.rb'
- - 'spec/controllers/api/v1/tracks_controller_spec.rb'
-
-# Offense count: 4
-# Configuration parameters: Include.
-# Include: db/**/*.rb
-Rails/ReversibleMigration:
- Exclude:
- - 'db/migrate/20170108053041_add_default_to_revision_in_conference.rb'
- - 'db/migrate/20170715131706_make_track_state_not_null_and_add_default_value.rb'
- - 'db/migrate/20170720134353_make_track_cfp_active_not_null.rb'
- - 'db/migrate/20171118113113_change_visit_id_type_of_ahoy_events_to_integer.rb'
-
-# Offense count: 48
-# Configuration parameters: ForbiddenMethods, AllowedMethods.
-# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all
-Rails/SkipsModelValidations:
- Enabled: false
-
-# Offense count: 77
-# Configuration parameters: Include.
-# Include: db/**/*.rb
-Rails/ThreeStateBooleanColumn:
- Enabled: false
-
-# Offense count: 40
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: strict, flexible
-Rails/TimeZone:
- Exclude:
- - 'app/models/comment.rb'
- - 'app/models/conference.rb'
- - 'config/environments/test.rb'
- - 'db/migrate/20180226032958_add_created_at_and_updated_at_to_event_types.rb'
- - 'db/migrate/20180313012253_add_timestamps_to_tickets.rb'
- - 'lib/tasks/dump_db.rake'
- - 'spec/controllers/admin/comments_controller_spec.rb'
- - 'spec/factories/users.rb'
- - 'spec/models/conference_spec.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Severity.
-Rails/TopLevelHashWithIndifferentAccess:
- Exclude:
- - 'db/migrate/20140701123203_add_events_per_week_to_conference.rb'
-
-# Offense count: 13
-# Configuration parameters: Include.
-# Include: app/models/**/*.rb
-Rails/UniqueValidationWithoutIndex:
- Exclude:
- - 'app/models/booth.rb'
- - 'app/models/cfp.rb'
- - 'app/models/commercial.rb'
- - 'app/models/conference.rb'
- - 'app/models/events_registration.rb'
- - 'app/models/organization.rb'
- - 'app/models/registration.rb'
- - 'app/models/role.rb'
- - 'app/models/subscription.rb'
- - 'app/models/survey_reply.rb'
- - 'app/models/survey_submission.rb'
- - 'app/models/track.rb'
- - 'app/models/vote.rb'
-
-# Offense count: 17
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Rails/WhereEquals:
- Exclude:
- - 'app/controllers/admin/registrations_controller.rb'
- - 'app/models/admin_ability.rb'
- - 'app/models/conference.rb'
- - 'app/models/event_schedule.rb'
- - 'app/models/program.rb'
- - 'app/models/user.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Rails/WhereNot:
- Exclude:
- - 'db/migrate/20140820093735_migrating_supporter_registrations_to_ticket_users.rb'
-
-# Offense count: 3
-Security/Open:
- Exclude:
- - 'app/pdfs/ticket_pdf.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: separated, grouped
-Style/AccessorGrouping:
- Exclude:
- - 'app/models/payment.rb'
-
-# Offense count: 3
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Style/ArrayIntersect:
- Exclude:
- - 'app/helpers/application_helper.rb'
- - 'app/models/admin_ability.rb'
-
-# Offense count: 1
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: MinBranchesCount.
-Style/CaseLikeIf:
- Exclude:
- - 'app/views/admin/events/events.xlsx.axlsx'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: is_a?, kind_of?
-Style/ClassCheck:
- Exclude:
- - 'app/models/email_settings.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-Style/ColonMethodCall:
- Exclude:
- - 'app/models/commercial.rb'
- - 'app/models/contact.rb'
-
-# Offense count: 1
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Style/CombinableLoops:
- Exclude:
- - 'db/migrate/20140820093735_migrating_supporter_registrations_to_ticket_users.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Keywords, RequireColon.
-# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE
-Style/CommentAnnotation:
- Exclude:
- - 'spec/controllers/admin/versions_controller_spec.rb'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
-# SupportedStyles: assign_to_condition, assign_inside_condition
-Style/ConditionalAssignment:
- Exclude:
- - 'app/helpers/format_helper.rb'
- - 'db/migrate/20140610165551_migrate_data_person_to_user.rb'
- - 'db/migrate/20140820124117_undo_wrong_migration20140801080705_add_users_to_events.rb'
-
-# Offense count: 518
-# Configuration parameters: AllowedConstants.
-Style/Documentation:
- Enabled: false
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-Style/EmptyCaseCondition:
- Exclude:
- - 'app/helpers/format_helper.rb'
- - 'app/helpers/versions_helper.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Style/EmptyLiteral:
- Exclude:
- - 'spec/models/conference_spec.rb'
+ - 'spec/models/conference_spec.rb'
# Offense count: 7
# This cop supports safe autocorrection (--autocorrect).
@@ -1573,6 +1081,9 @@ Style/ExpandPathArguments:
- 'spec/spec_helper.rb'
# Offense count: 36
+=======
+# Offense count: 40
+>>>>>>> main
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, always_true, never
@@ -1585,12 +1096,13 @@ Style/GlobalStdStream:
Exclude:
- 'config/environments/production.rb'
-# Offense count: 28
+# Offense count: 36
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
Style/GuardClause:
Enabled: false
+<<<<<<< HEAD
# Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
@@ -1609,20 +1121,14 @@ Style/HashConversion:
- 'app/models/conference.rb'
- 'spec/factories/event_users.rb'
+=======
+>>>>>>> main
# Offense count: 1
# Configuration parameters: MinBranchesCount.
Style/HashLikeCase:
Exclude:
- 'app/helpers/versions_helper.rb'
-# Offense count: 367
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
-# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
-# SupportedShorthandSyntax: always, never, either, consistent
-Style/HashSyntax:
- Enabled: false
-
# Offense count: 2
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/HashTransformValues:
@@ -1630,7 +1136,14 @@ Style/HashTransformValues:
- 'app/controllers/admin/comments_controller.rb'
- 'app/helpers/chart_helper.rb'
-# Offense count: 58
+# Offense count: 4
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/IdenticalConditionalBranches:
+ Exclude:
+ - 'app/controllers/admin/booths_controller.rb'
+ - 'app/controllers/admin/events_controller.rb'
+
+# Offense count: 32
# This cop supports safe autocorrection (--autocorrect).
Style/IfUnlessModifier:
Enabled: false
@@ -1642,21 +1155,13 @@ Style/LineEndConcatenation:
- 'spec/features/conference_spec.rb'
- 'spec/features/registration_periods_spec.rb'
-# Offense count: 1
+# Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/MapToHash:
Exclude:
- 'app/controllers/admin/comments_controller.rb'
-
-# Offense count: 9
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline
-Style/MethodDefParentheses:
- Exclude:
+ - 'app/helpers/chart_helper.rb'
- 'app/models/conference.rb'
- - 'app/models/user.rb'
- - 'lib/tasks/demo_data_for_development.rake'
# Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all).
@@ -1670,62 +1175,20 @@ Style/MixinUsage:
Exclude:
- 'spec/spec_helper.rb'
-# Offense count: 7
-# This cop supports safe autocorrection (--autocorrect).
-Style/MultilineIfModifier:
+# Offense count: 1
+Style/MultilineBlockChain:
Exclude:
- - 'app/controllers/conferences_controller.rb'
- - 'app/controllers/schedules_controller.rb'
- - 'app/models/cfp.rb'
- - 'app/models/event.rb'
- - 'app/models/registration_period.rb'
+ - 'app/controllers/admin/comments_controller.rb'
-# Offense count: 2
+# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: literals, strict
Style/MutableConstant:
Exclude:
- 'app/models/event_user.rb'
- - 'lib/tasks/migrate_config.rake'
-
-# Offense count: 4
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowedMethods.
-# AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with
-Style/NestedParenthesizedCalls:
- Exclude:
- - 'spec/features/conference_spec.rb'
- - 'spec/models/conference_spec.rb'
-
-# Offense count: 27
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, MinBodyLength.
-# SupportedStyles: skip_modifier_ifs, always
-Style/Next:
- Enabled: false
-
-# Offense count: 115
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedOctalStyle.
-# SupportedOctalStyles: zero_with_o, zero_only
-Style/NumericLiteralPrefix:
- Exclude:
- - 'config/environments/test.rb'
- - 'spec/controllers/admin/conferences_controller_spec.rb'
- - 'spec/helpers/date_time_helper_spec.rb'
- - 'spec/models/conference_spec.rb'
- - 'spec/models/email_settings_spec.rb'
- - 'spec/serializers/conference_serializer_spec.rb'
- - 'spec/serializers/event_serializer_spec.rb'
-
-# Offense count: 7
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns.
-Style/NumericLiterals:
- MinDigits: 15
-# Offense count: 36
+# Offense count: 32
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
# SupportedStyles: predicate, comparison
@@ -1734,7 +1197,6 @@ Style/NumericPredicate:
- 'app/controllers/admin/conferences_controller.rb'
- 'app/helpers/application_helper.rb'
- 'app/helpers/date_time_helper.rb'
- - 'app/helpers/format_helper.rb'
- 'app/models/cfp.rb'
- 'app/models/conference.rb'
- 'app/models/event.rb'
@@ -1742,7 +1204,6 @@ Style/NumericPredicate:
- 'app/models/registration.rb'
- 'app/models/ticket_purchase.rb'
- 'app/models/user.rb'
- - 'lib/tasks/events_registrations.rake'
# Offense count: 3
Style/OptionalArguments:
@@ -1758,6 +1219,7 @@ Style/OptionalBooleanParameter:
- 'app/helpers/format_helper.rb'
- 'app/models/event.rb'
+<<<<<<< HEAD
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
Style/OrAssignment:
@@ -1802,31 +1264,15 @@ Style/PreferredHashMethods:
# Offense count: 6
# This cop supports unsafe autocorrection (--autocorrect-all).
+=======
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+>>>>>>> main
# Configuration parameters: AllowedCompactTypes.
# SupportedStyles: compact, exploded
Style/RaiseArgs:
EnforcedStyle: compact
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Style/RandomWithOffset:
- Exclude:
- - 'spec/factories/sponsors.rb'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-Style/RedundantAssignment:
- Exclude:
- - 'app/helpers/application_helper.rb'
- - 'app/helpers/format_helper.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-Style/RedundantCondition:
- Exclude:
- - 'app/helpers/versions_helper.rb'
- - 'app/models/ticket.rb'
-
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
Style/RedundantConstantBase:
@@ -1841,6 +1287,7 @@ Style/RedundantFetchBlock:
Exclude:
- 'config/puma.rb'
+<<<<<<< HEAD
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/RedundantFilterChain:
@@ -1870,90 +1317,21 @@ Style/RedundantReturn:
- 'app/controllers/admin/events_controller.rb'
- 'app/controllers/admin/organizations_controller.rb'
+=======
+>>>>>>> main
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
Style/RedundantStringEscape:
Exclude:
- 'app/models/conference.rb'
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
-# SupportedStyles: slashes, percent_r, mixed
-Style/RegexpLiteral:
- Exclude:
- - 'app/uploaders/picture_uploader.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: implicit, explicit
-Style/RescueStandardError:
- Exclude:
- - 'app/controllers/users/omniauth_callbacks_controller.rb'
- - 'lib/tasks/migrate_config.rake'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: only_raise, only_fail, semantic
-Style/SignalException:
- Exclude:
- - 'lib/tasks/user.rake'
- - 'spec/support/flash.rb'
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowModifier.
-Style/SoleNestedConditional:
- Exclude:
- - 'app/controllers/admin/users_controller.rb'
- - 'app/models/event.rb'
- - 'app/models/user.rb'
- - 'db/migrate/20140801164901_move_conference_media_to_commercial.rb'
- - 'db/migrate/20140801170430_move_event_media_to_commercial.rb'
- - 'db/migrate/20151018152439_create_programs_table.rb'
-
-# Offense count: 28
+# Offense count: 34
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Mode.
Style/StringConcatenation:
Enabled: false
-# Offense count: 17
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
-# SupportedStyles: single_quotes, double_quotes
-Style/StringLiterals:
- Exclude:
- - 'Gemfile'
- - 'config/deploy.rb'
- - 'config/environments/production.rb'
- - 'config/puma.rb'
- - 'lib/tasks/dump_db.rake'
- - 'lib/tasks/events_registrations.rake'
- - 'lib/tasks/factory_bot.rake'
- - 'lib/tasks/user.rake'
-
-# Offense count: 14
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: single_quotes, double_quotes
-Style/StringLiteralsInInterpolation:
- Exclude:
- - 'app/views/admin/events/_all_events.xlsx.axlsx'
- - 'app/views/admin/events/_all_with_comments.xlsx.axlsx'
- - 'app/views/admin/events/_confirmed_events.xlsx.axlsx'
- - 'lib/tasks/dump_db.rake'
-
-# Offense count: 110
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, MinSize.
-# SupportedStyles: percent, brackets
-Style/SymbolArray:
- Enabled: false
-
-# Offense count: 10
+# Offense count: 11
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
# AllowedMethods: define_method, mail, respond_to
@@ -1963,10 +1341,10 @@ Style/SymbolProc:
- 'app/controllers/admin/questions_controller.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'
+<<<<<<< HEAD
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
@@ -1985,16 +1363,19 @@ Style/TrailingCommaInHashLiteral:
- 'spec/models/conference_spec.rb'
# Offense count: 4
+=======
+# Offense count: 1
+>>>>>>> main
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: WordRegex.
# SupportedStyles: percent, brackets
Style/WordArray:
EnforcedStyle: percent
- MinSize: 6
+ MinSize: 5
-# Offense count: 524
+# Offense count: 260
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
# URISchemes: http, https
Layout/LineLength:
- Max: 619
+ Max: 267
diff --git a/.ruby-version b/.ruby-version
index 0aec50e6e..be94e6f53 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-3.1.4
+3.2.2
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 000000000..80e5486ad
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1,2 @@
+ruby 3.2.2
+nodejs 16.20.2
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..81356792e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "editor.indentSize": "tabSize"
+}
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
index 05e824acb..29ba291ba 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,16 +1,16 @@
# frozen_string_literal: true
def next?
- File.basename(__FILE__) == "Gemfile.next"
+ File.basename(__FILE__) == 'Gemfile.next'
end
source 'https://rubygems.org'
-ruby ENV.fetch('OSEM_RUBY_VERSION', '3.1.4')
+ruby ENV.fetch('OSEM_RUBY_VERSION', '3.2.2')
# as web framework
if next?
- gem 'rails', '~> 7.1'
+ gem 'rails', '~> 7'
else
gem 'rails', '~> 7.0'
end
@@ -23,16 +23,17 @@ gem 'puma'
gem 'responders', '~> 3.0'
# as supported databases
-gem 'mysql2'
gem 'pg'
# for tracking data changes
-gem 'paper_trail'
+gem 'paper_trail', '< 13'
# for upload management
gem 'carrierwave'
gem 'carrierwave-bombshelter'
gem 'mini_magick'
+# for uploading images to the cloud
+gem 'cloudinary'
# for internationalizing
gem 'rails-i18n'
@@ -41,8 +42,8 @@ gem 'rails-i18n'
gem 'devise'
gem 'devise_ichain_authenticatable'
-# for openID authentication
gem 'omniauth'
+gem 'omniauth-discourse', github: 'snap-cloud/omniauth-discourse'
gem 'omniauth-facebook'
gem 'omniauth-github'
gem 'omniauth-google-oauth2'
@@ -62,7 +63,7 @@ gem 'rolify'
gem 'unobtrusive_flash', '>=3'
# as state machine
-gem 'transitions', :require => %w( transitions active_record/transitions )
+gem 'transitions', require: %w[transitions active_record/transitions]
# for comments
gem 'acts_as_commentable_with_threading'
@@ -83,6 +84,7 @@ gem 'bootstrap-sass', '~> 3.4.0'
gem 'cocoon'
# as the JavaScript library
+# TODO: Consolidate with the rails-assets below or move to webpack...
gem 'jquery-rails'
gem 'jquery-ui-rails', '~> 6.0.1'
@@ -94,7 +96,7 @@ gem 'bootstrap3-datetimepicker-rails', '~> 4.17.47'
# data tables
gem 'ajax-datatables-rails'
-gem 'jquery-datatables-rails'
+gem 'jquery-datatables'
# for charts
gem 'chartkick'
@@ -106,6 +108,8 @@ gem 'leaflet-rails'
gem 'gravtastic'
# for country selects
+# TODO-SNAPCON: Verify that this is no longer necessary.
+# gem 'country_select', '< 7'
gem 'i18n_data'
# as PDF generator
@@ -125,8 +129,10 @@ gem 'rqrcode'
# to render XLS spreadsheets
gem 'caxlsx_rails'
-# as error catcher
+# Application Monitoring
+gem 'sentry-delayed_job'
gem 'sentry-rails'
+gem 'sentry-ruby'
# to make links faster
gem 'turbolinks'
@@ -142,7 +148,7 @@ gem 'redcarpet'
# for recurring jobs
gem 'delayed_job_active_record'
-gem 'whenever', :require => false
+gem 'whenever', require: false
# to run scripts
gem 'daemons'
@@ -159,9 +165,6 @@ gem 'bootstrap-switch-rails', '3.3.3' # Locked pending Bttstrp/bootstrap-switch#
# for parsing OEmbed data
gem 'ruby-oembed'
-# for uploading images to the cloud
-gem 'cloudinary'
-
# for setting app configuration in the environment
gem 'dotenv-rails'
@@ -170,7 +173,7 @@ gem 'dotenv-rails'
gem 'feature'
# For countable.js
-gem "countable-rails"
+gem 'countable-rails'
# Both are not in a group as we use it also for rake data:demo
# for fake data
@@ -187,43 +190,50 @@ gem 'sprockets-rails'
# for multiple speakers select on proposal/event forms
gem 'selectize-rails'
-# For collecting performance data
-gem 'skylight'
+# n+1 query logging
+gem 'bullet'
# memcached binary connector
-gem 'dalli'
+gem 'dalli', require: false
+# Redis Cache
+gem 'redis'
# to generate ical files
gem 'icalendar'
+# for making external requests easier
+gem 'httparty'
+
+# pagination
+gem 'pagy', '<4.0'
+
# to tame logs
gem 'lograge'
group :development do
- # for static code analisys
- gem 'rubocop', require: false
- gem 'rubocop-rspec', require: false
- gem 'rubocop-rails', require: false
- gem 'rubocop-capybara', require: false
- gem 'rubocop-performance', require: false
- gem 'haml_lint'
# to open mails
gem 'letter_opener'
+ # view mail at /letter_opener/
gem 'letter_opener_web'
# as deployment system
gem 'mina'
# as debugger on error pages
gem 'web-console'
+ # prepend models with db schema
+ gem 'annotate'
end
group :test do
# as test framework
gem 'capybara'
+ gem 'cucumber-rails', require: false
+ gem 'cucumber-rails-training-wheels' # basic imperative step defs like "Then I should see..."
gem 'database_cleaner'
gem 'geckodriver-helper'
gem 'rspec-rails'
gem 'webdrivers'
# for measuring test coverage
+ gem 'simplecov', '<0.18'
gem 'simplecov-cobertura'
# for describing models
gem 'shoulda-matchers', require: false
@@ -242,12 +252,32 @@ group :test do
# For managing the environment
gem 'climate_control'
# For PDFs
- gem 'pdf-inspector', require: "pdf/inspector"
+ gem 'pdf-inspector', require: 'pdf/inspector'
end
-group :development, :test do
+group :development, :test, :linters do
# as debugger
gem 'byebug'
+
+ # for static code analisys
+ gem 'rubocop', require: false
+ gem 'rubocop-rspec', require: false
+ gem 'rubocop-rails', require: false
+ gem 'rubocop-capybara', require: false
+ gem 'rubocop-performance', require: false
+ gem 'haml_lint'
+
+ gem 'faraday-retry', require: false
+ # TODO-SNAPCON: figure out which haml-lint OR haml_lint is good.
+ gem 'haml-lint', require: false
+
+ # Easily run linters
+ gem 'pronto', require: false
+ gem 'pronto-haml', require: false
+ gem 'pronto-rubocop', require: false
+end
+
+group :development, :test do
# as development/test database
gem 'sqlite3'
# to test new rails version
diff --git a/Gemfile.lock b/Gemfile.lock
index 64dc765e0..78a4032ca 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,3 +1,12 @@
+GIT
+ remote: https://github.com/snap-cloud/omniauth-discourse.git
+ revision: 96dbfcb55bf7bef5a33f83a4919879c891af367c
+ specs:
+ omniauth-discourse (1.1.0)
+ addressable (~> 2.7)
+ omniauth (~> 2.0)
+ rack
+
GEM
remote: https://rubygems.org/
specs:
@@ -84,6 +93,9 @@ GEM
ajax-datatables-rails (1.4.0)
rails (>= 5.2)
zeitwerk
+ annotate (3.2.0)
+ activerecord (>= 3.2, < 8.0)
+ rake (>= 10.4, < 14.0)
archive-zip (0.12.0)
io-like (~> 0.3.0)
ast (2.4.2)
@@ -101,6 +113,9 @@ GEM
bootstrap3-datetimepicker-rails (4.17.47)
momentjs-rails (>= 2.8.1)
builder (3.2.4)
+ bullet (7.0.2)
+ activesupport (>= 3.0.0)
+ uniform_notifier (~> 1.11)
byebug (11.1.3)
cancancan (3.5.0)
capybara (3.39.2)
@@ -149,6 +164,46 @@ GEM
crack (0.4.5)
rexml
crass (1.0.6)
+ cucumber (7.1.0)
+ builder (~> 3.2, >= 3.2.4)
+ cucumber-core (~> 10.1, >= 10.1.0)
+ cucumber-create-meta (~> 6.0, >= 6.0.1)
+ cucumber-cucumber-expressions (~> 14.0, >= 14.0.0)
+ cucumber-gherkin (~> 22.0, >= 22.0.0)
+ cucumber-html-formatter (~> 17.0, >= 17.0.0)
+ cucumber-messages (~> 17.1, >= 17.1.1)
+ cucumber-wire (~> 6.2, >= 6.2.0)
+ diff-lcs (~> 1.4, >= 1.4.4)
+ mime-types (~> 3.3, >= 3.3.1)
+ multi_test (~> 0.1, >= 0.1.2)
+ sys-uname (~> 1.2, >= 1.2.2)
+ cucumber-core (10.1.1)
+ cucumber-gherkin (~> 22.0, >= 22.0.0)
+ cucumber-messages (~> 17.1, >= 17.1.1)
+ cucumber-tag-expressions (~> 4.1, >= 4.1.0)
+ cucumber-create-meta (6.0.4)
+ cucumber-messages (~> 17.1, >= 17.1.1)
+ sys-uname (~> 1.2, >= 1.2.2)
+ cucumber-cucumber-expressions (14.0.0)
+ cucumber-gherkin (22.0.0)
+ cucumber-messages (~> 17.1, >= 17.1.1)
+ cucumber-html-formatter (17.0.0)
+ cucumber-messages (~> 17.1, >= 17.1.0)
+ cucumber-messages (17.1.1)
+ cucumber-rails (2.6.1)
+ capybara (>= 2.18, < 4)
+ cucumber (>= 3.2, < 9)
+ mime-types (~> 3.3)
+ nokogiri (~> 1.10)
+ railties (>= 5.0, < 8)
+ rexml (~> 3.0)
+ webrick (~> 1.7)
+ cucumber-rails-training-wheels (1.0.0)
+ cucumber-rails (>= 1.1.1)
+ cucumber-tag-expressions (4.1.0)
+ cucumber-wire (6.2.1)
+ cucumber-core (~> 10.1, >= 10.1.0)
+ cucumber-cucumber-expressions (~> 14.0, >= 14.0.0)
daemons (1.4.1)
dalli (3.2.5)
dante (0.2.0)
@@ -193,6 +248,8 @@ GEM
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
+ faraday-retry (2.0.0)
+ faraday (~> 2.0)
fastimage (2.2.7)
feature (1.4.0)
ffi (1.16.3)
@@ -200,13 +257,18 @@ GEM
sassc (~> 2.0)
geckodriver-helper (0.24.0)
archive-zip (~> 0.7)
- globalid (1.1.0)
- activesupport (>= 5.0)
+ gitlab (4.19.0)
+ httparty (~> 0.20)
+ terminal-table (>= 1.5.1)
+ globalid (1.2.1)
+ activesupport (>= 6.1)
gravtastic (3.2.6)
haml (6.1.1)
temple (>= 0.8.2)
thor
tilt
+ haml-lint (0.999.999)
+ haml_lint
haml-rails (2.1.0)
actionpack (>= 5.1)
activesupport (>= 5.1)
@@ -225,6 +287,9 @@ GEM
http-accept (1.7.0)
http-cookie (1.0.5)
domain_name (~> 0.5)
+ httparty (0.21.0)
+ mini_mime (>= 1.0.0)
+ multi_xml (>= 0.5.2)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
i18n_data (0.17.1)
@@ -237,11 +302,7 @@ GEM
ruby-vips (>= 2.0.17, < 3)
io-like (0.3.1)
iso-639 (0.3.6)
- jquery-datatables-rails (3.4.0)
- actionpack (>= 3.1)
- jquery-rails
- railties (>= 3.1)
- sass-rails
+ jquery-datatables (1.10.20)
jquery-rails (4.5.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
@@ -270,7 +331,7 @@ GEM
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
- loofah (2.21.4)
+ loofah (2.22.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.8.1)
@@ -289,7 +350,6 @@ GEM
rake
mini_magick (4.12.0)
mini_mime (1.1.5)
- mini_portile2 (2.8.5)
minitest (5.22.2)
momentjs-rails (2.29.4.1)
railties (>= 3.1)
@@ -303,23 +363,26 @@ GEM
money (~> 6.13)
railties (>= 3.0)
multi_json (1.15.0)
+ multi_test (0.1.2)
multi_xml (0.6.0)
- mysql2 (0.5.5)
- net-imap (0.3.7)
+ net-imap (0.4.10)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.2)
timeout
- net-smtp (0.3.4)
+ net-smtp (0.4.0.1)
net-protocol
netrc (0.11.0)
next_rails (1.2.4)
colorize (>= 0.8.1)
- nio4r (2.5.9)
- nokogiri (1.16.2)
- mini_portile2 (~> 2.8.2)
+ nio4r (2.7.0)
+ nokogiri (1.16.2-arm64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.16.2-x86_64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.16.2-x86_64-linux)
racc (~> 1.4)
oauth2 (2.0.9)
faraday (>= 0.17.3, < 3.0)
@@ -328,6 +391,9 @@ GEM
rack (>= 1.2, < 4)
snaky_hash (~> 2.0)
version_gem (~> 1.1)
+ octokit (6.1.1)
+ faraday (>= 1, < 3)
+ sawyer (~> 0.9)
omniauth (2.1.1)
hashie (>= 3.4.6)
rack (>= 2.2.3)
@@ -353,6 +419,7 @@ GEM
omniauth (~> 2.0)
open4 (1.3.4)
orm_adapter (0.5.0)
+ pagy (3.11.0)
paper_trail (12.3.0)
activerecord (>= 5.2)
request_store (~> 1.1)
@@ -382,8 +449,22 @@ GEM
prawn-table
prawn-table (0.2.2)
prawn (>= 1.3.0, < 3.0.0)
+ pronto (0.11.1)
+ gitlab (>= 4.4.0, < 5.0)
+ httparty (>= 0.13.7, < 1.0)
+ octokit (>= 4.7.0, < 7.0)
+ rainbow (>= 2.2, < 4.0)
+ rexml (>= 3.2.5, < 4.0)
+ rugged (>= 0.23.0, < 2.0)
+ thor (>= 0.20.3, < 2.0)
+ pronto-haml (0.11.1)
+ haml_lint (~> 0.23)
+ pronto (~> 0.11.0)
+ pronto-rubocop (0.11.2)
+ pronto (~> 0.11.0)
+ rubocop (>= 0.63.1, < 2.0)
public_suffix (5.0.4)
- puma (6.2.2)
+ puma (6.4.2)
nio4r (~> 2.0)
racc (1.7.3)
rack (2.2.8.1)
@@ -412,11 +493,13 @@ GEM
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
activesupport (>= 5.0.1.rc1)
- rails-dom-testing (2.0.3)
- activesupport (>= 4.2.0)
+ rails-dom-testing (2.2.0)
+ activesupport (>= 5.0.0)
+ minitest
nokogiri (>= 1.6)
- rails-html-sanitizer (1.5.0)
- loofah (~> 2.19, >= 2.19.1)
+ rails-html-sanitizer (1.6.0)
+ loofah (~> 2.21)
+ nokogiri (~> 1.14)
rails-i18n (7.0.7)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
@@ -428,9 +511,11 @@ GEM
thor (~> 1.0)
zeitwerk (~> 2.5)
rainbow (3.1.1)
- rake (13.0.6)
+ rake (13.1.0)
recaptcha (5.14.0)
+ json
redcarpet (3.6.0)
+ redis (4.7.1)
regexp_parser (2.9.0)
request_store (1.5.1)
rack (>= 1.4)
@@ -505,6 +590,7 @@ GEM
ffi (~> 1.12)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
+ rugged (1.5.1)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.4.0)
@@ -515,11 +601,17 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
+ sawyer (0.9.2)
+ addressable (>= 2.3.5)
+ faraday (>= 0.17.3, < 3)
selectize-rails (0.12.6)
selenium-webdriver (4.10.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
+ sentry-delayed_job (5.9.0)
+ delayed_job (>= 4.0)
+ sentry-ruby (~> 5.9.0)
sentry-rails (5.9.0)
railties (>= 5.0)
sentry-ruby (~> 5.9.0)
@@ -528,41 +620,42 @@ GEM
shoulda-matchers (5.3.0)
activesupport (>= 5.2.0)
simple_po_parser (1.1.6)
- simplecov (0.22.0)
+ simplecov (0.17.1)
docile (~> 1.1)
- simplecov-html (~> 0.11)
- simplecov_json_formatter (~> 0.1)
- simplecov-cobertura (2.1.0)
- rexml
- simplecov (~> 0.19)
- simplecov-html (0.12.3)
- simplecov_json_formatter (0.1.4)
- skylight (5.3.4)
- activesupport (>= 5.2.0)
+ json (>= 1.8, < 3)
+ simplecov-html (~> 0.10.0)
+ simplecov-cobertura (1.4.2)
+ simplecov (~> 0.8)
+ simplecov-html (0.10.2)
snaky_hash (2.0.1)
hashie
version_gem (~> 1.1, >= 1.1.1)
- sprockets (4.2.0)
+ sprockets (4.2.1)
concurrent-ruby (~> 1.0)
rack (>= 2.2.4, < 4)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
- sqlite3 (1.6.3)
- mini_portile2 (~> 2.8.0)
+ sqlite3 (1.6.3-arm64-darwin)
+ sqlite3 (1.6.3-x86_64-darwin)
+ sqlite3 (1.6.3-x86_64-linux)
ssrf_filter (1.1.2)
- stripe (5.53.0)
+ stripe (5.55.0)
stripe-ruby-mock (3.1.0.rc3)
dante (>= 0.2.0)
multi_json (~> 1.0)
stripe (> 5, < 6)
+ sys-uname (1.2.2)
+ ffi (~> 1.1)
sysexits (1.2.0)
temple (0.10.1)
- thor (1.2.2)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ thor (1.3.1)
tilt (2.1.0)
timecop (0.9.6)
- timeout (0.3.2)
+ timeout (0.4.1)
transitions (1.3.0)
ttfunk (1.7.0)
turbolinks (5.2.1)
@@ -576,9 +669,10 @@ GEM
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (2.5.0)
+ uniform_notifier (1.16.0)
unobtrusive_flash (3.3.1)
railties
- version_gem (1.1.2)
+ version_gem (1.1.3)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.2.0)
@@ -594,6 +688,7 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
+ webrick (1.8.1)
websocket (1.2.9)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
@@ -605,18 +700,22 @@ GEM
zeitwerk (2.6.13)
PLATFORMS
- ruby
+ arm64-darwin-23
+ x86_64-darwin-23
+ x86_64-linux
DEPENDENCIES
active_model_serializers
acts_as_commentable_with_threading
acts_as_list
ajax-datatables-rails
+ annotate
autoprefixer-rails
awesome_nested_set
bootstrap-sass (~> 3.4.0)
bootstrap-switch-rails (= 3.3.3)
bootstrap3-datetimepicker-rails (~> 4.17.47)
+ bullet
byebug
cancancan
capybara
@@ -628,6 +727,8 @@ DEPENDENCIES
cloudinary
cocoon
countable-rails
+ cucumber-rails
+ cucumber-rails-training-wheels
daemons
dalli
database_cleaner
@@ -637,16 +738,19 @@ DEPENDENCIES
dotenv-rails
factory_bot_rails
faker
+ faraday-retry
feature
font-awesome-sass
geckodriver-helper
gravtastic
+ haml-lint
haml-rails
haml_lint
+ httparty
i18n_data
icalendar
iso-639
- jquery-datatables-rails
+ jquery-datatables
jquery-rails
jquery-ui-rails (~> 6.0.1)
json-schema
@@ -658,25 +762,30 @@ DEPENDENCIES
mina
mini_magick
money-rails
- mysql2
next_rails
omniauth
+ omniauth-discourse!
omniauth-facebook
omniauth-github
omniauth-google-oauth2
omniauth-openid
omniauth-rails_csrf_protection
- paper_trail
+ pagy (< 4.0)
+ paper_trail (< 13)
pdf-inspector
pg
prawn-qrcode
prawn-rails
+ pronto
+ pronto-haml
+ pronto-rubocop
puma
rails (~> 7.0)
rails-controller-testing
rails-i18n
recaptcha
redcarpet
+ redis
responders (~> 3.0)
rexml
rolify
@@ -691,10 +800,12 @@ DEPENDENCIES
ruby-oembed
sass-rails (>= 4.0.2)
selectize-rails
+ sentry-delayed_job
sentry-rails
+ sentry-ruby
shoulda-matchers
+ simplecov (< 0.18)
simplecov-cobertura
- skylight
sprockets-rails
sqlite3
stripe
@@ -710,7 +821,7 @@ DEPENDENCIES
whenever
RUBY VERSION
- ruby 3.1.4
+ ruby 3.2.2
BUNDLED WITH
- 2.3.26
+ 2.5.6
diff --git a/Gemfile.next.lock b/Gemfile.next.lock
index 0e8acc2e3..cdb7ec0a4 100644
--- a/Gemfile.next.lock
+++ b/Gemfile.next.lock
@@ -1,3 +1,12 @@
+GIT
+ remote: https://github.com/snap-cloud/omniauth-discourse.git
+ revision: 96dbfcb55bf7bef5a33f83a4919879c891af367c
+ specs:
+ omniauth-discourse (1.1.0)
+ addressable (~> 2.7)
+ omniauth (~> 2.0)
+ rack
+
GEM
remote: https://rails-assets.org/
specs:
@@ -107,6 +116,9 @@ GEM
afm (0.2.2)
ajax-datatables-rails (1.3.1)
zeitwerk
+ annotate (3.2.0)
+ activerecord (>= 3.2, < 8.0)
+ rake (>= 10.4, < 14.0)
archive-zip (0.12.0)
io-like (~> 0.3.0)
ast (2.4.2)
@@ -120,10 +132,13 @@ GEM
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
sassc (>= 2.0.0)
- bootstrap-switch-rails (3.3.5)
+ bootstrap-switch-rails (3.3.3)
bootstrap3-datetimepicker-rails (4.17.47)
momentjs-rails (>= 2.8.1)
builder (3.2.4)
+ bullet (7.0.7)
+ activesupport (>= 3.0.0)
+ uniform_notifier (~> 1.11)
byebug (11.1.3)
cancancan (3.3.0)
capybara (3.35.3)
@@ -165,8 +180,10 @@ GEM
aws_cf_signer
rest-client (>= 2.0.0)
cocoon (1.2.15)
+ coderay (1.1.3)
colorize (0.8.1)
concurrent-ruby (1.1.9)
+ connection_pool (2.3.0)
countable-rails (0.0.1)
railties (>= 3.1)
countries (4.0.1)
@@ -178,6 +195,46 @@ GEM
crack (0.4.5)
rexml
crass (1.0.6)
+ cucumber (7.1.0)
+ builder (~> 3.2, >= 3.2.4)
+ cucumber-core (~> 10.1, >= 10.1.0)
+ cucumber-create-meta (~> 6.0, >= 6.0.1)
+ cucumber-cucumber-expressions (~> 14.0, >= 14.0.0)
+ cucumber-gherkin (~> 22.0, >= 22.0.0)
+ cucumber-html-formatter (~> 17.0, >= 17.0.0)
+ cucumber-messages (~> 17.1, >= 17.1.1)
+ cucumber-wire (~> 6.2, >= 6.2.0)
+ diff-lcs (~> 1.4, >= 1.4.4)
+ mime-types (~> 3.3, >= 3.3.1)
+ multi_test (~> 0.1, >= 0.1.2)
+ sys-uname (~> 1.2, >= 1.2.2)
+ cucumber-core (10.1.1)
+ cucumber-gherkin (~> 22.0, >= 22.0.0)
+ cucumber-messages (~> 17.1, >= 17.1.1)
+ cucumber-tag-expressions (~> 4.1, >= 4.1.0)
+ cucumber-create-meta (6.0.4)
+ cucumber-messages (~> 17.1, >= 17.1.1)
+ sys-uname (~> 1.2, >= 1.2.2)
+ cucumber-cucumber-expressions (14.0.0)
+ cucumber-gherkin (22.0.0)
+ cucumber-messages (~> 17.1, >= 17.1.1)
+ cucumber-html-formatter (17.0.0)
+ cucumber-messages (~> 17.1, >= 17.1.0)
+ cucumber-messages (17.1.1)
+ cucumber-rails (2.6.1)
+ capybara (>= 2.18, < 4)
+ cucumber (>= 3.2, < 9)
+ mime-types (~> 3.3)
+ nokogiri (~> 1.10)
+ railties (>= 5.0, < 8)
+ rexml (~> 3.0)
+ webrick (~> 1.7)
+ cucumber-rails-training-wheels (1.0.0)
+ cucumber-rails (>= 1.1.1)
+ cucumber-tag-expressions (4.1.0)
+ cucumber-wire (6.2.1)
+ cucumber-core (~> 10.1, >= 10.1.0)
+ cucumber-cucumber-expressions (~> 14.0, >= 14.0.0)
daemons (1.4.0)
dalli (2.7.11)
dante (0.2.0)
@@ -241,24 +298,49 @@ GEM
fastimage (2.2.5)
feature (1.4.0)
ffi (1.15.3)
- font-awesome-rails (4.7.0.8)
- railties (>= 3.2, < 8.0)
+ flay (2.13.0)
+ erubi (~> 1.10)
+ path_expander (~> 1.0)
+ ruby_parser (~> 3.0)
+ sexp_processor (~> 4.0)
+ font-awesome-sass (6.3.0)
+ sassc (~> 2.0)
+ formatador (1.1.0)
geckodriver-helper (0.24.0)
archive-zip (~> 0.7)
+ gitlab (4.19.0)
+ httparty (~> 0.20)
+ terminal-table (>= 1.5.1)
globalid (1.0.0)
activesupport (>= 5.0)
gravtastic (3.2.6)
+ guard (2.18.0)
+ formatador (>= 0.2.4)
+ listen (>= 2.7, < 4.0)
+ lumberjack (>= 1.0.12, < 2.0)
+ nenv (~> 0.1)
+ notiffany (~> 0.0)
+ pry (>= 0.13.0)
+ shellany (~> 0.0)
+ thor (>= 0.18.1)
+ guard-compat (1.2.1)
+ guard-rspec (4.7.3)
+ guard (~> 2.1)
+ guard-compat (~> 1.1)
+ rspec (>= 2.99.0, < 4.0)
haml (5.2.2)
temple (>= 0.8.0)
tilt
+ haml-lint (0.999.999)
+ haml_lint
haml-rails (2.0.1)
actionpack (>= 5.1)
activesupport (>= 5.1)
haml (>= 4.0.6, < 6.0)
html2haml (>= 1.0.1)
railties (>= 5.1)
- haml_lint (0.37.1)
- haml (>= 4.0, < 5.3)
+ haml_lint (0.45.0)
+ haml (>= 4.0, < 6.2)
parallel (~> 1.10)
rainbow
rubocop (>= 0.50.0)
@@ -275,6 +357,9 @@ GEM
http-accept (1.7.0)
http-cookie (1.0.4)
domain_name (~> 0.5)
+ httparty (0.21.0)
+ mini_mime (>= 1.0.0)
+ multi_xml (>= 0.5.2)
i18n (1.9.1)
concurrent-ruby (~> 1.0)
i18n_data (0.13.0)
@@ -287,11 +372,7 @@ GEM
io-like (0.3.1)
io-wait (0.2.1)
iso-639 (0.3.5)
- jquery-datatables-rails (3.4.0)
- actionpack (>= 3.1)
- jquery-rails
- railties (>= 3.1)
- sass-rails
+ jquery-datatables (1.10.20)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
@@ -314,9 +395,13 @@ GEM
letter_opener (~> 1.7)
railties (>= 5.2)
rexml
+ listen (3.8.0)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.13.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
+ lumberjack (1.2.8)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (1.0.1)
@@ -344,9 +429,10 @@ GEM
money (~> 6.13.2)
railties (>= 3.0)
multi_json (1.15.0)
+ multi_test (0.1.2)
multi_xml (0.6.0)
multipart-post (2.1.1)
- mysql2 (0.5.3)
+ nenv (0.3.0)
net-imap (0.2.3)
digest
net-protocol
@@ -369,12 +455,18 @@ GEM
nokogiri (1.13.1)
mini_portile2 (~> 2.7.0)
racc (~> 1.4)
+ notiffany (0.1.3)
+ nenv (~> 0.1)
+ shellany (~> 0.0)
oauth2 (1.4.7)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
+ octokit (6.0.1)
+ faraday (>= 1, < 3)
+ sawyer (~> 0.9)
omniauth (2.0.4)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
@@ -400,12 +492,14 @@ GEM
omniauth (~> 2.0)
open4 (1.3.4)
orm_adapter (0.5.0)
+ pagy (3.11.0)
paper_trail (12.2.0)
activerecord (>= 5.2)
request_store (~> 1.1)
parallel (1.21.0)
parser (3.1.0.0)
ast (~> 2.4.1)
+ path_expander (1.1.1)
pdf-core (0.9.0)
pdf-inspector (1.3.0)
pdf-reader (>= 1.0, < 3.0.a)
@@ -428,6 +522,29 @@ GEM
prawn-table
prawn-table (0.2.2)
prawn (>= 1.3.0, < 3.0.0)
+ pronto (0.11.1)
+ gitlab (>= 4.4.0, < 5.0)
+ httparty (>= 0.13.7, < 1.0)
+ octokit (>= 4.7.0, < 7.0)
+ rainbow (>= 2.2, < 4.0)
+ rexml (>= 3.2.5, < 4.0)
+ rugged (>= 0.23.0, < 2.0)
+ thor (>= 0.20.3, < 2.0)
+ pronto-flay (0.11.1)
+ flay (~> 2.8)
+ pronto (~> 0.11.0)
+ pronto-haml (0.11.1)
+ haml_lint (~> 0.23)
+ pronto (~> 0.11.0)
+ pronto-rubocop (0.11.5)
+ pronto (~> 0.11.0)
+ rubocop (>= 0.63.1, < 2.0)
+ pry (0.14.2)
+ coderay (~> 1.1)
+ method_source (~> 1.0)
+ pry-byebug (3.10.1)
+ byebug (~> 11.0)
+ pry (>= 0.13, < 0.15)
public_suffix (4.0.6)
puma (4.3.8)
nio4r (~> 2.0)
@@ -475,9 +592,16 @@ GEM
zeitwerk (~> 2.5)
rainbow (3.1.1)
rake (13.0.6)
+ rb-fsevent (0.11.2)
+ rb-inotify (0.10.1)
+ ffi (~> 1.0)
recaptcha (5.8.1)
json
redcarpet (3.5.1)
+ redis (5.0.6)
+ redis-client (>= 0.9.0)
+ redis-client (0.12.2)
+ connection_pool
regexp_parser (2.2.0)
request_store (1.5.1)
rack (>= 1.4)
@@ -495,6 +619,10 @@ GEM
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.1.0)
+ rspec (3.10.0)
+ rspec-core (~> 3.10.0)
+ rspec-expectations (~> 3.10.0)
+ rspec-mocks (~> 3.10.0)
rspec-activemodel-mocks (1.1.0)
activemodel (>= 3.0)
activesupport (>= 3.0)
@@ -527,7 +655,10 @@ GEM
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.15.1)
parser (>= 3.0.1.1)
- rubocop-rails (2.11.3)
+ rubocop-faker (1.1.0)
+ faker (>= 2.12.0)
+ rubocop (>= 0.82.0)
+ rubocop-rails (2.15.2)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.7.0, < 2.0)
@@ -544,6 +675,7 @@ GEM
ruby_parser (3.17.0)
sexp_processor (~> 4.15, >= 4.15.1)
rubyzip (2.3.2)
+ rugged (1.5.1)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.4.0)
@@ -554,17 +686,26 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
+ sawyer (0.9.2)
+ addressable (>= 2.3.5)
+ faraday (>= 0.17.3, < 3)
selectize-rails (0.12.6)
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
+ sentry-delayed_job (5.8.0)
+ delayed_job (>= 4.0)
+ sentry-ruby (~> 5.8.0)
sentry-rails (4.6.5)
railties (>= 5.0)
sentry-ruby-core (~> 4.6.0)
+ sentry-ruby (5.8.0)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
sentry-ruby-core (4.6.5)
concurrent-ruby
faraday
sexp_processor (4.15.3)
+ shellany (0.0.1)
shoulda-matchers (4.5.1)
activesupport (>= 4.2.0)
simplecov (0.21.2)
@@ -576,7 +717,7 @@ GEM
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.3)
sixarm_ruby_unaccent (1.2.0)
- skylight (5.1.0)
+ skylight (5.3.3)
activesupport (>= 5.2.0)
sort_alphabetical (1.1.0)
unicode_utils (>= 1.2.2)
@@ -595,8 +736,12 @@ GEM
multi_json (~> 1.0)
stripe (> 5, < 6)
strscan (3.0.1)
+ sys-uname (1.2.2)
+ ffi (~> 1.1)
sysexits (1.2.0)
temple (0.8.2)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
thor (1.2.1)
tilt (2.0.10)
timecop (0.9.4)
@@ -615,6 +760,7 @@ GEM
unf_ext (0.0.7.7)
unicode-display_width (2.1.0)
unicode_utils (1.4.0)
+ uniform_notifier (1.16.0)
unobtrusive_flash (3.3.1)
railties
warden (1.2.9)
@@ -632,6 +778,7 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
+ webrick (1.8.1)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
@@ -649,11 +796,13 @@ DEPENDENCIES
acts_as_commentable_with_threading
acts_as_list
ajax-datatables-rails
+ annotate
autoprefixer-rails
awesome_nested_set
bootstrap-sass (~> 3.4.0)
- bootstrap-switch-rails (~> 3.3.5)
+ bootstrap-switch-rails (= 3.3.3)
bootstrap3-datetimepicker-rails (~> 4.17.47)
+ bullet
byebug
cancancan
capybara
@@ -665,7 +814,9 @@ DEPENDENCIES
cloudinary
cocoon
countable-rails
- country_select
+ country_select (< 7)
+ cucumber-rails
+ cucumber-rails-training-wheels
daemons
dalli
database_cleaner
@@ -676,14 +827,16 @@ DEPENDENCIES
factory_bot_rails
faker
feature
- font-awesome-rails
+ font-awesome-sass
geckodriver-helper
gravtastic
+ guard-rspec
+ haml-lint
haml-rails
- haml_lint
+ httparty
icalendar
iso-639
- jquery-datatables-rails
+ jquery-datatables
jquery-rails
jquery-ui-rails (~> 6.0.1)
json-schema
@@ -694,26 +847,35 @@ DEPENDENCIES
mina
mini_magick
money-rails
- mysql2
next_rails
- nokogiri (>= 1.8.1)
+ nokogiri
omniauth
+ omniauth-discourse!
omniauth-facebook
omniauth-github
omniauth-google-oauth2
omniauth-openid
omniauth-rails_csrf_protection
- paper_trail
+ pagy (< 4.0)
+ paper_trail (< 13)
pdf-inspector
pg
prawn-qrcode
prawn-rails
- puma (~> 4.3)
+ pronto
+ pronto-flay
+ pronto-haml
+ pronto-rubocop
+ pry
+ pry-byebug
+ puma
rails (~> 7)
+ rails-assets-bootstrap!
rails-assets-bootstrap-markdown!
rails-assets-bootstrap-select!
rails-assets-date.format!
rails-assets-holderjs!
+ rails-assets-jquery!
rails-assets-jquery-smooth-scroll!
rails-assets-markdown!
rails-assets-momentjs!
@@ -726,21 +888,25 @@ DEPENDENCIES
rails-i18n
recaptcha
redcarpet
+ redis
responders (~> 3.0)
+ rexml
rolify
rqrcode
rspec-activemodel-mocks
rspec-rails
- rubocop
+ rubocop-faker
rubocop-rails
rubocop-rspec
ruby-oembed
sass-rails (>= 4.0.2)
selectize-rails
+ sentry-delayed_job
sentry-rails
+ sentry-ruby
shoulda-matchers
simplecov-cobertura
- skylight
+ skylight (~> 5)
sprockets-rails
sqlite3
stripe
@@ -756,7 +922,7 @@ DEPENDENCIES
whenever
RUBY VERSION
- ruby 3.1.0
+ ruby 3.2.2p185
BUNDLED WITH
- 2.3.3
+ 2.3.26
diff --git a/INSTALL_SNAPCON.md b/INSTALL_SNAPCON.md
new file mode 100644
index 000000000..276f8fc21
--- /dev/null
+++ b/INSTALL_SNAPCON.md
@@ -0,0 +1,57 @@
+# Install Snap!Con
+Snap!Con runs Ruby 2.7.6 with Rails 5.2. The backend is provided by PostgreSQL. It is recommended to use RVM to manage the gems for this project. Some JavaScript dependencies are used, which need to be installed via NPM or Yarn.
+
+## Setup
+The recommended setup steps are as follows:
+
+1. Run `npm install` or `yarn` to install the necessary JavaScript dependencies.
+1. Run `bundle config set without production` to ensure only developmental gems are installed.
+1. Run `bundle config set path vendor/bundle` to install gems to `vendor/bundle`.
+1. Run `bundle install` to install the necessary gems.
+1. Configure your environment variables.
+ 1. Run `cp dotenv.example .env`
+ 1. At a bare minimum, you will likely need to provide credentials with which to access your PostgreSQL database. An example might be as follows:
+ ```
+ OSEM_DB_USER= esobeck #this can be computer's username
+ OSEM_DB_PASSWORD= password123 #likely not necessary if using postgress with computer's username
+ ```
+ 1. Other features will require more environment variables. See [Environment Variables](#environment-variables) and [INSTALL.md#configuration](INSTALL.md#configuration) for all the environment variables that may be set.
+1. Run `rake db:setup` (this command and all following commands may need to prefixed with `bundle exec`) to initialize the database.
+
+## Setting Environment Variables for macOS
+
+For developers using macOS, it's necessary to set an environment variable to prevent issues related to forking processes. Before starting the application, please set `OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES` by following these steps:
+
+### For Temporary Use in the Current Terminal Session
+
+Execute the following command in your terminal:
+
+```
+export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
+```
+This will set the environment variable for the duration of your current terminal session. You'll need to run this command each time you open a new terminal window.
+
+## Local Deployment
+
+To run Snap!Con, using [Overmind](https://github.com/DarthSim/overmind) or [Foreman](https://github.com/ddollar/foreman) is recommended. Since a release command is run which automatically performs migrations, it is necessary to flag the `release` command as able to be exited without closing all other processes. The Rails server may be run via the typical `rails server` command, but do note that no jobs will be run.
+
+## Heroku Deployment
+
+**TODO:** Update this section...
+
+When deploying to Heroku, the `heroku/nodejs` buildpack must be run before the `heroku/ruby` buildpack. Since Snap!Con runs on PostgreSQL, the Postgres add-on must also be added to the Heroku deployment.
+
+Heroku Stack: `heroku-22`
+
+### Example Data
+Example data can easily be generated by running `rake data:demo`. Please note that this command can fail if the database is not fresh.
+
+## Environment Variables
+
+In addition to the environment variables detailed under [INSTALL.md#configuration](INSTALL.md#configuration), the following environment variables may be configured.
+
+### `MAILBLUSTER_API_KEY`
+A generated API key necessary to utilize the Mailbluster integration.
+
+### `FULL_CALENDAR_LICENSE_KEY=GPL-My-Project-Is-Open-Source`
+If utilizing the FullCalendar display module, necessary to disable the license key warning.
diff --git a/Procfile b/Procfile
index 8b9e764cc..84cab0c9d 100644
--- a/Procfile
+++ b/Procfile
@@ -1,2 +1,2 @@
-web: bundle exec rails server -b 0.0.0.0
+web: bundle exec puma -C config/puma.rb
worker: bundle exec rails jobs:work
diff --git a/README.md b/README.md
index 69b58e21f..f8805568f 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,27 @@
-[![Build Status](https://github.com/openSUSE/osem/actions/workflows/spec.yml/badge.svg?branch=master)](https://github.com/openSUSE/osem/actions)
-[![Code Climate](https://codeclimate.com/github/openSUSE/osem.png)](https://codeclimate.com/github/openSUSE/osem)
-[![codecov](https://codecov.io/gh/opensuse/osem/branch/master/graph/badge.svg)](https://codecov.io/gh/opensuse/osem)
-[![Dependencies](https://badges.depfu.com/badges/8fcd630367d20f5b48d393774c00c5fd/overview.svg)](https://depfu.com/repos/openSUSE/osem)
+## Srping 2024 CS169L
+[![Specs](https://github.com/snap-cloud/snapcon/actions/workflows/spec.yml/badge.svg)](https://github.com/snap-cloud/snapcon/actions/workflows/spec.yml)
+[![Pivotal Tracker](doc/pivotal_tracker_logo.png)](https://www.pivotaltracker.com/n/projects/2487653)
+[![Maintainability](https://api.codeclimate.com/v1/badges/b7b0d559a03bf218663a/maintainability)](https://codeclimate.com/github/snap-cloud/snapcon/maintainability)
+[![Test Coverage](https://api.codeclimate.com/v1/badges/b7b0d559a03bf218663a/test_coverage)](https://codeclimate.com/github/snap-cloud/snapcon/test_coverage)
+[![codecov](https://codecov.io/gh/snap-cloud/snapcon/branch/snapcon/graph/badge.svg?token=EViEwaSjH4)](https://codecov.io/gh/snap-cloud/snapcon)
+
+
+
+
+# [Snap!Con](https://snapcon.org)
+Forked From:
+## Open Source Event Manager - [osem.io](https://osem.io)
-# Open Source Event Manager - [osem.io](https://osem.io)
![OSEM Logo](doc/osem-logo.png)
An event management tool tailored to Free and Open Source Software conferences.
## Installation
+
Please refer to our [installation guide](INSTALL.md).
## How to contribute to OSEM
+
Please refer to our [contributing guide](CONTRIBUTING.md).
## Contact
diff --git a/app/assets/images/snapcon_logo-original.png b/app/assets/images/snapcon_logo-original.png
new file mode 100644
index 000000000..76c32bbd2
Binary files /dev/null and b/app/assets/images/snapcon_logo-original.png differ
diff --git a/app/assets/images/snapcon_logo.png b/app/assets/images/snapcon_logo.png
new file mode 100644
index 000000000..17f3a3340
Binary files /dev/null and b/app/assets/images/snapcon_logo.png differ
diff --git a/app/assets/images/snapshot-2020.png b/app/assets/images/snapshot-2020.png
new file mode 100644
index 000000000..3d9b05b17
Binary files /dev/null and b/app/assets/images/snapshot-2020.png differ
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index cf12a0859..8284fbf80 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -15,8 +15,14 @@
//= require jquery-ui/widgets/draggable
//= require jquery-ui/widgets/droppable
//= require waypoints/jquery.waypoints
-//= require dataTables/jquery.dataTables
-//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
+
+//= require datatables/jquery.dataTables
+//= require datatables/dataTables.bootstrap
+//= require datatables/extensions/Buttons/dataTables.buttons
+//= require datatables/extensions/Buttons/buttons.bootstrap
+//= require datatables/extensions/Buttons/buttons.html5
+//= require datatables/extensions/Buttons/buttons.dataTables
+
//= require cocoon
//= require bootstrap
//= require Chart.bundle
@@ -47,6 +53,9 @@
//= require selectize
//= require bootstrap-select
//= require osem-survey
+//= require pagy
+//= require fullcalendar-scheduler/main.js
+//= require fullcalendar
$(document).ready(function() {
$('a[disabled=disabled]').click(function(event){
@@ -56,4 +65,6 @@ $(document).ready(function() {
$('body').smoothScroll({
delegateSelector: 'a.smoothscroll'
});
+
+ window.addEventListener("load", Pagy.init);
});
diff --git a/app/assets/javascripts/fullcalendar.js.erb b/app/assets/javascripts/fullcalendar.js.erb
new file mode 100644
index 000000000..e80570776
--- /dev/null
+++ b/app/assets/javascripts/fullcalendar.js.erb
@@ -0,0 +1,92 @@
+$( document ).ready(function() {
+ let calendarEl = document.getElementById('vert-schedule-full-calendar');
+ if (!calendarEl) return; //check that we need a vertical schedule
+ let $fullCalendar = $('#fullcalendar');
+
+ let license_key = "<%= Rails.configuration.fullcalendar[:license_key]%>";
+
+ let offset = $fullCalendar.data('tzOffset');
+ let interval = Math.max(5, $fullCalendar.data('minInterval'));
+ let localOffset = (new Date()).getTimezoneOffset()/60;
+ let startTime = $fullCalendar.data('startHour') - offset - localOffset;
+ let endTime = $fullCalendar.data('endHour') - offset - localOffset;
+ let startDate = new Date($fullCalendar.data('startDate'));
+ let endDate = new Date($fullCalendar.data('endDate'));
+ let event_num_days = (endDate.getTime() - startDate.getTime())/ (1000 * 3600 * 24);
+ let width = Math.min(4, event_num_days);
+ let localName = Intl.DateTimeFormat().resolvedOptions().timeZone;
+ // Program Hours * Minutes / Interval * Min Row Height for an event.
+ let contentHeight = Math.max(400, (endTime - startTime) * 60 / interval * 16);
+ // UTC JS offsets are "-1 *" of how they're displayed.
+ let operator = localOffset < 0 ? '+' : '-';
+ $('.js-localTimezone').text(`(${localName} UTC ${operator}${Math.abs(localOffset)})`);
+
+ let rightHeaderToolbar = 'resourceTimeGridDay,resourceTimeGridFourDay,listDay';
+ if (event_num_days == 1) {
+ rightHeaderToolbar = 'resourceTimeGridDay,listDay';
+ }
+
+ // Remove subevents from the calendar.
+ let filterSubevents = (events) => {
+ return events.filter(event => event.has_parent === false)
+ };
+
+ var calendar = new FullCalendar.Calendar(calendarEl, {
+ schedulerLicenseKey: license_key,
+ nowIndicator: true,
+ now: $fullCalendar.data('now'),
+ contentHeight: contentHeight,
+ expandRows: true,
+ allDaySlot: false,
+ slotMinTime: startTime + ':00:00',
+ slotMaxTime: endTime + ':00:00',
+ // TODO: Set these dynamically.
+ slotDuration: '00:15:00',
+ slotLabelInterval: '00:15:00',
+ slotLabelFormat: {
+ hour: 'numeric',
+ minute: '2-digit',
+ omitZeroMinute: true,
+ meridiem: 'short'
+ },
+ validRange: {
+ start: $fullCalendar.data('startDate'),
+ end: $fullCalendar.data('endDate')
+ },
+ timeZone: 'local',
+ initialDate: $fullCalendar.data('day'),
+ initialView: 'resourceTimeGridDay',
+ resources: $fullCalendar.data('rooms'),
+ resourceOrder: 'order, title',
+ // TODO: Move this to a XHR.
+ events: filterSubevents($fullCalendar.data('events')),
+ displayEventEnd: false, // TODO change in list view.
+ displayEventTime: false,
+ titleFormat: { // will produce something like "Tues, September 18"
+ month: 'long',
+ day: 'numeric',
+ weekday: 'short'
+ },
+ headerToolbar: {
+ left: 'prev,next',
+ center: 'title',
+ right: rightHeaderToolbar
+ },
+ // TODO: Make this conference Specific?
+ views: {
+ resourceTimeGridFourDay: {
+ type: 'resourceTimeGrid',
+ duration: { days: width },
+ buttonText: 'overview',
+ datesAboveResources: true
+ },
+ listDay: {
+ type: 'listDay',
+ displayEventEnd: true,
+ displayEventTime: true
+ }
+ }
+ });
+
+ calendar.render();
+});
diff --git a/app/assets/javascripts/osem-datatables.js b/app/assets/javascripts/osem-datatables.js
index f5cfdd47e..5f8122330 100644
--- a/app/assets/javascripts/osem-datatables.js
+++ b/app/assets/javascripts/osem-datatables.js
@@ -1,5 +1,7 @@
$(function () {
$.extend(true, $.fn.dataTable.defaults, {
+ "buttons": ["csv"],
+ "dom": "lBfrtip",
"stateSave": true,
"autoWidth": false,
"pagingType": "full_numbers",
diff --git a/app/assets/javascripts/osem-datepickers.js b/app/assets/javascripts/osem-datepickers.js
index d2c01bd12..e603d15d0 100644
--- a/app/assets/javascripts/osem-datepickers.js
+++ b/app/assets/javascripts/osem-datepickers.js
@@ -1,5 +1,3 @@
-// get current_date
-var today = new Date().toISOString().slice(0, 10);
$(function () {
$("input[id^='datetimepicker']").datetimepicker({
useCurrent: false,
@@ -25,9 +23,6 @@ $(function () {
format: "YYYY-MM-DD"
});
- // today <= start_registration <= end_registration <= end_conference
- var end_conference = $('form').data('end-conference');
-
$('#registration-period-start-datepicker').datetimepicker({
format: 'YYYY-MM-DD'
});
diff --git a/app/assets/javascripts/osem-schedule.js b/app/assets/javascripts/osem-schedule.js
index aeb76ae37..45df23908 100644
--- a/app/assets/javascripts/osem-schedule.js
+++ b/app/assets/javascripts/osem-schedule.js
@@ -1,3 +1,5 @@
+// ADMIN SCHEDULE
+
var url; // Should be initialize in Schedule.initialize
var schedule_id; // Should be initialize in Schedule.initialize
@@ -114,18 +116,57 @@ $(document).ready( function() {
});
});
-function eventClicked(e, element){
+
+// PUBLIC SCHEDULE
+
+function starClicked(e) {
+ // stops the click from propagating
+ if (!e) var e = window.event;
+ e.preventDefault();
+ e.cancelBubble = true;
+ if (e.stopPropagation) e.stopPropagation();
+
+ var callback = function(data) {
+ $(e.target).toggleClass('fa-solid fa-regular');
+ }
+
+ var params = { favourite_user_id: $(e.target).data('user') };
+
+ $.ajax({
+ url: $(e.target).data('url'),
+ type: 'PATCH',
+ data: params,
+ success: callback,
+ dataType : 'json'
+ });
+}
+
+function eventClicked(e, element) {
+ if (e.target.href) {
+ return;
+ }
var url = $(element).data('url');
- if(e.ctrlKey)
- window.open(url,'_blank');
- else
+ if (e.ctrlKey || e.metaKey) {
+ window.open(url, '_blank');
+ } else {
window.location = url;
+ }
+}
+
+function updateFavouriteStatus(options) {
+ if (options.loggedIn === false) {
+ $('.js-toggleEvent').hide();
+ }
+
+ options.events.forEach(function (id) {
+ $(`#eventFavourite-${id}`).removeClass('fa-regular').addClass('fa-solid');
+ });
}
/* Links inside event-panel (to make ctrl + click work for these links):
= link_to text, '#', onClick: 'insideLinkClicked();', 'data-url' => url
*/
-function insideLinkClicked(event){
+function insideLinkClicked(event) {
// stops the click from propagating
if (!event) // for IE
var event = window.event;
@@ -133,7 +174,7 @@ function insideLinkClicked(event){
if (event.stopPropagation) event.stopPropagation();
var url = $(event.target).data('url');
- if(event.ctrlKey)
+ if(event.ctrlKey || e.metaKey)
window.open(url,'_blank');
else
window.location = url;
diff --git a/app/assets/javascripts/osem.js b/app/assets/javascripts/osem.js
index 240a48c95..87049a23a 100644
--- a/app/assets/javascripts/osem.js
+++ b/app/assets/javascripts/osem.js
@@ -4,7 +4,7 @@ $(function () {
* releases a key on the keyboard
*/
$("#user_biography").bind('keyup', function() {
- word_count(this, 'bio_length', 150);
+ word_count(this, 'bio-length', 150);
} );
/**
@@ -48,7 +48,8 @@ $(function () {
var id = $(this).attr('id');
$('.' + id).collapse('hide');
- $('#' + $(this).val() + '-help.' + id).collapse('show');
+ $(`#event_type_${$(this).val()}-help.${id}`).collapse('show');
+ $(`#event_type_${$(this).val()}-instructions.${id}`).collapse('show');w
});
$('.dropdown-toggle').dropdown();
@@ -142,13 +143,84 @@ function word_count(text, divId, maxcount) {
});
};
+function replace_defaut_submission_text(input_selector, new_text, valid_defaults) {
+ let $area = $(input_selector);
+ let current_text = $area.val();
+
+ if (!current_text) {
+ $area.val(new_text);
+ $area.trigger('change');
+ return;
+ }
+
+ valid_defaults.some(default_text => {
+ if (current_text == default_text) {
+ $area.val(new_text);
+ $area.trigger('change');
+ return true;
+ }
+ });
+}
+
+// TODO-SNAPCON: Verify what is on the proposals _from from upstream
+/* Wait for the DOM to be ready before attaching events to the elements */
+$( document ).ready(function() {
+ /* Set the minimum and maximum proposal abstract word length */
+ function updateEventTypeRequirements() {
+ var $selected = $("#event_event_type_id option:selected")
+ var max = $selected.data("max-words");
+ var min = $selected.data("min-words");
+
+ // We replace the default text only if the current field is empty,
+ // or is set to the default text of another event type.
+ replace_defaut_submission_text(
+ '#event_submission_text',
+ $selected.data("instructions"),
+ $("#event_event_type_id option").toArray().map(e => $(e).data('instructions'))
+ );
+
+ $("#abstract-maximum-word-count").text(max);
+ $("#submission-maximum-word-count").text(max);
+ $("#abstract-minimum-word-count").text(min);
+ $("#submission-minimum-word-count").text(min);
+ word_count($('#event_abstract').get(0), 'abstract-count', max);
+ }
+ $("#event_event_type_id").change(updateEventTypeRequirements);
+ updateEventTypeRequirements();
+
+ /* Count the proposal abstract length */
+ $("#event_abstract").on('input', function() {
+ var $selected = $("#event_event_type_id option:selected")
+ var max = $selected.data("max-words");
+ word_count(this, 'abstract-count', max);
+ } );
+
+ /* Count the submission text length */
+ $("#event_submission_text").bind('change keyup paste input', function() {
+ var $selected = $("event_event_type_id option:selected")
+ var max = $selected.data("max-words");
+ word_count(this, 'submission-count', max);
+ });
+
+ /* Listen for reset template button, wait for confirm, and reset. */
+ $('.js-resetSubmissionText').click((e) => {
+ let $selected = $("#event_event_type_id option:selected");
+ let $this = $(e.target);
+ let affirm = confirm($this.data('confirm'));
+ if (affirm) {
+ let sub_text = $('#event_submission_text');
+ sub_text.val($selected.data('instructions'));
+ sub_text.trigger('change');
+ }
+ });
+});
+
/* Commodity function for modal windows */
window.build_dialog = function(selector, content) {
// Close it and remove content if it's already open
$("#" + selector).modal('hide');
$("#" + selector).remove();
- // Add new content and pops it up
- $("body").append("
\n" + content + "
");
+ $("body").append(`${content}
`);
$("#" + selector).modal();
}
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 576997ae2..7c23a0d90 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -1,10 +1,18 @@
/*
*= require strap-on
- *= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
+ *= require datatables/dataTables.bootstrap
+ *= require datatables/extensions/Buttons/buttons.dataTables
+ *= require datatables/extensions/Buttons/buttons.bootstrap
+
+ *= require selectize
+ *= require selectize.bootstrap3
+ *= require bootstrap-markdown
+ *= require bootstrap-datetimepicker
+ *= require bootstrap-select
+
*= require osem
*= require osem-rating
*= require osem-schedule
- *= require osem-schedule-print
*= require osem-dashboard
*= require osem-splash
*= require osem-fonts
@@ -18,4 +26,6 @@
*= require selectize.bootstrap3
*= require bootstrap-select
*= require conferences
+
+ *= require fullcalendar-scheduler/main.css
*/
diff --git a/app/assets/stylesheets/osem-dashboard.scss b/app/assets/stylesheets/osem-dashboard.scss
index 9e336c1a3..fbab73114 100644
--- a/app/assets/stylesheets/osem-dashboard.scss
+++ b/app/assets/stylesheets/osem-dashboard.scss
@@ -1,26 +1,34 @@
@import "breakpoints.scss";
-.dashbox span.fa-solid {
- line-height: 1.1em;
+.dashbox span {
+ margin-bottom: 2px;
+
+ @include breakpoint(xs) {
+ font-size: 1.5em;
+ }
@include breakpoint(sm) {
- font-size: 3em;
+ font-size: 2em;
}
@include breakpoint(md) {
- font-size: 4em;
+ font-size: 3.5em;
}
@include breakpoint(lg) {
font-size: 5em;
}
}
-.dashbox {
- padding-top: 20px;
- padding-bottom: 20px;
-}
+.dashbox.panel {
+ padding: 10px;
+ display: -webkit-flex;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.4rem;
+ line-height: 1.6rem;
-.dashbox label {
- display: block;
- font-size: 1.1em;
+ label {
+ line-height: 2rem;
+ }
}
.todolist-missing span {
diff --git a/app/assets/stylesheets/osem-navbar.scss b/app/assets/stylesheets/osem-navbar.scss
index 011ebc890..4df2423f9 100644
--- a/app/assets/stylesheets/osem-navbar.scss
+++ b/app/assets/stylesheets/osem-navbar.scss
@@ -1,4 +1,6 @@
@import "breakpoints.scss";
+@import "osem-variables.scss";
+
.nav-osem {
border: none;
@include breakpoint(xs) {
@@ -29,7 +31,28 @@
}
}
.dropdown-menu {
- padding: 17px;
min-width: 225px;
}
+
+ &.navbar-default {
+ .navbar-nav > .open > a {
+ &:hover, &:focus {
+ color: $navbar-default-link-color;
+ }
+ }
+ }
+
+ .navbar-brand img {
+ max-height: 100%;
+ }
+
+ .trapezoid {
+ border-top-color: $navbar-default-bg;
+ }
+
+ .profile-thumbnail {
+ border-radius: 3px;
+ max-height: 20px;
+ max-width: 20px;
+ }
}
diff --git a/app/assets/stylesheets/osem-rating.scss b/app/assets/stylesheets/osem-rating.scss
index f686fbe2b..b4a43c28f 100644
--- a/app/assets/stylesheets/osem-rating.scss
+++ b/app/assets/stylesheets/osem-rating.scss
@@ -1,8 +1,9 @@
/* Styling for voting on proposals*/
.rating {
background: image-url("star.png") 0 0;
- width: 24px;
- height: 24px;
+ background-size: 20px;
+ width: 20px;
+ height: 20px;
display: inline-block;
float: left;
diff --git a/app/assets/stylesheets/osem-schedule-print.css b/app/assets/stylesheets/osem-schedule-print.css
deleted file mode 100644
index 310c8eb9d..000000000
--- a/app/assets/stylesheets/osem-schedule-print.css
+++ /dev/null
@@ -1,54 +0,0 @@
-@media print {
-
-.navbar {
- display: none;
-}
-.nav li, .tab-pane, #messages, #footer, h2, .text-error, br, .label, .schedule-subtitle, .schedule-track {
- display: none;
-}
-.nav li.active, .tab-pane .active {
- display: block;
- width: 100%;
- text-align: center;
- text-size: 12px;
- border-style: none;
- font-weight: bold;
-}
-.nav-tabs {
- border-bottom-style: none;
-}
-.nav-tabs > .active > a {
- border-style: none;
-}
-#wrap {
- min-height:0px;
- height:auto;
- margin:0px;
- line-height: 12px;
- font-size: 10px;
-}
-#content {
- position: absolute;
- top: 0px;
- left: 0px;
- max-width: 210mm !important;
- max-height: 297mm !important;
- padding: 10px;
-}
-#schedule td.event {
- background-image: none;
- border-style:solid;
- border-width: 2px;
- border-color: black;
- line-height: auto;
- font-size: 10px;
-}
-.event img, .img-circle {
- display: none;
-}
-.table th, .table td {
- line-height: 12px;
- padding: 3px;
-}
-
-}
diff --git a/app/assets/stylesheets/osem-schedule.scss b/app/assets/stylesheets/osem-schedule.scss
index 1cf1efecf..f722a41e9 100644
--- a/app/assets/stylesheets/osem-schedule.scss
+++ b/app/assets/stylesheets/osem-schedule.scss
@@ -1,14 +1,4 @@
-#schedule-content .carousel-control{
- width: 12%;
-}
-
-@media (min-width: 768px){
- #schedule-content .carousel-control{
- width: 5%;
- }
-}
-
-.room-name{
+.room-name {
font-weight: bold;
padding:10px;
margin-top: 30px;
@@ -24,7 +14,7 @@
overflow: hidden;
}
-.schedule-room-slot{
+.schedule-room-slot {
padding: 2px 3px;
border: 1px solid #848484;
height: 58px;
@@ -32,21 +22,43 @@
font-size: 13px;
}
-.schedule-event{
+.schedule-event {
padding: 7px;
border: 2px solid #151515;
position:relative;
z-index:1;
- cursor:move;
+ cursor: move;
+
+ a {
+ color: black;
+ }
+}
+
+.schedule-room-slot.compact {
+ line-height: 12px;
+ font-size: 10px;
+ padding: 1px;
}
+// When an event is in a room.
.schedule-room-slot .schedule-event.compact {
- margin-top: -23px;
- margin-left: 20%;
- width: 80%;
+ margin-top: -14px;
+ width: 85%;
+ margin-left: 15%;
}
-.schedule-event-text{
+.schedule-event.compact {
+ font-size: 75%;
+ padding: 0;
+ border-width: 1px;
+
+ .schedule-event-text {
+ line-height: 12px;
+ overflow: hidden;
+ }
+}
+
+.schedule-event-text {
display: -webkit-box;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
@@ -54,25 +66,21 @@
overflow: hidden;
}
+.schedule-event.compact {
+ .schedule-event-delete-button {
+ padding: 1px;
+ margin-right: 3px;
+ }
+}
+
.schedule-event-delete-button {
- font-weight:bold;
+ font-weight: bold;
cursor: pointer;
- padding: 0px 3px;
- border: 1px solid grey;
+ padding: 0 3px;
margin-right: 5px;
+ color: black;
}
-#schedule td.event {
- background-image: linear-gradient(to top, rgb(247,250,242) 24%, rgb(194,232,190) 97%, rgb(194,232,190) 100%);
-
-cursor: pointer;
-padding: 3px 2px;
-}
-
-#schedule td.event:hover
-{
- background-image: linear-gradient(to top, rgb(247,250,242) 24%, rgb(83,171,74) 97%, rgb(83,171,74) 100%);
-}
.flexvideo {
position: relative;
@@ -96,29 +104,16 @@ padding: 3px 2px;
margin-top: 10px;
}
-.schedule-title, .schedule-subtitle, .schedule-speaker, .schedule-track {
- clear: none;
- display: block;
-}
-.schedule-subtitle {
- color: rgb(185, 74, 72);
-}
-
-a.unstyled-link {
- text-decoration: none;
- color: #333333;
- outline: 0;
- }
-
-.program-dropdown, .schedule-dropdown{
+.program-dropdown {
margin-left: 20%;
margin-right: 20%;
- margin-top: 40px;
+ margin-top: 10px;
margin-bottom: 20px;
}
-.schedule-dropdown > button, .program-dropdown > button, .program-dropdown > .dropdown-menu, .schedule-dropdown > .dropdown-menu{
+.program-dropdown > button,
+.program-dropdown > .dropdown-menu {
width: 100%;
}
@@ -146,42 +141,6 @@ a.unstyled-link {
hyphens: auto;
}
-.room, .event-title{
- font-size: 11px;
- line-height: 17px;
- overflow: hidden;
-}
-
-td.room{
- width: 15%;
- padding: 3px !important;
- max-width:15%;
-}
-
-th.date{
- font-size: 12px;
- padding: 4px 0px 4px 2px !important;
-}
-
-.speaker-pic{
- padding: 4px 2px;
-}
-
-.schedule-table{
- width: 100%;
- margin:0px;
- padding:0px;
-}
-
-td.no-padding{
- padding:0px !important;
-}
-
-.all-events-title{
- margin-top: 40px;
- border-bottom: 1px solid #848484;
-}
-
.date-title{
font-size: 23px;
font-weight: bold;
@@ -189,136 +148,40 @@ td.no-padding{
display:inline-block;
}
-.start-time{
- font-size: 16px;
- padding-left: 30px;
- margin-top: 32px;
-}
-
-.new-time-event{
- margin-top: 20px;
-}
-
-.date-content{
+.date-content {
border-bottom: 1px solid #6E6E6E;
- margin-top: 30px;
+ margin: 30px 0 20px;
}
.unscheduled-event{
margin-top: 8px;
}
-.all-speaker-pic{
- padding: 20px 10px 20px 10px;
-}
-
-.track{
+.track {
padding: 0px 5px 0px 5px;
display: inline-block;
}
-.event-panel{
- cursor: pointer;
- }
-
-.program-dropdown{
- margin-left: 20%;
- margin-right: 20%;
- margin-top: 40px;
-}
-
-.program-dropdown > button{
- width: 100%;
-}
-
-.program-dropdown > .dropdown-menu{
- width: 100%;
-}
-
-.no-events-day{
+.no-events-day {
color: #D8D8D8 !important;
}
-.schedule-label{
- height: 14px;
- line-height: 11px;
- overflow: hidden;
- display: inline-block;
- white-space: normal;
- padding-left: 1px;
- padding-right: 1px;
- font-size: 7px;
-}
-
.non_schedulable{
opacity: 0.5;
}
-/* Small devices (tablets, 768px and up) */
-@media (min-width: 768px) {
- .room, .event-title{
- font-size: 12px;
- }
-
- td.room{
- width: 12%;
- max-width:12%;
- }
-
- th.date{
- font-size: 13px;
- }
-
- td.event{
- padding: 3px 3px !important;
- }
-
- .schedule-label{
- padding-left: 2px;
- padding-right: 3px;
- }
-}
-
-/* Medium devices (desktops, 992px and up) */
-@media (min-width: 992px) {
- .room, .event-title{
- font-size: 13px;
- }
-
- td.room{
- width: 10%;
- max-width: 10%;
- }
+.event-panel-title {
+ margin: auto 3px;
+ flex: 1;
+ line-height: 1.4;
- .schedule-label{
- height: 15px;
- line-height: 12px;
- font-size: 7px;
+ // Override some bootstrap
+ small {
+ line-height: 1.6;
}
}
-/* Large devices (large desktops, 1200px and up) */
-@media (min-width: 1200px) {
- .room, .event-title{
- font-size: 14px;
- }
-
- .schedule-label{
- padding-left: 2px;
- padding-right: 2px;
- font-size: 8px;
- }
-
- td.room{
- width: 9%;
- max-width: 9%;
- }
-
- td.event{
- padding: 3px 6px !important;
- }
-
- th.date{
- font-size: 14px;
- }
+// Override Boostrap setting.
+h3.event-panel-title small {
+ line-height: 1.4;
}
diff --git a/app/assets/stylesheets/osem-splash.scss b/app/assets/stylesheets/osem-splash.scss
index 35a27dca5..15354cfc2 100644
--- a/app/assets/stylesheets/osem-splash.scss
+++ b/app/assets/stylesheets/osem-splash.scss
@@ -1,23 +1,37 @@
@import "breakpoints.scss";
+@import "osem-variables.scss";
#splash {
// Counter the general padding for #content
- margin-bottom: -60px;
+ margin-bottom: -65px;
+
section {
padding-top: 60px;
padding-bottom: 60px;
+
+ .trapezoid {
+ top: 60px + $trapezoid-height;
+ }
}
section:nth-child(even) {
- background: none repeat scroll 0 0 #e0e0e0;
- color: #555;
+ background: none repeat scroll 0 0 #eee;
+ color: #333;
+
+ .trapezoid {
+ border-top-color: #eee;
+ }
}
section:nth-child(odd) {
background: none repeat scroll 0 0 #ffffff;
- color: #7b7b7b;
+ color: #333;
.thumbnail {
background: none repeat scroll 0 0 #e0e0e0;
}
+
+ .trapezoid {
+ border-top-color: #fff;
+ }
}
.cta-button {
@@ -32,11 +46,15 @@
padding-top: 100px;
padding-bottom: 100px;
.container {
- #header {
+ #header-no-image {
background-color: rgba(224, 224, 224,.80);
padding-top: 20px;
padding-bottom: 20px;
}
+
+ #header-image {
+ color: #FFF;
+ }
}
}
@@ -67,6 +85,10 @@
#venue-pic {
margin-top: 20px;
}
+ // The venue section has less padding.
+ .trapezoid {
+ top: $trapezoid-height;
+ }
}
#lodging {
@@ -93,6 +115,14 @@
}
}
+ #tickets {
+ a {
+ &:hover, &:focus {
+ text-decoration: none;
+ }
+ }
+ }
+
#sponsors {
.img-sponsor {
max-height: 100px;
@@ -115,15 +145,18 @@
}
}
+ .social-media.trapezoid {
+ border-top-color: #0C3559; // TODO: Use @conference color?
+ }
+
#social-media{
- background: none repeat scroll 0 0 #2f2f2f;
- padding-top: 60px;
- padding-bottom: 60px;
+ background: none repeat scroll 0 0 #0C3559;
+ padding: 50px 20px;
i{
- color: #4a4a4a;
+ color: #FFF;
}
i:hover{
- color: #16a085;
+ color: #F2E205;
}
a{
padding-left: 100px;
@@ -164,11 +197,11 @@
i.fa-solid {
line-height: inherit;
}
- &.show {
- visibility:visible;
- cursor:pointer;
- opacity: 1.0;
- }
+ // &.show {
+ // visibility:visible;
+ // cursor:pointer;
+ // opacity: 1.0;
+ // }
a {
color: white;
}
diff --git a/app/assets/stylesheets/osem-variables.scss b/app/assets/stylesheets/osem-variables.scss
new file mode 100644
index 000000000..1ae23c039
--- /dev/null
+++ b/app/assets/stylesheets/osem-variables.scss
@@ -0,0 +1,14 @@
+// This file is included *early* in CSS order!
+// These variables change bootstrap defaults.
+
+$navbar-default-bg: #0C3559;
+$navbar-default-link-active-bg: rgb(8,8,8);
+$navbar-default-border: 1px solid #d4d4d4;
+$navbar-default-color: #FFF;
+$navbar-default-link-color: #FFF;
+$navbar-default-link-hover-color: #F2E205;
+$navbar-default-link-active-hover-color: #FFF;
+
+
+// The hiegh of the section tabs that are like Snap! blocks.
+$trapezoid-height: 14px;
diff --git a/app/assets/stylesheets/osem.scss b/app/assets/stylesheets/osem.scss
index 04da1a377..999d9f0f4 100644
--- a/app/assets/stylesheets/osem.scss
+++ b/app/assets/stylesheets/osem.scss
@@ -1,3 +1,4 @@
+@import "osem-variables";
@import "bootstrap/mixins";
html {
@@ -6,16 +7,33 @@ html {
}
body {
+ // Specifically remove Helevtica Neue from BS3 stack.
+ font-family: sans-serif;
/* Margin bottom by 2 times the footer height */
- margin-bottom: 60px;
- /* Margin bottom by navbar height */
+ margin-bottom: 85px;
padding-top: 60px;
+ font-size: 16px;
}
#content {
padding-bottom: 60px;
}
+// Designed to be the last element in a section / nav
+// Makes a little "puzzle piece" connector.
+.trapezoid {
+ position: relative;
+ margin-left: 60px;
+ border-top: $trapezoid-height solid;
+ border-left: 9px solid transparent;
+ border-right: 9px solid transparent;
+ height: 0;
+ width: 64px;
+ margin-top: -1 * $trapezoid-height;
+ top: $trapezoid-height;
+ z-index: 1000;
+}
+
#footer {
position: absolute;
bottom: 0;
@@ -125,3 +143,8 @@ p.comment-body {
.word_break {
word-wrap: break-word;
}
+
+// Override bootstrap 3...
+hr {
+ border-top: 1px solid #333;
+}
diff --git a/app/assets/stylesheets/strap-on.scss b/app/assets/stylesheets/strap-on.scss
index 571161e6d..bd2d3b38b 100644
--- a/app/assets/stylesheets/strap-on.scss
+++ b/app/assets/stylesheets/strap-on.scss
@@ -1,10 +1,5 @@
-$navbar-default-bg: #299a0b;
-$navbar-default-link-active-bg: #DFE0DF;
-$navbar-default-border: 1px solid #d4d4d4;
-$navbar-default-color: #ffffff;
-$navbar-default-link-color: #ffffff;
-$navbar-default-link-hover-color: #000000;
-
+// Place bootstrap variable customizations in the file below.
+@import "osem-variables";
@import 'bootstrap-datetimepicker';
@import "bootstrap-sprockets";
@@ -56,7 +51,6 @@ $navbar-default-link-hover-color: #000000;
@import "bootstrap/modals";
@import "bootstrap/tooltip";
@import "bootstrap/popovers";
-@import "bootstrap/carousel";
// Utility classes
@import "bootstrap/utilities";
diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb
index 824744266..2229d983d 100644
--- a/app/controllers/admin/base_controller.rb
+++ b/app/controllers/admin/base_controller.rb
@@ -16,12 +16,13 @@ def current_ability
end
def verify_user_admin
- if (current_user.nil?)
+ if current_user.nil?
redirect_to sign_in_path
return false
end
unless (current_user.has_cached_role? :organizer, :any) || (current_user.has_cached_role? :cfp, :any) ||
- (current_user.has_cached_role? :info_desk, :any) || (current_user.has_cached_role? :organization_admin, :any) ||
+ (current_user.has_cached_role? :info_desk,
+ :any) || (current_user.has_cached_role? :organization_admin, :any) ||
(current_user.has_cached_role? :volunteers_coordinator, :any) ||
(current_user.has_cached_role? :track_organizer, :any) || current_user.is_admin
raise CanCan::AccessDenied.new('You are not authorized to access this page.')
diff --git a/app/controllers/admin/booths_controller.rb b/app/controllers/admin/booths_controller.rb
index 0a8999213..533fe23c6 100644
--- a/app/controllers/admin/booths_controller.rb
+++ b/app/controllers/admin/booths_controller.rb
@@ -59,8 +59,8 @@ def update
redirect_to admin_conference_booths_path,
notice: "Successfully updated #{t 'booth'} for #{@booth.title}."
else
- flash.now[:error] = "An error prohibited the #{t'booth'} for #{@booth.title} "\
- "#{@booth.errors.full_messages.join('. ')}."
+ flash.now[:error] = "An error prohibited the #{t 'booth'} for #{@booth.title} " \
+ "#{@booth.errors.full_messages.join('. ')}."
render :edit
end
end
@@ -73,7 +73,7 @@ def accept
Mailbot.conference_booths_acceptance_mail(@booth).deliver_later
end
redirect_to admin_conference_booths_path(conference_id: @conference.short_title),
- notice: "#{(t'booth').capitalize} successfully accepted!"
+ notice: "#{(t 'booth').capitalize} successfully accepted!"
else
redirect_to admin_conference_booths_path(conference_id: @conference.short_title)
flash[:error] = "#{(t 'booth').capitalize} could not be accepted. #{@booth.errors.full_messages.to_sentence}."
@@ -81,11 +81,11 @@ def accept
end
def to_accept
- update_state(:to_accept, "#{(t'booth').capitalize} to accept")
+ update_state(:to_accept, "#{(t 'booth').capitalize} to accept")
end
def to_reject
- update_state(:to_reject, "#{(t'booth').capitalize} to reject")
+ update_state(:to_reject, "#{(t 'booth').capitalize} to reject")
end
def reject
@@ -96,7 +96,7 @@ def reject
Mailbot.conference_booths_rejection_mail(@booth).deliver_later
end
redirect_to admin_conference_booths_path(conference_id: @conference.short_title),
- notice: "#{(t'booth').capitalize} successfully rejected."
+ notice: "#{(t 'booth').capitalize} successfully rejected."
else
redirect_to admin_conference_booths_path(conference_id: @conference.short_title)
flash[:error] = "#{(t 'booth').capitalize} could not be rejected. #{@booth.errors.full_messages.to_sentence}."
@@ -125,7 +125,7 @@ def update_state(transition, 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
+ redirect_back_or_to(admin_conference_booths_path(conference_id: @conference.short_title)) && return
end
end
diff --git a/app/controllers/admin/cfps_controller.rb b/app/controllers/admin/cfps_controller.rb
index cb43bf565..f7ac1f3ca 100644
--- a/app/controllers/admin/cfps_controller.rb
+++ b/app/controllers/admin/cfps_controller.rb
@@ -49,8 +49,8 @@ def destroy
if @cfp.destroy
redirect_to admin_conference_program_cfps_path, notice: 'Call for Papers was successfully deleted.'
else
- redirect_to admin_conference_program_cfps_path, error: 'An error prohibited this Call for Papers from being destroyed: '\
- "#{@cfp.errors.full_messages.join('. ')}."
+ 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
diff --git a/app/controllers/admin/comments_controller.rb b/app/controllers/admin/comments_controller.rb
index 16c07a6d2..d9fcd4cd0 100644
--- a/app/controllers/admin/comments_controller.rb
+++ b/app/controllers/admin/comments_controller.rb
@@ -19,12 +19,20 @@ def index
# Returning all available comments, ordered by created_at: :desc and by event.title
def accessible_ordered_comments
- Comment.accessible_by(current_ability).joins('INNER JOIN events ON commentable_id = events.id').order('events.title', 'comments.created_at DESC')
+ Comment.accessible_by(current_ability).joins('INNER JOIN events ON commentable_id = events.id').order(
+ 'events.title', 'comments.created_at DESC'
+ )
end
-# Grouping all comments by conference, and by event. It returns {:conference => {:event => [{comment_2}, {comment_1 }]}}
+ # Grouping all comments by conference, and by event. It returns {:conference => {:event => [{comment_2}, {comment_1 }]}}
def grouped_comments(remarks)
- remarks.group_by{ |comment| comment.commentable.program.conference }.map {|conference, comments| [conference, comments.group_by{|comment| comment.commentable}]}.to_h
+ remarks.group_by do |comment|
+ comment.commentable.program.conference
+ end.map do |conference, comments|
+ [conference, comments.group_by do |comment|
+ comment.commentable
+ end]
+ end.to_h
end
end
end
diff --git a/app/controllers/admin/commercials_controller.rb b/app/controllers/admin/commercials_controller.rb
index 66a1d8d77..073e135e4 100644
--- a/app/controllers/admin/commercials_controller.rb
+++ b/app/controllers/admin/commercials_controller.rb
@@ -3,7 +3,7 @@
module Admin
class CommercialsController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title
- load_and_authorize_resource through: :conference, except: [:new, :create]
+ load_and_authorize_resource through: :conference, except: %i[new create]
def index
@commercials = @conference.commercials
@@ -17,11 +17,11 @@ def create
if @commercial.save
redirect_to admin_conference_commercials_path,
- notice: 'Commercial was successfully created.'
+ notice: 'Materials were successfully created.'
else
redirect_to admin_conference_commercials_path,
- error: 'An error prohibited this Commercial from being saved: '\
- "#{@commercial.errors.full_messages.join('. ')}."
+ error: 'An error prohibited materials from being saved: ' \
+ "#{@commercial.errors.full_messages.join('. ')}."
end
end
@@ -29,23 +29,23 @@ def create
def update
if @commercial.update(commercial_params)
redirect_to admin_conference_commercials_path,
- notice: 'Commercial was successfully updated.'
+ notice: 'Materials were successfully updated.'
else
redirect_to admin_conference_commercials_path,
- error: 'An error prohibited this Commercial from being saved: '\
- "#{@commercial.errors.full_messages.join('. ')}."
+ error: 'An error prohibited materials from being saved: ' \
+ "#{@commercial.errors.full_messages.join('. ')}."
end
end
def destroy
@commercial.destroy
- redirect_to admin_conference_commercials_path, notice: 'Commercial was successfully destroyed.'
+ redirect_to admin_conference_commercials_path, notice: 'Materials were successfully removed.'
end
def render_commercial
result = Commercial.render_from_url(params[:url])
if result[:error]
- render plain: result[:error], status: 400
+ render plain: result[:error], status: :bad_request
else
render plain: result[:html]
end
@@ -60,13 +60,15 @@ def mass_upload
errors = Commercial.read_file(params[:file]) if params[:file]
if !params[:file]
- flash[:error] = 'Empty file detected while adding commercials to Event'
+ flash[:error] = 'Empty file detected while adding materials to Event'
elsif errors.all? { |_k, v| v.blank? }
- flash[:notice] = 'Successfully added commercials.'
+ flash[:notice] = 'Successfully added materials.'
else
errors_text = ''
errors_text += 'Unable to find event with ID: ' + errors[:no_event].join(', ') + '. ' if errors[:no_event].any?
- errors_text += 'There were some errors: ' + errors[:validation_errors].join('. ') if errors[:validation_errors].any?
+ if errors[:validation_errors].any?
+ errors_text += 'There were some errors: ' + errors[:validation_errors].join('. ')
+ end
flash[:error] = errors_text
end
@@ -76,7 +78,7 @@ def mass_upload
private
def commercial_params
- params.require(:commercial).permit(:url)
+ params.require(:commercial).permit(:title, :url)
end
end
end
diff --git a/app/controllers/admin/conferences_controller.rb b/app/controllers/admin/conferences_controller.rb
index 80e36f47c..fc149b54f 100644
--- a/app/controllers/admin/conferences_controller.rb
+++ b/app/controllers/admin/conferences_controller.rb
@@ -24,11 +24,11 @@ def index
@total_withdrawn = Event.where(state: :withdrawn).count
@new_withdrawn = Event
- .where('state = ? and created_at > ?', 'withdrawn', current_user.last_sign_in_at).count
+ .where('state = ? and created_at > ?', 'withdrawn', current_user.last_sign_in_at).count
@active_conferences = Conference.get_active_conferences_for_dashboard # pending or the last two
@deactive_conferences = Conference
- .get_conferences_without_active_for_dashboard(@active_conferences) # conferences without active
+ .get_conferences_without_active_for_dashboard(@active_conferences) # conferences without active
@conferences = @active_conferences + @deactive_conferences
@recent_users = User.limit(5).order(created_at: :desc)
@@ -97,7 +97,7 @@ def update
else
redirect_to edit_admin_conference_path(id: short_title),
error: 'Updating conference failed. ' \
- "#{@conference.errors.full_messages.join('. ')}."
+ "#{@conference.errors.full_messages.join('. ')}."
end
end
@@ -111,20 +111,19 @@ def show
@all_events = @program.events
@total_submissions = @all_events.count
- @new_submissions = @all_events
- .where('created_at > ?', current_user.last_sign_in_at).count
+ # @new_submissions = @all_events
+ # .where('created_at > ?', current_user.last_sign_in_at).count
@program_length = @conference.current_program_hours
- @new_program_length = @conference.new_program_hours(current_user.last_sign_in_at)
+ # @new_program_length = @conference.new_program_hours(current_user.last_sign_in_at)
@total_withdrawn = @all_events.where(state: :withdrawn).count
- @new_withdrawn = @all_events.where(state: :withdrawn).where(
- 'events.created_at > ?',
- current_user.last_sign_in_at
- ).count
+ # @new_withdrawn = @all_events.where(state: :withdrawn).where(
+ # 'events.created_at > ?',
+ # current_user.last_sign_in_at
+ # ).count
- # Step by step list
- @conference_progress = @conference.get_status
+ @conference_todo_list = @conference.get_status
# Line charts
@registrations = @conference.get_registrations_per_week
@@ -133,22 +132,23 @@ def show
# Doughnut charts
@event_type_distribution = @conference.event_type_distribution
- @event_type_distribution_confirmed = @conference.event_type_distribution(:confirmed)
- @event_type_distribution_withdrawn = @conference.event_type_distribution(:withdrawn)
+ # @event_type_distribution_confirmed = @conference.event_type_distribution(:confirmed)
+ # @event_type_distribution_withdrawn = @conference.event_type_distribution(:withdrawn)
@difficulty_levels_distribution = @conference.difficulty_levels_distribution
- @difficulty_levels_distribution_confirmed = @conference
- .difficulty_levels_distribution(:confirmed)
- @difficulty_levels_distribution_withdrawn = @conference
- .difficulty_levels_distribution(:withdrawn)
+ # @difficulty_levels_distribution_confirmed = @conference
+ # .difficulty_levels_distribution(:confirmed)
+ # @difficulty_levels_distribution_withdrawn = @conference
+ # .difficulty_levels_distribution(:withdrawn)
@tracks_distribution = @conference.tracks_distribution
- @tracks_distribution_confirmed = @conference.tracks_distribution(:confirmed)
- @tracks_distribution_withdrawn = @conference.tracks_distribution(:withdrawn)
+ # @tracks_distribution_confirmed = @conference.tracks_distribution(:confirmed)
+ # @tracks_distribution_withdrawn = @conference.tracks_distribution(:withdrawn)
# Recent actions information
- @recent_events = @conference.program.events.limit(5).order(created_at: :desc)
- @recent_registrations = @conference.registrations.limit(5).order(created_at: :desc)
+ @recent_events = @conference.program.events.includes(%i[submitter_event_user submitter
+ program]).limit(5).order(created_at: :desc)
+ @recent_registrations = @conference.registrations.includes([:user]).limit(5).order(created_at: :desc)
@top_submitter = @conference.get_top_submitter
@@ -181,7 +181,7 @@ def conference_params
:vpositions_attributes, :use_volunteers, :color,
:sponsorship_levels_attributes, :sponsors_attributes,
:registration_limit, :organization_id, :ticket_layout,
- :booth_limit)
+ :booth_limit, :custom_css)
end
end
end
diff --git a/app/controllers/admin/contacts_controller.rb b/app/controllers/admin/contacts_controller.rb
index d5f6ddcd3..dabb871aa 100644
--- a/app/controllers/admin/contacts_controller.rb
+++ b/app/controllers/admin/contacts_controller.rb
@@ -26,7 +26,8 @@ def update
def contact_params
params.require(:contact).permit(
:social_tag, :email, :facebook, :googleplus, :twitter, :instagram,
- :mastodon, :public, :sponsor_email, :youtube, :blog)
+ :mastodon, :public, :sponsor_email, :youtube, :blog
+ )
end
end
end
diff --git a/app/controllers/admin/currency_conversions_controller.rb b/app/controllers/admin/currency_conversions_controller.rb
new file mode 100644
index 000000000..e74738223
--- /dev/null
+++ b/app/controllers/admin/currency_conversions_controller.rb
@@ -0,0 +1,58 @@
+module Admin
+ class CurrencyConversionsController < Admin::BaseController
+ load_and_authorize_resource :conference, find_by: :short_title
+ load_and_authorize_resource :currency_conversion, through: :conference
+
+ # GET /currency_conversions
+ def index; end
+
+ # GET /currency_conversions/1
+ def show; end
+
+ # GET /currency_conversions/new
+ def new
+ @currency_conversion = @conference.currency_conversions.new(conference_id: @conference.short_title)
+ end
+
+ # GET /currency_conversions/1/edit
+ def edit; end
+
+ # POST /currency_conversions
+ def create
+ @currency_conversion = @conference.currency_conversions.new(currency_conversion_params)
+
+ if @currency_conversion.save
+ redirect_to admin_conference_currency_conversions_path(@conference.short_title), notice: 'Currency conversion was successfully created.'
+ else
+ flash.now[:error] = 'Creating currency conversion failed.'
+ render :new
+ end
+ end
+
+ # PATCH/PUT /currency_conversions/1
+ def update
+ if @currency_conversion.update(currency_conversion_params)
+ redirect_to admin_conference_currency_conversions_path(@conference.short_title), notice: 'Currency conversion was successfully updated.'
+ else
+ flash.now[:error] = 'Updating currency conversion failed.'
+ render :edit
+ end
+ end
+
+ # DELETE /currency_conversions/1
+ def destroy
+ if @currency_conversion.destroy
+ redirect_to admin_conference_currency_conversions_path(@conference.short_title), notice: 'Currency conversion was successfully deleted.'
+ else
+ redirect_to admin_conference_currency_conversions_path(@conference.short_title), notice: 'Deleting currency conversion failed.'
+ end
+ end
+
+ private
+
+ # Only allow a list of trusted parameters through.
+ def currency_conversion_params
+ params.require(:currency_conversion).permit(:from_currency, :to_currency, :rate)
+ end
+ end
+end
diff --git a/app/controllers/admin/difficulty_levels_controller.rb b/app/controllers/admin/difficulty_levels_controller.rb
index e5717f7e8..009399f30 100644
--- a/app/controllers/admin/difficulty_levels_controller.rb
+++ b/app/controllers/admin/difficulty_levels_controller.rb
@@ -7,7 +7,7 @@ class DifficultyLevelsController < Admin::BaseController
load_and_authorize_resource through: :program
def index
-# authorize! :index, DifficultyLevel.new(program_id: @program.id)
+ # authorize! :index, DifficultyLevel.new(program_id: @program.id)
end
def edit; end
@@ -43,8 +43,8 @@ def destroy
notice: 'Difficulty level successfully deleted.'
else
redirect_to admin_conference_program_difficulty_levels_path(conference_id: @conference.short_title),
- error: 'Deleting difficulty level type failed! '\
- "#{@difficulty_level.errors.full_messages.join('. ')}."
+ error: 'Deleting difficulty level type failed! ' \
+ "#{@difficulty_level.errors.full_messages.join('. ')}."
end
end
diff --git a/app/controllers/admin/emails_controller.rb b/app/controllers/admin/emails_controller.rb
index ba456efe3..b232a2638 100644
--- a/app/controllers/admin/emails_controller.rb
+++ b/app/controllers/admin/emails_controller.rb
@@ -8,11 +8,13 @@ class EmailsController < Admin::BaseController
def update
if @conference.email_settings.update(email_params)
redirect_to admin_conference_emails_path(
- @conference.short_title),
+ @conference.short_title
+ ),
notice: 'Email settings have been successfully updated.'
else
redirect_to admin_conference_emails_path(
- @conference.short_title),
+ @conference.short_title
+ ),
error: "Updating email settings failed. #{@conference.email_settings.errors.to_a.join('. ')}."
end
end
diff --git a/app/controllers/admin/event_schedules_controller.rb b/app/controllers/admin/event_schedules_controller.rb
index 8068b41e9..a29f9098e 100644
--- a/app/controllers/admin/event_schedules_controller.rb
+++ b/app/controllers/admin/event_schedules_controller.rb
@@ -9,7 +9,8 @@ def create
if @event_schedule.save
render json: { event_schedule_id: @event_schedule.id }
else
- render json: { errors: "The event couldn't be scheduled. #{@event_schedule.errors.full_messages.join('. ')}" }, status: 422
+ render json: { errors: "The event couldn't be scheduled. #{@event_schedule.errors.full_messages.join('. ')}" },
+ status: :unprocessable_entity
end
end
@@ -17,7 +18,8 @@ def update
if @event_schedule.update(event_schedule_params)
render json: { event_schedule_id: @event_schedule.id }
else
- render json: { errors: "The event couldn't be scheduled. #{@event_schedule.errors.full_messages.join('. ')}" }, status: 422
+ render json: { errors: "The event couldn't be scheduled. #{@event_schedule.errors.full_messages.join('. ')}" },
+ status: :unprocessable_entity
end
end
@@ -25,7 +27,8 @@ def destroy
if @event_schedule.destroy
render json: {}
else
- render json: { errors: "The event couldn't be unscheduled. #{@event_schedule.errors.full_messages.join('. ')}" }, status: 422
+ render json: { errors: "The event couldn't be unscheduled. #{@event_schedule.errors.full_messages.join('. ')}" },
+ status: :unprocessable_entity
end
end
diff --git a/app/controllers/admin/event_types_controller.rb b/app/controllers/admin/event_types_controller.rb
index a171b4278..0cd2eed20 100644
--- a/app/controllers/admin/event_types_controller.rb
+++ b/app/controllers/admin/event_types_controller.rb
@@ -41,15 +41,16 @@ def destroy
notice: 'Event type successfully deleted.'
else
redirect_to admin_conference_program_event_types_path(conference_id: @conference.short_title),
- error: 'Destroying event type failed! '\
- "#{@event_type.errors.full_messages.join('. ')}."
+ error: 'Destroying event type failed! ' \
+ "#{@event_type.errors.full_messages.join('. ')}."
end
end
private
def event_type_params
- params.require(:event_type).permit(:title, :length, :minimum_abstract_length, :maximum_abstract_length, :color, :conference_id, :description)
+ params.require(:event_type).permit(:title, :length, :minimum_abstract_length, :maximum_abstract_length,
+ :submission_template, :color, :conference_id, :description)
end
end
end
diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb
index 7187940ec..adf1bde7a 100644
--- a/app/controllers/admin/events_controller.rb
+++ b/app/controllers/admin/events_controller.rb
@@ -2,6 +2,7 @@
module Admin
class EventsController < Admin::BaseController
+ before_action :load_events_with_data, only: :index
load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource :program, through: :conference, singleton: true
load_and_authorize_resource :event, through: :program
@@ -9,7 +10,7 @@ class EventsController < Admin::BaseController
# 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 :assign_tracks, only: [:index, :show, :edit]
+ before_action :assign_tracks, only: %i[index show edit]
def index
@difficulty_levels = @program.difficulty_levels
@@ -20,7 +21,7 @@ def index
@scheduled_event_distribution = @conference.scheduled_event_distribution
@file_name = "events_for_#{@conference.short_title}"
@event_export_option = params[:event_export_option]
- @export_formats = [:pdf, :csv, :xlsx]
+ @export_formats = %i[pdf csv xlsx]
respond_to do |format|
format.html
@@ -45,10 +46,15 @@ def show
@votes = @event.votes.includes(:user)
@difficulty_levels = @program.difficulty_levels
@versions = @event.versions |
- PaperTrail::Version.where(item_type: 'Commercial').where('object LIKE ?', "%commercialable_id: #{@event.id}\ncommercialable_type: Event%") |
- PaperTrail::Version.where(item_type: 'Commercial').where('object_changes LIKE ?', "%commercialable_id:\n- \n- #{@event.id}\ncommercialable_type:\n- \n- Event%") |
- PaperTrail::Version.where(item_type: 'Vote').where('object_changes LIKE ?', "%\nevent_id:\n- \n- #{@event.id}\n%") |
- PaperTrail::Version.where(item_type: 'Vote').where('object LIKE ?', "%\nevent_id: #{@event.id}\n%")
+ PaperTrail::Version.where(item_type: 'Commercial').where('object LIKE ?',
+ "%commercialable_id: #{@event.id}\ncommercialable_type: Event%") |
+ PaperTrail::Version.where(item_type: 'Commercial').where('object_changes LIKE ?',
+ "%commercialable_id:\n- \n- #{@event.id}\ncommercialable_type:\n- \n- Event%") |
+ PaperTrail::Version.where(item_type: 'Vote').where('object_changes LIKE ?',
+ "%\nevent_id:\n- \n- #{@event.id}\n%") |
+ PaperTrail::Version.where(item_type: 'Vote').where('object LIKE ?', "%\nevent_id: #{@event.id}\n%") |
+ PaperTrail::Version.where(item_type: 'EventUser').where('object_changes LIKE ?',
+ "%\nevent_id:\n-\n- #{@event.id}\n%")
end
def edit
@@ -58,6 +64,7 @@ def edit
@user = @event.submitter
@url = admin_conference_program_event_path(@conference.short_title, @event)
@languages = @program.languages_list
+ @superevents = @program.events.where(superevent: true)
end
def comment
@@ -78,7 +85,7 @@ def update
render js: 'index'
else
flash[:notice] = "Successfully updated event with ID #{@event.id}."
- redirect_back_or_to(admin_conference_program_event_path(@conference.short_title, @event))
+ redirect_to admin_conference_program_event_path(@conference.short_title, @event)
end
else
@url = admin_conference_program_event_path(@conference.short_title, @event)
@@ -91,9 +98,11 @@ def create
@url = admin_conference_program_events_path(@conference.short_title, @event)
@languages = @program.languages_list
@event.submitter = current_user
+ @superevents = @program.events.where(superevent: true)
if @event.save
- redirect_to admin_conference_program_events_path(@conference.short_title), notice: 'Event was successfully submitted.'
+ redirect_to admin_conference_program_events_path(@conference.short_title),
+ notice: 'Event was successfully submitted.'
else
flash.now[:error] = "Could not submit proposal: #{@event.errors.full_messages.join(', ')}"
render action: 'new'
@@ -103,6 +112,7 @@ def create
def new
@url = admin_conference_program_events_path(@conference.short_title, @event)
@languages = @program.languages_list
+ @superevents = @program.events.where(superevent: true)
end
def accept
@@ -118,8 +128,10 @@ def confirm
def cancel
update_state(:cancel, 'Event canceled!')
selected_schedule = @event.program.selected_schedule
- event_schedule = EventSchedule.unscoped.where(event: @event).find_by(schedule: selected_schedule) if selected_schedule
- Rails.logger.debug "schedule: #{selected_schedule.inspect} and event_schedule #{event_schedule.inspect}"
+ if selected_schedule
+ event_schedule = EventSchedule.unscoped.where(event: @event).find_by(schedule: selected_schedule)
+ end
+ Rails.logger.debug { "schedule: #{selected_schedule.inspect} and event_schedule #{event_schedule.inspect}" }
if selected_schedule && event_schedule
event_schedule.enabled = false
event_schedule.save
@@ -174,13 +186,15 @@ def toggle_attendance
def event_params
params.require(:event).permit(
- # Set also in proposals controller
- :title, :subtitle, :event_type_id, :abstract, :description, :require_registration, :difficulty_level_id,
- # Set only in admin/events controller
- :track_id, :state, :language, :is_highlight, :max_attendees,
- # Not used anymore?
- :proposal_additional_speakers, :user, :users_attributes,
- speaker_ids: [])
+ # Set also in proposals controller
+ :title, :subtitle, :event_type_id, :abstract, :submission_text, :description, :require_registration, :difficulty_level_id,
+ :committee_review, :superevent, :parent_id, :presentation_mode,
+ # Set only in admin/events controller
+ :track_id, :state, :language, :is_highlight, :max_attendees,
+ # Not used anymore?
+ :proposal_additional_speakers, :user, :users_attributes,
+ speaker_ids: [], volunteer_ids: []
+ )
end
def comment_params
@@ -195,12 +209,20 @@ def update_state(transition, notice, mail = false, subject = false, send_mail =
redirect_back_or_to(admin_conference_program_events_path(conference_id: @conference.short_title)) && return
else
flash[:error] = alert
- return redirect_back_or_to(admin_conference_program_events_path(conference_id: @conference.short_title)) && return
+ redirect_back_or_to(admin_conference_program_events_path(conference_id: @conference.short_title)) && return
end
end
def assign_tracks
@tracks = Track.accessible_by(current_ability).where(program: @program).confirmed
end
+
+ def load_events_with_data
+ @events = Event.where(program: Program.find_by(conference: Conference.find_by(short_title: params[:conference_id]))).includes(
+ :submitter, :submitter_event_user, :speakers, :speaker_event_users, :volunteers,
+ :volunteer_event_users, :program, :event_type, :track, :voters,
+ votes: [:user]
+ )
+ end
end
end
diff --git a/app/controllers/admin/lodgings_controller.rb b/app/controllers/admin/lodgings_controller.rb
index 639e0544e..7e6d265c8 100644
--- a/app/controllers/admin/lodgings_controller.rb
+++ b/app/controllers/admin/lodgings_controller.rb
@@ -5,8 +5,7 @@ class LodgingsController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource :lodging, through: :conference
- def index
- end
+ def index; end
def new
@lodging = @conference.lodgings.new
@@ -42,7 +41,7 @@ def destroy
else
redirect_to admin_conference_lodgings_path(conference_id: @conference.short_title),
error: 'Deleting lodging failed.' \
- "#{@lodging.errors.full_messages.join('. ')}."
+ "#{@lodging.errors.full_messages.join('. ')}."
end
end
diff --git a/app/controllers/admin/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb
index 14ff040d3..f885edb8d 100644
--- a/app/controllers/admin/organizations_controller.rb
+++ b/app/controllers/admin/organizations_controller.rb
@@ -3,7 +3,7 @@
module Admin
class OrganizationsController < Admin::BaseController
load_and_authorize_resource :organization
- before_action :verify_user, only: [:assign_org_admins, :unassign_org_admins]
+ before_action :verify_user, only: %i[assign_org_admins unassign_org_admins]
def index
@organizations = Organization.all
@@ -85,7 +85,7 @@ def verify_user
unless @user
redirect_to admins_admin_organization_path(@organization),
error: 'Could not find user. Please provide a valid email!'
- return
+ nil
end
end
diff --git a/app/controllers/admin/programs_controller.rb b/app/controllers/admin/programs_controller.rb
index d47d4d1de..83cb6b1ce 100644
--- a/app/controllers/admin/programs_controller.rb
+++ b/app/controllers/admin/programs_controller.rb
@@ -12,7 +12,10 @@ def edit; end
def update
authorize! :update, @conference.program
@program = @conference.program
- params['program']['languages'] = params['program']['languages'].join(',') if params['program']['languages'].present?
+ if params['program']['languages'].present?
+ params['program']['languages'] =
+ params['program']['languages'].join(',')
+ end
@program.assign_attributes(program_params)
send_mail_on_schedule_public = @program.notify_on_schedule_public?
event_schedules_count_was = @program.event_schedules.count
@@ -22,7 +25,9 @@ def update
respond_to do |format|
format.html do
notice = 'The program was successfully updated.'
- notice += ' You changed schedule interval and some events were unscheduled.' if @program.event_schedules.count != event_schedules_count_was
+ if @program.event_schedules.count != event_schedules_count_was
+ notice += ' You changed schedule interval and some events were unscheduled.'
+ end
redirect_to admin_conference_program_path(@conference.short_title), notice: notice
end
format.js { render json: {} }
@@ -33,7 +38,10 @@ def update
flash.now[:error] = "Updating program failed. #{@program.errors.to_a.join('. ')}."
render :new
end
- format.js { render json: { errors: "The selected schedule couldn't be updated #{@program.errors.to_a.join('. ')}" }, status: 422 }
+ format.js do
+ render json: { errors: "The selected schedule couldn't be updated #{@program.errors.to_a.join('. ')}" },
+ status: :unprocessable_entity
+ end
end
end
end
@@ -41,7 +49,8 @@ def update
private
def program_params
- params.require(:program).permit(:rating, :schedule_public, :schedule_interval, :schedule_fluid, :blind_voting, :voting_start_date, :voting_end_date, :selected_schedule_id, :languages)
+ params.require(:program).permit(:rating, :schedule_public, :schedule_interval, :schedule_fluid, :blind_voting,
+ :voting_start_date, :voting_end_date, :selected_schedule_id, :languages)
end
end
end
diff --git a/app/controllers/admin/questions_controller.rb b/app/controllers/admin/questions_controller.rb
index b6451c92d..75d2c0bc1 100644
--- a/app/controllers/admin/questions_controller.rb
+++ b/app/controllers/admin/questions_controller.rb
@@ -3,7 +3,7 @@
module Admin
class QuestionsController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title
- load_and_authorize_resource except: [:new, :create]
+ load_and_authorize_resource except: %i[new create]
def index
authorize! :index, Question.new(conference_id: @conference.id)
@@ -34,7 +34,10 @@ def create
if @conference.save
format.html { redirect_to admin_conference_questions_path, notice: 'Question was successfully created.' }
else
- format.html { redirect_to admin_conference_questions_path, error: "Oops, couldn't save Question. #{@question.errors.full_messages.join('. ')}" }
+ format.html do
+ redirect_to admin_conference_questions_path,
+ error: "Oops, couldn't save Question. #{@question.errors.full_messages.join('. ')}"
+ end
end
end
end
@@ -42,16 +45,19 @@ def create
# GET questions/1/edit
def edit
if @question.global
- redirect_to admin_conference_questions_path(conference_id: @conference.short_title), error: 'Sorry, you cannot edit global questions. Create a new one.'
+ redirect_to admin_conference_questions_path(conference_id: @conference.short_title),
+ error: 'Sorry, you cannot edit global questions. Create a new one.'
end
end
# PUT questions/1
def update
if @question.update(question_params)
- redirect_to admin_conference_questions_path(conference_id: @conference.short_title), notice: "Question '#{@question.title}' for #{@conference.short_title} successfully updated."
+ redirect_to admin_conference_questions_path(conference_id: @conference.short_title),
+ notice: "Question '#{@question.title}' for #{@conference.short_title} successfully updated."
else
- redirect_to admin_conference_questions_path(conference_id: @conference.short_title), notice: "Update of questions for #{@conference.short_title} failed. #{@question.errors.full_messages.join('. ')}"
+ redirect_to admin_conference_questions_path(conference_id: @conference.short_title),
+ notice: "Update of questions for #{@conference.short_title} failed. #{@question.errors.full_messages.join('. ')}"
end
end
@@ -59,9 +65,11 @@ def update
def update_conference
authorize! :update, Question.new(conference_id: @conference.id)
if @conference.update(conference_params)
- redirect_to admin_conference_questions_path(conference_id: @conference.short_title), notice: "Questions for #{@conference.short_title} successfully updated."
+ redirect_to admin_conference_questions_path(conference_id: @conference.short_title),
+ notice: "Questions for #{@conference.short_title} successfully updated."
else
- redirect_to admin_conference_questions_path(conference_id: @conference.short_title), notice: "Update of questions for #{@conference.short_title} failed."
+ redirect_to admin_conference_questions_path(conference_id: @conference.short_title),
+ notice: "Update of questions for #{@conference.short_title} failed."
end
end
@@ -75,12 +83,13 @@ def destroy
# Delete question and its answers
begin
Question.transaction do
-
@question.destroy
@question.answers.each do |a|
a.destroy
end
- flash[:notice] = "Deleted question: #{@question.title} and its answers: #{@question.answers.map {|a| a.title}.join ','}"
+ flash[:notice] = "Deleted question: #{@question.title} and its answers: #{@question.answers.map do |a|
+ a.title
+ end.join ','}"
end
rescue ActiveRecord::RecordInvalid
flash[:error] = 'Could not delete question.'
@@ -97,7 +106,8 @@ def destroy
private
def question_params
- params.require(:question).permit(:title, :global, :answer_ids, :question_type_id, :conference_id, answers_attributes: [:id, :title])
+ params.require(:question).permit(:title, :global, :answer_ids, :question_type_id, :conference_id,
+ answers_attributes: %i[id title])
end
def conference_params
diff --git a/app/controllers/admin/registration_periods_controller.rb b/app/controllers/admin/registration_periods_controller.rb
index d08847197..f963aa789 100644
--- a/app/controllers/admin/registration_periods_controller.rb
+++ b/app/controllers/admin/registration_periods_controller.rb
@@ -18,7 +18,8 @@ def create
redirect_to admin_conference_registration_period_path(@conference.short_title),
notice: 'Registration Period successfully updated.'
else
- flash.now[:error] = "An error prohibited the Registration Period from being saved: #{@registration_period.errors.full_messages.join('. ')}."
+ flash.now[:error] =
+ "An error prohibited the Registration Period from being saved: #{@registration_period.errors.full_messages.join('. ')}."
render :new
end
end
@@ -33,7 +34,7 @@ def update
notice: 'Registration Period successfully updated.'
else
flash.now[:error] = 'An error prohibited the Registration Period from being saved: ' \
- "#{@registration_period.errors.full_messages.join('. ')}."
+ "#{@registration_period.errors.full_messages.join('. ')}."
render :edit
end
end
diff --git a/app/controllers/admin/registrations_controller.rb b/app/controllers/admin/registrations_controller.rb
index 88027bbd2..03b5662fe 100644
--- a/app/controllers/admin/registrations_controller.rb
+++ b/app/controllers/admin/registrations_controller.rb
@@ -44,8 +44,8 @@ def update
redirect_to admin_conference_registrations_path(@conference.short_title),
notice: "Successfully updated registration for #{@registration.user.email}!"
else
- flash.now[:error] = "An error prohibited the Registration for #{@registration.user.email}: "\
- "#{@registration.errors.full_messages.join('. ')}."
+ flash.now[:error] = "An error prohibited the Registration for #{@registration.user.email}: " \
+ "#{@registration.errors.full_messages.join('. ')}."
render :edit
end
end
diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb
index 28f4f94f0..3f92ed547 100644
--- a/app/controllers/admin/reports_controller.rb
+++ b/app/controllers/admin/reports_controller.rb
@@ -8,17 +8,18 @@ class ReportsController < Admin::BaseController
# load_and_authorize_resource :event, through: :program
def index
- @events = Event.accessible_by(current_ability).where(program: @program)
+ @events = Event.accessible_by(current_ability).where(program: @program,
+ state: %i[confirmed unconfirmed])
@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])
attended_registrants_ids = @conference.registrations.where(attended: true).pluck(:user_id)
@missing_event_speakers = EventUser.joins(:event)
- .where('event_role = ? and program_id = ?', 'speaker', @program.id)
- .where.not(user_id: attended_registrants_ids)
- .where(event_id: @events.pluck(:id))
- .includes(:user, :event)
+ .where('event_role = ? and program_id = ?', 'speaker', @program.id)
+ .where.not(user_id: attended_registrants_ids)
+ .where(event_id: @events.pluck(:id))
+ .includes(:user, :event)
end
end
end
diff --git a/app/controllers/admin/resources_controller.rb b/app/controllers/admin/resources_controller.rb
index a6e51e99f..88db23bd5 100644
--- a/app/controllers/admin/resources_controller.rb
+++ b/app/controllers/admin/resources_controller.rb
@@ -3,7 +3,7 @@
module Admin
class ResourcesController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title
- load_and_authorize_resource :resource, only: [:show, :edit, :update, :destroy]
+ load_and_authorize_resource :resource, only: %i[show edit update destroy]
def index; end
@@ -41,7 +41,7 @@ def destroy
else
redirect_to admin_conference_resources_path(conference_id: @conference.short_title),
error: 'Resource was successfully destroyed.' \
- "#{@resource.errors.full_messages.join('. ')}."
+ "#{@resource.errors.full_messages.join('. ')}."
end
end
diff --git a/app/controllers/admin/rooms_controller.rb b/app/controllers/admin/rooms_controller.rb
index af83d5cb2..a2578136a 100644
--- a/app/controllers/admin/rooms_controller.rb
+++ b/app/controllers/admin/rooms_controller.rb
@@ -48,7 +48,9 @@ def destroy
private
def room_params
- params.require(:room).permit(:name, :size)
+ params.require(:room)
+ .permit(:name, :size, :url, :order, :discussion_url)
+ .each_value { |value| value.try(:strip!) }
end
end
end
diff --git a/app/controllers/admin/schedules_controller.rb b/app/controllers/admin/schedules_controller.rb
index 71e7382a7..f3176245c 100644
--- a/app/controllers/admin/schedules_controller.rb
+++ b/app/controllers/admin/schedules_controller.rb
@@ -6,7 +6,7 @@ class SchedulesController < Admin::BaseController
# 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, except: [:new, :create]
+ load_and_authorize_resource :schedule, through: :program, except: %i[new create]
load_resource :event_schedules, through: :schedule
load_resource :selected_schedule, through: :program, singleton: true
load_resource :venue, through: :conference, singleton: true
@@ -37,9 +37,10 @@ def show
:difficulty_level,
:track,
:event_type,
- event_users: :user
+ { event_users: :user }
]
)
+ @event_types = @program.event_types || []
if @schedule.track
track = @schedule.track
@@ -51,7 +52,7 @@ def show
@event_schedules += t.selected_schedule.event_schedules if t.selected_schedule
end
self_organized_tracks_events = Event.eager_load(event_users: :user).confirmed.where(track: @program.tracks.self_organized.confirmed)
- @unscheduled_events = @program.events.confirmed - @schedule.events - self_organized_tracks_events
+ @unscheduled_events = (@program.events.confirmed + @program.events.unconfirmed) - @schedule.events - self_organized_tracks_events
@dates = @conference.start_date..@conference.end_date
@rooms = @conference.venue.rooms if @conference.venue
end
diff --git a/app/controllers/admin/splashpages_controller.rb b/app/controllers/admin/splashpages_controller.rb
index 6b40d0d14..7e50bb22b 100644
--- a/app/controllers/admin/splashpages_controller.rb
+++ b/app/controllers/admin/splashpages_controller.rb
@@ -37,8 +37,8 @@ def destroy
if @splashpage.destroy
redirect_to admin_conference_splashpage_path, notice: 'Splashpage was successfully destroyed.'
else
- redirect_to admin_conference_splashpage_path, error: 'An error prohibited this Splashpage from being destroyed: '\
- "#{@splashpage.errors.full_messages.join('. ')}."
+ redirect_to admin_conference_splashpage_path, error: 'An error prohibited this Splashpage from being destroyed: ' \
+ "#{@splashpage.errors.full_messages.join('. ')}."
end
end
@@ -46,11 +46,12 @@ def destroy
def splashpage_params
params.require(:splashpage).permit(:public,
+ :banner_photo, :banner_photo_cache,
:include_tracks, :include_program, :include_cfp,
:include_venue, :include_registrations,
:include_tickets, :include_lodgings,
:include_sponsors, :include_social_media,
- :include_booths)
+ :include_booths, :include_happening_now)
end
end
end
diff --git a/app/controllers/admin/sponsors_controller.rb b/app/controllers/admin/sponsors_controller.rb
index bc61d8342..7aca6794f 100644
--- a/app/controllers/admin/sponsors_controller.rb
+++ b/app/controllers/admin/sponsors_controller.rb
@@ -4,7 +4,7 @@ module Admin
class SponsorsController < Admin::BaseController
load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource :sponsor, through: :conference
- before_action :sponsorship_level_required, only: [:index, :new]
+ before_action :sponsorship_level_required, only: %i[index new]
def index
authorize! :index, Sponsor.new(conference_id: @conference.id)
@@ -30,7 +30,8 @@ def create
def update
if @sponsor.update(sponsor_params)
redirect_to admin_conference_sponsors_path(
- conference_id: @conference.short_title),
+ conference_id: @conference.short_title
+ ),
notice: 'Sponsor successfully updated.'
else
flash.now[:error] = "Update sponsor failed: #{@sponsor.errors.full_messages.join('. ')}."
@@ -45,14 +46,15 @@ def destroy
else
redirect_to admin_conference_sponsors_path(conference_id: @conference.short_title),
error: 'Deleting sponsor failed! ' \
- "#{@sponsor.errors.full_messages.join('. ')}."
+ "#{@sponsor.errors.full_messages.join('. ')}."
end
end
private
def sponsor_params
- params.require(:sponsor).permit(:name, :description, :website_url, :picture, :picture_cache, :sponsorship_level_id, :conference_id)
+ params.require(:sponsor).permit(:name, :description, :website_url, :picture, :picture_cache,
+ :sponsorship_level_id, :conference_id)
end
def sponsorship_level_required
diff --git a/app/controllers/admin/sponsorship_levels_controller.rb b/app/controllers/admin/sponsorship_levels_controller.rb
index 443aa8b96..d7515187f 100644
--- a/app/controllers/admin/sponsorship_levels_controller.rb
+++ b/app/controllers/admin/sponsorship_levels_controller.rb
@@ -29,7 +29,8 @@ def create
def update
if @sponsorship_level.update(sponsorship_level_params)
redirect_to admin_conference_sponsorship_levels_path(
- conference_id: @conference.short_title),
+ conference_id: @conference.short_title
+ ),
notice: 'Sponsorship level successfully updated.'
else
flash.now[:error] = "Update Sponsorship level failed: #{@sponsorship_level.errors.full_messages.join('. ')}."
@@ -44,7 +45,7 @@ def destroy
else
redirect_to admin_conference_sponsorship_levels_path(conference_id: @conference.short_title),
error: 'Deleting sponsorship level failed! ' \
- "#{@sponsorship_level.errors.full_messages.join('. ')}."
+ "#{@sponsorship_level.errors.full_messages.join('. ')}."
end
end
diff --git a/app/controllers/admin/survey_questions_controller.rb b/app/controllers/admin/survey_questions_controller.rb
index 61febf1da..187815848 100644
--- a/app/controllers/admin/survey_questions_controller.rb
+++ b/app/controllers/admin/survey_questions_controller.rb
@@ -14,7 +14,8 @@ def new
def create
@survey_question = @survey.survey_questions.new(survey_question_params)
if @survey_question.save
- redirect_to admin_conference_survey_path(@conference.short_title, @survey), notice: 'Successfully created Survey Question.'
+ redirect_to admin_conference_survey_path(@conference.short_title, @survey),
+ notice: 'Successfully created Survey Question.'
else
@url = admin_conference_survey_survey_questions_path(@conference.short_title, @survey)
render :new
@@ -29,7 +30,8 @@ def edit
# PUT questions/1
def update
if @survey_question.update(survey_question_params)
- redirect_to admin_conference_survey_path(@conference.short_title, @survey), notice: 'Successfully updated Survey Question.'
+ redirect_to admin_conference_survey_path(@conference.short_title, @survey),
+ notice: 'Successfully updated Survey Question.'
else
@url = admin_conference_survey_survey_question_path(@conference.short_title, @survey, @survey_question)
render :edit
@@ -39,9 +41,11 @@ def update
# DELETE questions/1
def destroy
if @survey_question.destroy
- redirect_to admin_conference_survey_path(@conference.short_title, @survey), notice: 'Successfully deleted Survey Question.'
+ redirect_to admin_conference_survey_path(@conference.short_title, @survey),
+ notice: 'Successfully deleted Survey Question.'
else
- redirect_to admin_conference_survey_path(@conference.short_title, @survey), error: "Can't delete this Survey Question"
+ redirect_to admin_conference_survey_path(@conference.short_title, @survey),
+ error: "Can't delete this Survey Question"
end
end
diff --git a/app/controllers/admin/surveys_controller.rb b/app/controllers/admin/surveys_controller.rb
index 8054cc446..e63c56517 100644
--- a/app/controllers/admin/surveys_controller.rb
+++ b/app/controllers/admin/surveys_controller.rb
@@ -17,9 +17,11 @@ def new
def create
@survey = Survey.new(survey_params)
if @survey.save
- redirect_to new_admin_conference_survey_survey_question_path(@conference.short_title, @survey), notice: 'Successfully created survey'
+ redirect_to new_admin_conference_survey_survey_question_path(@conference.short_title, @survey),
+ notice: 'Successfully created survey'
else
- redirect_to new_admin_conference_survey_path(@conference.short_title, survey: { surveyable_type: survey_params['surveyable_type'], surveyable_id: survey_params['surveyable_id'] }), error: 'Could not create survey.' + @survey.errors.full_messages.to_sentence
+ redirect_to new_admin_conference_survey_path(@conference.short_title, survey: { surveyable_type: survey_params['surveyable_type'], surveyable_id: survey_params['surveyable_id'] }),
+ error: 'Could not create survey.' + @survey.errors.full_messages.to_sentence
end
end
@@ -44,7 +46,8 @@ def destroy
private
def survey_params
- params.require(:survey).permit(:title, :description, :target, :start_date, :end_date, :surveyable_type, :surveyable_id)
+ params.require(:survey).permit(:title, :description, :target, :start_date, :end_date, :surveyable_type,
+ :surveyable_id)
end
end
end
diff --git a/app/controllers/admin/ticket_scannings_controller.rb b/app/controllers/admin/ticket_scannings_controller.rb
index f66371116..82b8be0b1 100644
--- a/app/controllers/admin/ticket_scannings_controller.rb
+++ b/app/controllers/admin/ticket_scannings_controller.rb
@@ -4,14 +4,20 @@ 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
+ if !@physical_ticket && params[:physical_ticket]
+ @physical_ticket = PhysicalTicket.find_by(token: params[:physical_ticket][:token])
+ end
@ticket_scanning = TicketScanning.new(physical_ticket: @physical_ticket)
authorize! :create, @ticket_scanning
@ticket_scanning.save
- redirect_to conferences_path,
+ dest_path = conferences_path
+ if request.referer&.match?(%r{admin/conferences})
+ dest_path = admin_conference_physical_tickets_path(conference_id: @conference.short_title)
+ end
+ redirect_to dest_path,
notice: "Ticket with token #{@physical_ticket.token} successfully scanned."
end
end
diff --git a/app/controllers/admin/tickets_controller.rb b/app/controllers/admin/tickets_controller.rb
index e0e5b1868..74299c89c 100644
--- a/app/controllers/admin/tickets_controller.rb
+++ b/app/controllers/admin/tickets_controller.rb
@@ -38,21 +38,66 @@ def update
end
end
+ def give
+ message = ''
+ ticket_purchase = @ticket.ticket_purchases.new(gift_ticket_params)
+ recipient = ticket_purchase.user
+ old_ticket_purchases = TicketPurchase.unpaid.by_conference(@conference)
+ .where(
+ user_id: gift_ticket_params[:user_id],
+ ticket_id: @conference.registration_tickets
+ )
+ # We need to cancel any in progress ticket purchases
+ # TODO-SNAPCON: Add tests, update existing DB records? Add pluralize
+ if old_ticket_purchases.any?
+ message = "(Removed #{old_ticket_purchases.count} unpaid ticket)."
+ old_ticket_purchases.destroy_all
+ end
+ if ticket_purchase.save
+ # We must pay for a ticket purchase to create a physical ticket.
+ # Because there is no CC xact, the Payment does not need to be saved.
+ ticket_purchase.pay(Payment.new)
+ registration = @conference.register_user(recipient) if @ticket.registration_ticket?
+ redirect_to(
+ admin_conference_ticket_path(@conference.short_title, @ticket),
+ notice: "#{recipient.name} was given a #{@ticket.title} ticket #{if registration
+ 'and registered'
+ end}. #{message}"
+ )
+ else
+ redirect_back(
+ fallback_location: admin_conference_ticket_path(@conference.short_title, @ticket),
+ error: "Unable to give #{recipient.name} a #{@ticket.title} ticket: " +
+ ticket_purchase.errors.full_messages.to_sentence
+ )
+ end
+ end
+
def destroy
if @ticket.destroy
redirect_to admin_conference_tickets_path(conference_id: @conference.short_title),
- notice: 'Ticket successfully destroyed.'
+ notice: 'Ticket successfully deleted.'
else
redirect_to admin_conference_tickets_path(conference_id: @conference.short_title),
- error: 'Ticket was successfully destroyed.' \
- "#{@ticket.errors.full_messages.join('. ')}."
+ error: 'Deleting ticket failed! ' \
+ "#{@ticket.errors.full_messages.join('. ')}."
end
end
private
def ticket_params
- params.require(:ticket).permit(:conference, :title, :url, :description, :conference_id, :price_cents, :price_currency, :price, :registration_ticket)
+ params.require(:ticket).permit(
+ :conference, :conference_id,
+ :title, :url, :description, :email_subject, :email_body,
+ :price_cents, :price_currency, :price,
+ :registration_ticket, :visible
+ )
+ end
+
+ def gift_ticket_params
+ response = params.require(:ticket_purchase).permit(:user_id)
+ response.merge(paid: true, amount_paid: 0, conference: @conference)
end
end
end
diff --git a/app/controllers/admin/tracks_controller.rb b/app/controllers/admin/tracks_controller.rb
index 77de53f49..fc30e9f20 100644
--- a/app/controllers/admin/tracks_controller.rb
+++ b/app/controllers/admin/tracks_controller.rb
@@ -128,7 +128,10 @@ def update_selected_schedule
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 }
+ format.js do
+ render json: { errors: "The selected schedule couldn't be updated #{@track.errors.to_a.join('. ')}" },
+ status: :unprocessable_entity
+ end
end
end
end
@@ -136,7 +139,8 @@ def update_selected_schedule
private
def track_params
- params.require(:track).permit(:name, :description, :color, :short_name, :cfp_active, :start_date, :end_date, :room_id)
+ params.require(:track).permit(:name, :description, :color, :short_name, :cfp_active, :start_date, :end_date,
+ :room_id)
end
def update_state(transition, notice)
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 9820cef16..fe260bc58 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -43,32 +43,43 @@ def show
# Variable @show_attributes holds the attributes that are visible for the 'show' action
# If you want to change the attributes that are shown in the 'show' action of users
# add/remove the attributes in the following string array
- @show_attributes = %w(name email username nickname affiliation biography registered attended roles created_at
+ @show_attributes = %w[name email username nickname affiliation biography
+ profile_picture registered attended roles created_at
updated_at sign_in_count current_sign_in_at last_sign_in_at
- current_sign_in_ip last_sign_in_ip)
+ current_sign_in_ip last_sign_in_ip]
end
def update
message = ''
- if params[:user] && !params[:user][:email].nil?
- if (new_email = params[:user][:email]) != @user.email
- message = " Confirmation email sent to #{new_email}. The new email needs to be confirmed before it can be used."
- end
+ if params[:user] && !params[:user][:email].nil? && ((new_email = params[:user][:email]) != @user.email)
+ message = " Confirmation email sent to #{new_email}. The new email needs to be confirmed before it can be used."
end
if @user.update(user_params)
redirect_to admin_users_path, notice: "Updated #{@user.name} (#{@user.email})!" + message
else
- redirect_to admin_users_path, error: "Could not update #{@user.name} (#{@user.email}). #{@user.errors.full_messages.join('. ')}."
+ redirect_to admin_users_path,
+ error: "Could not update #{@user.name} (#{@user.email}). #{@user.errors.full_messages.join('. ')}."
end
end
def edit; end
+ def destroy
+ if @user.destroy
+ redirect_to admin_users_path,
+ notice: "User #{@user.id} (#{@user.email}) deleted."
+ else
+ redirect_to admin_users_path,
+ error: "User #{@user.id} (#{@user.emai}) could not be deleted. #{@user.full_messages.join(',')}"
+ end
+ end
+
private
def user_params
- params.require(:user).permit(:email, :name, :email_public, :biography, :nickname, :affiliation, :is_admin,
+ params.require(:user).permit(:email, :name, :email_public, :biography, :nickname,
+ :affiliation, :is_admin, :picture, :picture_cache,
:username, :login, :is_disabled, :tshirt, :mobile, :volunteer_experience,
:languages, :to_confirm, :password, role_ids: [])
end
diff --git a/app/controllers/admin/venue_commercials_controller.rb b/app/controllers/admin/venue_commercials_controller.rb
index ddd13aa9c..11a87d199 100644
--- a/app/controllers/admin/venue_commercials_controller.rb
+++ b/app/controllers/admin/venue_commercials_controller.rb
@@ -12,11 +12,11 @@ def create
if @commercial.save
redirect_to admin_conference_venue_path,
- notice: 'Commercial was successfully created.'
+ notice: 'Materials successfully created.'
else
redirect_to admin_conference_venue_path,
- error: 'An error prohibited this Commercial from being saved: '\
- "#{@commercial.errors.full_messages.join('. ')}."
+ error: 'An error prohibited materials from being saved: ' \
+ "#{@commercial.errors.full_messages.join('. ')}."
end
end
@@ -24,23 +24,23 @@ def create
def update
if @commercial.update(commercial_params)
redirect_to admin_conference_venue_path,
- notice: 'Commercial was successfully updated.'
+ notice: 'Materials successfully updated.'
else
redirect_to admin_conference_venue_path,
- error: 'An error prohibited this Commercial from being saved: '\
- "#{@commercial.errors.full_messages.join('. ')}."
+ error: 'An error prohibited materials from being saved: ' \
+ "#{@commercial.errors.full_messages.join('. ')}."
end
end
def destroy
@commercial.destroy
- redirect_to admin_conference_venue_path, notice: 'Commercial was successfully destroyed.'
+ redirect_to admin_conference_venue_path, notice: 'Materials successfully destroyed.'
end
def render_commercial
result = Commercial.render_from_url(params[:url])
if result[:error]
- render plain: result[:error], status: 400
+ render plain: result[:error], status: :bad_request
else
render plain: result[:html]
end
@@ -49,7 +49,7 @@ def render_commercial
private
def commercial_params
- params.require(:commercial).permit(:url)
+ params.require(:commercial).permit(:title, :url)
end
end
end
diff --git a/app/controllers/admin/venues_controller.rb b/app/controllers/admin/venues_controller.rb
index 17ba4c992..a39d18058 100644
--- a/app/controllers/admin/venues_controller.rb
+++ b/app/controllers/admin/venues_controller.rb
@@ -38,15 +38,16 @@ def destroy
if @venue.destroy
redirect_to admin_conference_venue_path, notice: 'Venue was successfully deleted.'
else
- redirect_to admin_conference_venue_path, error: 'An error prohibited this Venue from being destroyed: '\
- "#{@venue.errors.full_messages.join('. ')}."
+ redirect_to admin_conference_venue_path, error: 'An error prohibited this Venue from being destroyed: ' \
+ "#{@venue.errors.full_messages.join('. ')}."
end
end
private
def venue_params
- params.require(:venue).permit(:name, :street, :postalcode, :city, :country, :longitude, :latitude, :description, :website, :picture, :picture_cache, :lodgings_attributes, :conference_id)
+ params.require(:venue).permit(:name, :street, :postalcode, :city, :country, :longitude, :latitude, :description,
+ :website, :picture, :picture_cache, :lodgings_attributes, :conference_id)
end
end
end
diff --git a/app/controllers/admin/versions_controller.rb b/app/controllers/admin/versions_controller.rb
index 1bc7ae2ab..723c1d972 100644
--- a/app/controllers/admin/versions_controller.rb
+++ b/app/controllers/admin/versions_controller.rb
@@ -6,10 +6,18 @@ class VersionsController < Admin::BaseController
load_and_authorize_resource class: PaperTrail::Version
def index
- @conferences_with_role = current_user.is_admin? ? Conference.pluck(:short_title) : Conference.with_role([:organizer, :cfp, :info_desk], current_user).pluck(:short_title)
+ @conferences_with_role = if current_user.is_admin?
+ Conference.pluck(:short_title)
+ else
+ Conference.with_role(
+ %i[organizer cfp info_desk], current_user
+ ).pluck(:short_title)
+ end
if current_user.has_cached_role? :organization_admin, :any
- @conferences_with_role = Organization.with_role('organization_admin', current_user).map { |org| org.conferences.pluck :short_title }.flatten
+ @conferences_with_role = Organization.with_role('organization_admin', current_user).map do |org|
+ org.conferences.pluck :short_title
+ end.flatten
end
@conferences_with_role.uniq!
@@ -19,7 +27,9 @@ def index
end
def revert_attribute
- if params[:attribute] && @version.changeset.reject{ |_, values| values[0].blank? && values[1].blank? }.keys.include?(params[:attribute])
+ if params[:attribute] && @version.changeset.reject do |_, values|
+ values[0].blank? && values[1].blank?
+ end.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'
@@ -28,7 +38,8 @@ def revert_attribute
if @version.item.save
flash[:notice] = 'The selected change was successfully reverted'
else
- flash[:error] = "An error prohibited this change from being reverted: #{@version.item.errors.full_messages.join('. ')}."
+ flash[:error] =
+ "An error prohibited this change from being reverted: #{@version.item.errors.full_messages.join('. ')}."
end
end
@@ -44,7 +55,8 @@ def revert_object
if @version.reify.save
flash[:notice] = 'The selected change was successfully reverted'
else
- flash[:error] = "An error prohibited this change from being reverted: #{@version.reify.errors.full_messages.join('. ')}."
+ flash[:error] =
+ "An error prohibited this change from being reverted: #{@version.reify.errors.full_messages.join('. ')}."
end
elsif @version.event == 'create' && @version.item
diff --git a/app/controllers/admin/volunteers_controller.rb b/app/controllers/admin/volunteers_controller.rb
index b0b85ffcd..c93ee9721 100644
--- a/app/controllers/admin/volunteers_controller.rb
+++ b/app/controllers/admin/volunteers_controller.rb
@@ -27,9 +27,11 @@ def show
def update
if @conference.update(conference_params)
- redirect_to admin_conference_volunteers_info_path(conference_id: params[:conference_id]), notice: 'Volunteering options were successfully updated.'
+ redirect_to admin_conference_volunteers_info_path(conference_id: params[:conference_id]),
+ notice: 'Volunteering options were successfully updated.'
else
- redirect_to admin_conference_volunteers_info_path(conference_id: params[:conference_id]), error: "Volunteering options update failed: #{@conference.errors.full_messages.join '. '}"
+ redirect_to admin_conference_volunteers_info_path(conference_id: params[:conference_id]),
+ error: "Volunteering options update failed: #{@conference.errors.full_messages.join '. '}"
end
end
diff --git a/app/controllers/api/v1/events_controller.rb b/app/controllers/api/v1/events_controller.rb
index e818a780c..0577eb417 100644
--- a/app/controllers/api/v1/events_controller.rb
+++ b/app/controllers/api/v1/events_controller.rb
@@ -12,9 +12,7 @@ class EventsController < Api::BaseController
def index
events = Event.includes(:track, :event_type, event_users: :user)
- if @conference
- events = events.where(program: @conference.program)
- end
+ events = events.where(program: @conference.program) if @conference
respond_with events.confirmed, callback: params[:callback]
end
diff --git a/app/controllers/api/v1/speakers_controller.rb b/app/controllers/api/v1/speakers_controller.rb
index ea76a32bc..aae10079f 100644
--- a/app/controllers/api/v1/speakers_controller.rb
+++ b/app/controllers/api/v1/speakers_controller.rb
@@ -11,13 +11,13 @@ class SpeakersController < Api::BaseController
def index
if @conference
- users = User.joins(event_users: { event: { program: :conference} })
+ users = User.joins(event_users: { event: { program: :conference } })
users = users.where(conferences: { short_title: @conference.short_title })
else
users = User.joins(:event_users)
end
- users = users.where(event_users: {event_role: :speaker}).uniq
+ users = users.where(event_users: { event_role: :speaker }).uniq
render json: users,
each_serializer: SpeakerSerializer,
callback: params['callback'],
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 93105587a..672afae83 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -3,34 +3,31 @@
class ApplicationController < ActionController::Base
before_action :set_paper_trail_whodunnit
include ApplicationHelper
+ include Pagy::Backend
add_flash_types :error
protect_from_forgery with: :exception, prepend: true
before_action :store_location
+ before_action :set_sentry_user
# Ensure every controller authorizes resource or skips authorization (skip_authorization_check)
check_authorization unless: :devise_controller?
+ skip_authorization_check if:
- def store_location
- # store last url - this is needed for post-login redirect to whatever the user last visited.
- return unless request.get?
+ def store_location
+ # store last url - this is needed for post-login redirect to whatever the user last visited.
+ return unless request.get? && !request.xhr?
- if (request.path != '/accounts/sign_in' &&
- request.path != '/accounts/sign_up' &&
- request.path != '/accounts/password/new' &&
- request.path != '/accounts/password/edit' &&
- request.path != '/accounts/confirmation' &&
- request.path != '/accounts/sign_out' &&
- request.path != '/users/ichain_registration/ichain_sign_up' &&
- !request.path.starts_with?(Devise.ichain_base_url) &&
- !request.xhr?) # don't store ajax calls
- session[:return_to] = request.fullpath
- end
- end
+ if !request.path.starts_with?('/accounts') &&
+ request.path != '/users/ichain_registration/ichain_sign_up' &&
+ !request.path.starts_with?(Devise.ichain_base_url)
+ session[:return_to] = request.fullpath
+ end
+ end
def after_sign_in_path_for(_resource)
if (can? :view, Conference) &&
- (!session[:return_to] ||
- session[:return_to] &&
- session[:return_to] == root_path)
+ (!session[:return_to] ||
+ (session[:return_to] &&
+ session[:return_to] == root_path))
admin_conferences_path
else
session[:return_to] || root_path
@@ -42,7 +39,7 @@ def current_ability
end
rescue_from CanCan::AccessDenied do |exception|
- Rails.logger.debug "Access denied on #{exception.action} #{exception.subject.inspect}"
+ Rails.logger.debug { "Access denied on #{exception.action} #{exception.subject.inspect}" }
message = exception.message
message << ' Maybe you need to sign in?' unless @ignore_not_signed_in_user || current_user
redirect_to root_path, alert: message
@@ -59,10 +56,21 @@ def current_ability
Rails.logger.debug('User is disabled!')
sign_out(current_user)
mail = User.admin.first ? User.admin.first.email : 'the admin!'
- redirect_to User.ichain_logout_url, error: "This User is disabled. Please contact #{mail}!"
+ redirect_to User.ichain_logout_url, error: "This User is disabled. Please contact #{mail}!"
end
def not_found
raise ActionController::RoutingError.new('Not Found')
end
+
+ skip_authorization_check only: :apple_pay
+ def apple_pay
+ render plain: ENV.fetch('OSEM_APPLE_PAY_ID', '')
+ end
+
+ def set_sentry_user
+ return unless current_user
+
+ Sentry.set_user(email: current_user.email, id: current_user.id, username: current_user.username)
+ end
end
diff --git a/app/controllers/booths_controller.rb b/app/controllers/booths_controller.rb
index f80576377..92db93b16 100644
--- a/app/controllers/booths_controller.rb
+++ b/app/controllers/booths_controller.rb
@@ -4,7 +4,7 @@ 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]
+ skip_authorize_resource only: %i[withdraw confirm restart]
def index
@booths = current_user.booths.where(conference_id: @conference.id).uniq
diff --git a/app/controllers/commercials_controller.rb b/app/controllers/commercials_controller.rb
index aa48a4064..40f292159 100644
--- a/app/controllers/commercials_controller.rb
+++ b/app/controllers/commercials_controller.rb
@@ -12,33 +12,33 @@ def create
if @commercial.save
redirect_to edit_conference_program_proposal_path(conference_id: @conference.short_title, id: @event.id, anchor: 'commercials-content'),
- notice: 'Commercial was successfully created.'
+ notice: 'Materials were successfully created.'
else
redirect_to edit_conference_program_proposal_path(conference_id: @conference.short_title, id: @event.id, anchor: 'commercials-content'),
- error: "An error prohibited this Commercial from being saved: #{@commercial.errors.full_messages.join('. ')}."
+ error: "An error prohibited these materials from being saved: #{@commercial.errors.full_messages.join('. ')}."
end
end
def update
if @commercial.update(commercial_params)
redirect_to edit_conference_program_proposal_path(conference_id: @conference.short_title, id: @event.id, anchor: 'commercials-content'),
- notice: 'Commercial was successfully updated.'
+ notice: 'Materials were successfully updated.'
else
redirect_to edit_conference_program_proposal_path(conference_id: @conference.short_title, id: @event.id, anchor: 'commercials-content'),
- error: "An error prohibited this Commercial from being saved: #{@commercial.errors.full_messages.join('. ')}."
+ error: "An error prohibited materials from being saved: #{@commercial.errors.full_messages.join('. ')}."
end
end
def destroy
@commercial.destroy
redirect_to edit_conference_program_proposal_path(conference_id: @conference.short_title, id: @event.id),
- notice: 'Commercial was successfully destroyed.'
+ notice: 'Materials were successfully destroyed.'
end
def render_commercial
result = Commercial.render_from_url(params[:url])
if result[:error]
- render plain: result[:error], status: 400
+ render plain: result[:error], status: :bad_request
else
render plain: result[:html]
end
@@ -51,6 +51,6 @@ def set_event
end
def commercial_params
- params.require(:commercial).permit(:url)
+ params.require(:commercial).permit(:title, :url)
end
end
diff --git a/app/controllers/conference_registrations_controller.rb b/app/controllers/conference_registrations_controller.rb
index 70cfc7362..8df31ea05 100644
--- a/app/controllers/conference_registrations_controller.rb
+++ b/app/controllers/conference_registrations_controller.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
class ConferenceRegistrationsController < ApplicationController
- before_action :authenticate_user!, except: [:new, :create]
+ before_action :authenticate_user!, except: %i[new create]
load_resource :conference, find_by: :short_title
- authorize_resource :conference_registrations, class: Registration, except: [:new, :create]
- before_action :set_registration, only: [:edit, :update, :destroy, :show]
+ authorize_resource :conference_registrations, class: Registration, except: %i[new create]
+ before_action :set_registration, only: %i[edit update destroy show]
def new
@registration = Registration.new(conference_id: @conference.id)
@@ -53,20 +53,23 @@ def create
if @registration.save
# Sign in the new user
- unless current_user
- sign_in(@registration.user)
- end
+ sign_in(@registration.user) unless current_user
+
+ MailblusterEditLeadJob.perform_later(@user.id, add_tags: ["snapcon-#{@conference.short_title}"])
- if @conference.tickets.any? && !current_user.supports?(@conference)
+ if @conference.tickets.visible.any? && !current_user.supports?(@conference)
redirect_to conference_tickets_path(@conference.short_title),
notice: 'You are now registered and will be receiving E-Mail notifications.'
else
redirect_to conference_conference_registration_path(@conference.short_title),
notice: 'You are now registered and will be receiving E-Mail notifications.'
end
+ elsif @conference.registration_ticket_required? && !current_user&.supports?(@conference)
+ redirect_to conference_tickets_path(@conference.short_title),
+ notice: 'You must buy a registration ticket before registering.'
else
- flash.now[:error] = "Could not create your registration for #{@conference.title}: "\
- "#{@registration.errors.full_messages.join('. ')}."
+ flash.now[:error] = "Could not create your registration for #{@conference.title}: " \
+ "#{@registration.errors.full_messages.join('. ')}."
render :new
end
end
@@ -76,20 +79,21 @@ def update
redirect_to conference_conference_registration_path(@conference.short_title),
notice: 'Registration was successfully updated.'
else
- flash.now[:error] = "Could not update your registration for #{@conference.title}: "\
- "#{@registration.errors.full_messages.join('. ')}."
+ flash.now[:error] = "Could not update your registration for #{@conference.title}: " \
+ "#{@registration.errors.full_messages.join('. ')}."
render :edit
end
end
def destroy
if @registration.destroy
+ MailblusterEditLeadJob.perform_later(current_user.id, remove_tags: ["snapcon-#{@conference.short_title}"])
redirect_to root_path,
notice: "You are not registered for #{@conference.title} anymore!"
else
redirect_to conference_conference_registration_path(@conference.short_title),
- error: "Could not delete your registration for #{@conference.title}: "\
- "#{@registration.errors.full_messages.join('. ')}."
+ error: "Could not delete your registration for #{@conference.title}: " \
+ "#{@registration.errors.full_messages.join('. ')}."
end
end
@@ -109,14 +113,15 @@ def user_params
def registration_params
params.require(:registration)
- .permit(
- :conference_id,
- :volunteer, :accepted_code_of_conduct,
- vchoice_ids: [], qanswer_ids: [],
- qanswers_attributes: [],
- event_ids: [],
- user_attributes: [
- :username, :email, :name, :password, :password_confirmation]
- )
+ .permit(
+ :conference_id,
+ :volunteer, :accepted_code_of_conduct,
+ vchoice_ids: [], qanswer_ids: [],
+ qanswers_attributes: [],
+ event_ids: [],
+ user_attributes: %i[
+ username email name password password_confirmation
+ ]
+ )
end
end
diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb
index a9dcdd3ba..ef90c670c 100644
--- a/app/controllers/conferences_controller.rb
+++ b/app/controllers/conferences_controller.rb
@@ -1,18 +1,19 @@
# frozen_string_literal: true
class ConferencesController < ApplicationController
+ include ConferenceHelper
+
protect_from_forgery with: :null_session
before_action :respond_to_options
load_and_authorize_resource find_by: :short_title, except: :show
def index
@current = Conference.upcoming.reorder(start_date: :asc)
- @antiquated = Conference.past
- if @antiquated.empty? && @current.empty? && User.empty?
- render :new_install
- end
+ @antiquated = Conference.past.select { |conf| conf.splashpage&.public? }
+ render :new_install if @antiquated.empty? && @current.empty? && User.empty?
end
+ # rubocop:disable Metrics/CyclomaticComplexity
def show
# load conference with header content
@conference = Conference.unscoped.eager_load(
@@ -24,15 +25,17 @@ def show
).find_by!(conference_finder_conditions)
authorize! :show, @conference # TODO: reduce the 10 queries performed here
- splashpage = @conference.splashpage
+ @splashpage = @conference.splashpage
- unless splashpage.present?
- redirect_to admin_conference_splashpage_path(@conference.short_title) && return
- end
+ redirect_to admin_conference_splashpage_path(@conference.short_title) && return unless @splashpage.present?
+
+ # User messages at the top of the page.
+ @unpaid_tickets = current_user_has_unpaid_tickets?
+ @user_needs_to_register = current_user_needs_to_register?
- @image_url = "#{request.protocol}#{request.host}#{@conference.picture}"
+ @image_url = @splashpage.banner_photo_url || @conference.picture_url
- if splashpage.include_cfp
+ if @splashpage.include_cfp?
cfps = @conference.program.cfps
@call_for_events = cfps.find { |call| call.cfp_type == 'events' }
if @call_for_events.try(:open?)
@@ -42,30 +45,28 @@ def show
@call_for_tracks = cfps.find { |call| call.cfp_type == 'tracks' }
@call_for_booths = cfps.find { |call| call.cfp_type == 'booths' }
end
- if splashpage.include_program
- @highlights = @conference.highlighted_events.eager_load(:speakers)
- if splashpage.include_tracks
+ if @splashpage.include_program?
+ @highlights = @conference.highlighted_events.includes(:speakers, :speaker_event_users)
+ if @splashpage.include_tracks?
@tracks = @conference.confirmed_tracks.eager_load(
:room
).order('tracks.name')
end
- if splashpage.include_booths
- @booths = @conference.confirmed_booths.order('title')
- end
- end
- if splashpage.include_registrations || splashpage.include_tickets
- @tickets = @conference.tickets.order('price_cents')
+ @booths = @conference.confirmed_booths.order('title') if @splashpage.include_booths?
+ load_happening_now if @splashpage.include_happening_now
end
- if splashpage.include_lodgings
- @lodgings = @conference.lodgings.order('name')
+ if @splashpage.include_registrations? || @splashpage.include_tickets?
+ @tickets = @conference.tickets.visible.order('price_cents')
end
- if splashpage.include_sponsors
+ @lodgings = @conference.lodgings.order('id') if @splashpage.include_lodgings?
+ if @splashpage.include_sponsors?
@sponsorship_levels = @conference.sponsorship_levels.eager_load(
:sponsors
).order('sponsorship_levels.position ASC', 'sponsors.name')
@sponsors = @conference.sponsors
end
end
+ # rubocop:enable Metrics/CyclomaticComplexity
def calendar
respond_to do |format|
@@ -119,8 +120,23 @@ def conference_finder_conditions
end
def respond_to_options
- respond_to do |format|
- format.html { head :ok }
- end if request.options?
+ if request.options?
+ respond_to do |format|
+ format.html { head :ok }
+ end
+ end
+ end
+
+ def current_user_tickets
+ @current_user_tickets ||= current_user.ticket_purchases.by_conference(@conference)
+ end
+
+ def current_user_needs_to_register?
+ current_user && !@conference.user_registered?(current_user) &&
+ current_user_tickets.where(ticket: @conference.registration_tickets).paid.any?
+ end
+
+ def current_user_has_unpaid_tickets?
+ current_user && current_user_tickets.unpaid.any?
end
end
diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb
index 5dae6d190..29317e210 100644
--- a/app/controllers/payments_controller.rb
+++ b/app/controllers/payments_controller.rb
@@ -12,10 +12,9 @@ def index
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
+ raise CanCan::AccessDenied.new('Nothing to pay for!', :new, Payment) if @total_amount_to_pay.zero?
+ @has_registration_ticket = params[:has_registration_ticket]
@unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference)
end
@@ -24,8 +23,21 @@ def create
if @payment.purchase && @payment.save
update_purchased_ticket_purchases
- redirect_to conference_physical_tickets_path,
- notice: 'Thanks! Your ticket is booked successfully.'
+
+ has_registration_ticket = params[:has_registration_ticket]
+ if has_registration_ticket == 'true'
+ registration = @conference.register_user(current_user)
+ if registration
+ redirect_to conference_physical_tickets_path,
+ notice: "Thanks! Your ticket is booked successfully and you have been registered for #{@conference.title}."
+ return
+ end
+ redirect_to new_conference_conference_registration_path(@conference.short_title),
+ notice: 'Thanks! Your ticket is booked successfully. Please register for the conference.'
+ else
+ redirect_to conference_physical_tickets_path,
+ notice: 'Thanks! Your ticket is booked successfully.'
+ end
else
@total_amount_to_pay = Ticket.total_price(@conference, current_user, paid: false)
@unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference)
diff --git a/app/controllers/physical_tickets_controller.rb b/app/controllers/physical_tickets_controller.rb
index bc917b6ef..60c5e4bd6 100644
--- a/app/controllers/physical_tickets_controller.rb
+++ b/app/controllers/physical_tickets_controller.rb
@@ -8,7 +8,10 @@ class PhysicalTicketsController < ApplicationController
def index
@physical_tickets = current_user.physical_tickets.by_conference(@conference)
+ @has_registration_ticket = current_user.ticket_purchases.where(ticket: @conference.registration_tickets,
+ paid: true).any?
@unpaid_ticket_purchases = current_user.ticket_purchases.by_conference(@conference).unpaid
+ @user = current_user
end
def show
diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb
index 692b4db01..48d62c817 100644
--- a/app/controllers/proposals_controller.rb
+++ b/app/controllers/proposals_controller.rb
@@ -1,34 +1,42 @@
# frozen_string_literal: true
class ProposalsController < ApplicationController
- before_action :authenticate_user!, except: [:show, :new, :create]
+ include ConferenceHelper
+ before_action :authenticate_user!, except: %i[show new create]
load_resource :conference, find_by: :short_title
load_resource :program, through: :conference, singleton: true
load_and_authorize_resource :event, parent: false, through: :program
# We authorize manually in these actions
- skip_authorize_resource :event, only: [:confirm, :restart, :withdraw]
+ skip_authorize_resource :event, only: %i[join confirm restart withdraw]
def index
@event = @program.events.new
@event.event_users.new(user: current_user, event_role: 'submitter')
@events = current_user.proposals(@conference)
+ @volunteer_events = current_user.volunteer_duties(@conference)
end
def show
@event_schedule = @event.event_schedules.find_by(schedule_id: @program.selected_schedule_id)
@speakers_ordered = @event.speakers_ordered
@surveys_after_event = @event.surveys.after_event.select(&:active?)
+ # TODO: include when conference is in session.
+ @happening_now = !@conference.pending? && !@conference.ended? &&
+ @conference.splashpage.include_happening_now
+ load_happening_now if @happening_now
end
def new
@user = User.new
@url = conference_program_proposals_path(@conference.short_title)
@languages = @program.languages_list
+ @superevents = @program.super_events
end
def edit
@url = conference_program_proposal_path(@conference.short_title, params[:id])
@languages = @program.languages_list
+ @superevents = @program.events.where(superevent: true)
end
def create
@@ -62,7 +70,8 @@ def create
if @event.save
Mailbot.submitted_proposal_mail(@event).deliver_later if @conference.email_settings.send_on_submitted_proposal
- redirect_to conference_program_proposals_path(@conference.short_title), notice: 'Proposal was successfully submitted.'
+ redirect_to conference_program_proposals_path(@conference.short_title),
+ notice: 'Proposal was successfully submitted.'
else
flash.now[:error] = "Could not submit proposal: #{@event.errors.full_messages.join(', ')}"
render action: 'new'
@@ -83,11 +92,41 @@ def update
redirect_to conference_program_proposals_path(conference_id: @conference.short_title),
notice: 'Proposal was successfully updated.'
else
- flash.now[:error] = "Could not update proposal: #{@event.errors.full_messages.join(', ')}"
+ flash[:error] = "Could not update proposal: #{@event.errors.full_messages.join(', ')}"
render action: 'edit'
end
end
+ def toggle_favorite
+ users = @event.favourite_users
+ if users.include?(current_user)
+ @event.favourite_users.delete(current_user)
+ else
+ @event.favourite_users << current_user
+ end
+ # TODO: Remove cache busting?
+ @event.touch
+ @program.touch
+ render json: {}
+ end
+
+ # Joining an event marks as user as attending the event, and redirects to room url.
+ # attendees can only join during the event time
+ def join
+ admin = current_user.roles.where(id: @conference.roles).any?
+ registered_happening_now = @conference.user_registered?(current_user) && @event.happening_now?
+ can_view_event = @event.url.present? && (admin || registered_happening_now)
+
+ if can_view_event
+ current_user.mark_attendance_for_conference(@conference)
+ current_user.mark_attendance_for_event(@event)
+ redirect_to @event.url, allow_other_host: true
+ else
+ redirect_to conference_program_proposal_path(@conference, @event),
+ error: 'You cannot join this event yet. Please try again closer to the start of the event.'
+ end
+ end
+
def withdraw
authorize! :update, @event
@url = conference_program_proposal_path(@conference.short_title, params[:id])
@@ -96,7 +135,7 @@ def withdraw
@event.withdraw
selected_schedule = @event.program.selected_schedule
event_schedule = @event.event_schedules.find_by(schedule: selected_schedule) if selected_schedule
- Rails.logger.debug "schedule: #{selected_schedule.inspect} and event_schedule #{event_schedule.inspect}"
+ Rails.logger.debug { "schedule: #{selected_schedule.inspect} and event_schedule #{event_schedule.inspect}" }
if selected_schedule && event_schedule
event_schedule.enabled = false
event_schedule.save
@@ -168,11 +207,12 @@ def registrations; end
private
def event_params
+ # TODO-SNAPCON: Restrict committee review to admins.
params.require(:event).permit(:event_type_id, :track_id, :difficulty_level_id,
- :title, :subtitle, :abstract, :description,
- :require_registration, :max_attendees, :language,
- speaker_ids: []
- )
+ :title, :subtitle, :abstract, :submission_text, :description,
+ :superevent, :parent_id, :require_registration, :max_attendees,
+ :language, :committee_review, :presentation_mode,
+ speaker_ids: [], volunteer_ids: [])
end
def user_params
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 0db33128e..c2e0a71bb 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -26,7 +26,7 @@ def sign_up_params
end
def account_update_params
- user_attributes = [:email, :name, :password, :password_confirmation, :current_password, :email_public]
+ user_attributes = %i[email name password password_confirmation current_password email_public]
user_attributes << :is_admin if current_user.is_admin?
params.require(:user).permit(user_attributes)
end
diff --git a/app/controllers/rooms_controller.rb b/app/controllers/rooms_controller.rb
new file mode 100644
index 000000000..d4b0510e8
--- /dev/null
+++ b/app/controllers/rooms_controller.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class RoomsController < ApplicationController
+ include ConferenceHelper
+ before_action :authenticate_user!
+ protect_from_forgery with: :null_session
+ load_resource :conference, find_by: :short_title
+
+ # TODO: This duplicates too much of the #join route.
+ def live_session
+ @room = Room.find(params[:room_id])
+ user_registered = @conference.user_registered?(current_user)
+ can_view = user_registered || current_user.roles.where(id: @conference.roles).any?
+
+ if @room.url.present? && can_view
+ current_user.mark_attendance_for_conference(@conference)
+ else
+ redirect_to conference_schedule_path(@conference),
+ error: 'You cannot join this room yet. Please try again closer to the start of the event.'
+ end
+ end
+end
diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb
index 5f68c27cd..0a6f121fb 100644
--- a/app/controllers/schedules_controller.rb
+++ b/app/controllers/schedules_controller.rb
@@ -1,16 +1,17 @@
# frozen_string_literal: true
class SchedulesController < ApplicationController
+ include ConferenceHelper
+
load_and_authorize_resource
before_action :respond_to_options
+ before_action :favourites
load_resource :conference, find_by: :short_title
load_resource :program, through: :conference, singleton: true, except: :index
- before_action :load_withdrawn_event_schedules, only: [:show, :events]
+ before_action :load_withdrawn_event_schedules, only: %i[show events]
def show
- event_schedules = @program.selected_event_schedules(
- includes: [{ event: %i[event_type speakers submitter] }]
- )
+ event_schedules = @program.event_schedule_for_fullcalendar
unless event_schedules
redirect_to events_conference_schedule_path(@conference.short_title)
@@ -19,7 +20,7 @@ def show
respond_to do |format|
format.xml do
- @events_xml = event_schedules.map(&:event).group_by{ |event| event.time.to_date } if event_schedules
+ @events_xml = event_schedules.map(&:event).group_by { |event| event.time.to_date } if event_schedules
end
format.ics do
cal = Icalendar::Calendar.new
@@ -29,53 +30,91 @@ def show
end
format.html do
- @rooms = @conference.venue.rooms.order(:name) if @conference.venue
- @dates = @conference.start_date..@conference.end_date
- @step_minutes = @program.schedule_interval.minutes
- @conf_start = @conference.start_hour
- @conf_period = @conference.end_hour - @conf_start
-
+ dates = @conference.start_date..@conference.end_date
# 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
- if @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 @event_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
+ current_day = @conference.current_conference_day
+ @day = current_day.presence || dates.first
+
+ if current_user && @favourites
+ event_schedules = event_schedules.select { |e| e.event.planned_for_user?(current_user) }
end
- @selected_schedules_ids.compact!
- @event_schedules_by_room_id = event_schedules.select { |s| @selected_schedules_ids.include?(s.schedule_id) }.group_by(&:room_id)
+
+ @rooms = FullCalendarFormatter.rooms_to_resources(@conference.rooms) if @conference.rooms
+ @event_schedules = FullCalendarFormatter.event_schedules_to_resources(event_schedules)
+ @now = Time.now.in_time_zone(@conference.timezone).strftime('%FT%T%:z')
end
end
end
def events
@dates = @conference.start_date..@conference.end_date
- @events_schedules = @program.selected_event_schedules(
- includes: [:room, { event: %i[track event_type speakers submitter] }]
- )
- @events_schedules = [] unless @events_schedules
+ @events_schedules = @program.event_schedule_program_view || []
+
+ @unscheduled_events = if @program.selected_schedule
+ @program.events.confirmed - @events_schedules.map(&:event)
+ else
+ @program.events.confirmed
+ end
+ event_type = params[:event_type]
+ if event_type && event_type != 'All Events'
+ @events_schedules.select! { |event_schedule| event_schedule.event.event_type.title == event_type }
+ end
- @unscheduled_events = @program.events.confirmed - @events_schedules.map(&:event)
+ event_ids = @events_schedules.map(&:event_id) + @unscheduled_events.map(&:id)
+ favourited_events(event_ids)
+
+ if current_user && @favourites
+ @events_schedules.keep_if { |es| es.event.planned_for_user?(current_user) }
+ @unscheduled_events.keep_if { |e| e.planned_for_user?(current_user) }
+ end
day = @conference.current_conference_day
@tag = day.strftime('%Y-%m-%d') if day
end
+ def happening_now
+ # TODO: Adapt to include happening next.
+ @events_schedules = get_happening_now_events_schedules(@conference)
+ @current_time = Time.now.in_time_zone(@conference.timezone)
+
+ event_ids = @events_schedules.map { |es| es.event.id }
+ favourited_events(event_ids)
+
+ respond_to do |format|
+ format.html
+ format.json { render json: @events_schedules.to_json(root: false, include: :event) }
+ end
+ end
+
+ def vertical_schedule
+ redirect_to conference_schedule_path(@conference)
+ end
+
def app
- @qr_code = RQRCode::QRCode.new(conference_schedule_url).as_svg(offset: 20, color: '000', shape_rendering: 'crispEdges', module_size: 11)
+ @qr_code = RQRCode::QRCode.new(conference_schedule_url).as_svg(offset: 20, color: '000',
+ shape_rendering: 'crispEdges', module_size: 11)
end
private
+ def favourites
+ @favourites = params[:favourites] == 'true'
+ end
+
+ def favourited_events(event_ids = [])
+ return @favourited_events = [] unless current_user
+
+ @favourited_events ||= FavouriteEvents.where(
+ user_id: current_user.id, event_id: event_ids
+ ).pluck(:event_id)
+ end
+
def respond_to_options
- respond_to do |format|
- format.html { head :ok }
- end if request.options?
+ if request.options?
+ respond_to do |format|
+ format.html { head :ok }
+ end
+ end
end
def load_withdrawn_event_schedules
diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb
index cf00af6f4..22b24dd92 100644
--- a/app/controllers/subscriptions_controller.rb
+++ b/app/controllers/subscriptions_controller.rb
@@ -3,7 +3,7 @@
class SubscriptionsController < ApplicationController
before_action :authenticate_user!
load_resource :conference, find_by: :short_title
- load_and_authorize_resource only: [:create, :destroy], through: :conference
+ load_and_authorize_resource only: %i[create destroy], through: :conference
def create
@subscription = current_user.subscriptions.build(conference_id: @conference.id)
@@ -18,8 +18,10 @@ 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 unsubscribed and you will not be receiving email notifications for #{@conference.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 632a72892..7a09477e4 100644
--- a/app/controllers/ticket_purchases_controller.rb
+++ b/app/controllers/ticket_purchases_controller.rb
@@ -8,23 +8,48 @@ class TicketPurchasesController < ApplicationController
def create
current_user.ticket_purchases.by_conference(@conference).unpaid.destroy_all
+
+ # Create a ticket purchase which can be paid or unpaid
+ count_registration_tickets_before = current_user.count_registration_tickets(@conference)
message = TicketPurchase.purchase(@conference, current_user, params[:tickets].try(:first))
- if message.blank?
- if current_user.ticket_purchases.by_conference(@conference).unpaid.any?
- redirect_to new_conference_payment_path,
- notice: 'Please pay here to get tickets.'
- elsif current_user.ticket_purchases.by_conference(@conference).paid.any?
+ # The new ticket_purchase has been added to the database. current_user.ticket_purchases contains the new one.
+ count_registration_tickets_after = current_user.count_registration_tickets(@conference)
+
+ # Failed to create ticket purchase
+ if message.present?
+ redirect_to conference_tickets_path(@conference.short_title),
+ error: "Oops, something went wrong with your purchase! #{message}"
+ return
+ end
+
+ # Current user already paid for a registration ticket and the current ticket purchase contains one
+ if count_registration_tickets_before == 1 && count_registration_tickets_after > 1
+ redirect_to conference_physical_tickets_path,
+ error: 'You already have one registration ticket for the conference.'
+ return
+ end
+
+ # User needs to pay for tickets if any of them is not free.
+ if current_user.ticket_purchases.by_conference(@conference).unpaid.any?
+ has_registration_ticket = count_registration_tickets_before.zero? && count_registration_tickets_after == 1
+ redirect_to new_conference_payment_path(has_registration_ticket: has_registration_ticket),
+ notice: 'Please pay here to get tickets.'
+ return
+ end
+
+ # Automatically register the user after purchasing a registration ticket.
+ if count_registration_tickets_before.zero? && count_registration_tickets_after == 1
+ registration = @conference.register_user(current_user)
+ if registration
redirect_to conference_physical_tickets_path,
- notice: 'You have free tickets for the conference.'
- elsif @conference.tickets.for_registration.any?
- redirect_to conference_tickets_path(@conference.short_title),
- error: 'Please get at least one ticket to continue.'
+ notice: "Thanks! Your ticket is booked successfully & you have been registered for #{@conference.title}"
else
- redirect_to conference_conference_registration_path(@conference.short_title)
+ redirect_to new_conference_conference_registration_path(@conference.short_title),
+ notice: 'Thanks! Your ticket is booked successfully. Please register for the conference.'
end
else
- redirect_to conference_tickets_path(@conference.short_title),
- error: "Oops, something went wrong with your purchase! #{message}"
+ redirect_to conference_physical_tickets_path,
+ notice: 'Thanks! Your ticket is booked successfully.'
end
end
@@ -32,6 +57,14 @@ def index
@unpaid_ticket_purchases = current_user.ticket_purchases.by_conference(@conference).unpaid
end
+ def destroy
+ @ticket_purchase = TicketPurchase.find(params[:id])
+ authorize! :delete, @ticket_purchase
+ @ticket_purchase.delete
+ redirect_to admin_conference_ticket_path(@conference, @ticket_purchase.ticket.id),
+ notice: "Ticket for user #{@ticket_purchase.user.name} successfully removed."
+ end
+
private
def ticket_purchase_params
diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb
index c62afc06a..6a3043739 100644
--- a/app/controllers/tickets_controller.rb
+++ b/app/controllers/tickets_controller.rb
@@ -3,15 +3,21 @@
class TicketsController < ApplicationController
before_action :authenticate_user!
load_resource :conference, find_by: :short_title
- load_resource :ticket, through: :conference
+ before_action :load_tickets
+ authorize_resource :ticket, through: :conference
authorize_resource :conference_registrations, class: Registration
before_action :check_load_resource, only: :index
- def index; end
+ def index
+ # Clear out unpaid tickets so a user can reselect registration tickets.
+ current_user.ticket_purchases.unpaid.delete_all
+ end
def check_load_resource
- if @tickets.empty?
- redirect_to root_path, notice: "There are no tickets available for #{@conference.title}!"
- end
+ redirect_to root_path, notice: "There are no tickets available for #{@conference.title}!" if @tickets.empty?
+ end
+
+ def load_tickets
+ @tickets = @conference.tickets.visible.order(:title)
end
end
diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb
index 3cc3c6219..22760eb46 100644
--- a/app/controllers/users/omniauth_callbacks_controller.rb
+++ b/app/controllers/users/omniauth_callbacks_controller.rb
@@ -29,14 +29,12 @@ def handle(provider)
begin
user.save!
- if openid.user != user
- openid.user = user
- end
+ openid.user = user if openid.user != user
openid.save!
sign_in user
- redirect_to root_path, notice: "#{user.email} signed in successfully with #{provider}"
- rescue => e
+ redirect_to session[:return_to] || root_path, notice: "#{user.email} signed in successfully with #{provider}"
+ rescue StandardError => e
flash[:error] = e.message
redirect_back_or_to new_user_registration_path
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index f68ea51f3..d661a9b3e 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -10,31 +10,44 @@ def show
end
# GET /users/1/edit
- def edit
- end
+ def edit; end
# PATCH/PUT /users/1
def update
if @user.update(user_params)
- redirect_to @user, notice: 'User was successfully updated.'
+ redirect_to user_path(@user), notice: 'User was successfully updated.'
else
- flash.now[:error] = "An error prohibited your Profile from being saved: #{@user.errors.full_messages.join('. ')}."
+ flash.now[:error] = "An error prohibited your profile from being saved: #{@user.errors.full_messages.join('. ')}."
render :edit
end
end
def search
+ fields = %i[username id name]
+ fields << :email if current_user.is_admin?
respond_to do |format|
format.json do
- render json: { users: User.active.where('username like ?', "%#{params[:query]}%").select(:username, :id) }
+ render json: { users:
+ User.active.where(
+ 'username ILIKE :search OR email ILIKE :search OR name ILIKE :search',
+ search: "%#{params[:query]}%"
+ ).as_json(only: fields, methods: :dropdwon_display) }
end
end
end
private
- # Only allow a trusted parameter "white list" through.
- def user_params
- params.require(:user).permit(:name, :biography, :nickname, :affiliation)
- end
+ def user_params
+ params[:user][:timezone] = params[:user][:timezone].presence || nil
+ params.require(:user).permit(:name, :biography, :nickname, :affiliation,
+ :picture, :picture_cache, :timezone)
+ end
+
+ # Somewhat of a hack: users/current/edit
+ # rubocop:disable Naming/MemoizedInstanceVariableName
+ def load_user
+ @user ||= (params[:id] && params[:id] != 'current' && User.find(params[:id])) || current_user
+ end
+ # rubocop:enable Naming/MemoizedInstanceVariableName
end
diff --git a/app/datatables/registration_datatable.rb b/app/datatables/registration_datatable.rb
index 533a2bf06..1639ef422 100644
--- a/app/datatables/registration_datatable.rb
+++ b/app/datatables/registration_datatable.rb
@@ -17,6 +17,7 @@ def view_columns
name: { source: 'User.name' },
email: { source: 'User.email' },
accepted_code_of_conduct: { source: 'Registration.accepted_code_of_conduct', searchable: false },
+ ticket_type: { source: 'Ticket.title' },
actions: { source: 'Registration.id', searchable: false, orderable: false }
}
end
@@ -41,6 +42,7 @@ def data
roles: conference_role_titles(record.user),
email: record.email,
accepted_code_of_conduct: !!record.accepted_code_of_conduct, # rubocop:disable Style/DoubleNegation
+ ticket_type: record.user.tickets.where(conference: conference).pluck(:title),
edit_url: edit_admin_conference_registration_path(conference, record),
DT_RowId: dom_id(record)
}
@@ -48,7 +50,7 @@ def data
end
def get_raw_records # rubocop:disable Naming/AccessorMethodName
- conference.registrations.includes(user: :roles).references(:users, :roles).distinct
+ conference.registrations.includes(user: %i[roles tickets]).references(:users, :roles).distinct
end
# override upstream santitation, which converts everything to strings
diff --git a/app/datatables/user_datatable.rb b/app/datatables/user_datatable.rb
index 8ee1acb69..55d1e2c38 100644
--- a/app/datatables/user_datatable.rb
+++ b/app/datatables/user_datatable.rb
@@ -21,9 +21,11 @@ def view_columns
confirmed_at: { source: 'User.confirmed_at', searchable: false },
email: { source: 'User.email' },
name: { source: 'User.name' },
+ username: { source: 'User.username' },
attended: { source: 'attended_count', searchable: false },
roles: { source: 'Role.name' },
- actions: { source: 'User.id', searchable: false, orderable: false }
+ actions: { source: 'User.id', searchable: false, orderable: false },
+ confirmed: { source: 'User.confirmed_at', searchable: false }
}
end
@@ -36,11 +38,13 @@ def data
confirmed_at: record.confirmed_at,
email: record.email,
name: record.name,
+ username: record.username,
attended: record.attended_count,
roles: record.roles.any? ? show_roles(record.get_roles) : 'None',
view_url: admin_user_path(record),
edit_url: edit_admin_user_path(record),
- DT_RowId: dom_id(record)
+ DT_RowId: dom_id(record),
+ confirmed: record.confirmed_at.present?
}
end
end
@@ -48,9 +52,9 @@ def data
# rubocop:disable Naming/AccessorMethodName
def get_raw_records
User.left_outer_joins(:registrations, :roles)
- .distinct
- .select("users.*, COUNT(CASE WHEN registrations.attended = 't' THEN 1 END) AS attended_count")
- .group('users.id')
+ .distinct
+ .select("users.*, COUNT(CASE WHEN registrations.attended = 't' THEN 1 END) AS attended_count")
+ .group('users.id')
end
# rubocop:enable Naming/AccessorMethodName
diff --git a/app/helpers/admin/tickets_helper.rb b/app/helpers/admin/tickets_helper.rb
new file mode 100644
index 000000000..5db9f82de
--- /dev/null
+++ b/app/helpers/admin/tickets_helper.rb
@@ -0,0 +1,14 @@
+module Admin
+ module TicketsHelper
+ def default_ticket_email_template
+ {
+ subject_input_id: 'ticket_email_subject',
+ subject_text: '{conference} | Ticket Confirmation and PDF!',
+ body_input_id: 'ticket_email_body',
+ body_text: "Dear {name},\n\nThanks! You have successfully booked {ticket_quantity} {ticket_title}
+ticket(s) for the event {conference}. Your transaction id is {ticket_purchase_id}.\nPlease,
+find the ticket(s) pdf attached.\n\nBest wishes,\n{conference} Team"
+ }
+ end
+ end
+end
diff --git a/app/helpers/admin/volunteers_helper.rb b/app/helpers/admin/volunteers_helper.rb
index 0833c7713..cf0ed7129 100644
--- a/app/helpers/admin/volunteers_helper.rb
+++ b/app/helpers/admin/volunteers_helper.rb
@@ -3,7 +3,8 @@
module Admin
module VolunteersHelper
def can_manage_volunteers?(conference)
- current_user.has_cached_role?(:organizer, conference) || current_user.has_cached_role?(:volunteers_coordinator, conference)
+ current_user.has_cached_role?(:organizer,
+ conference) || current_user.has_cached_role?(:volunteers_coordinator, conference)
end
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index e3474111f..6edc74c84 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
+DEFAULT_LOGO = Rails.configuration.conference[:default_logo_filename]
+
+# TODO-SNAPCON: Refactor this module. Move chunks to a dates_help, some events_helper
module ApplicationHelper
+ include Pagy::Frontend
# Returns a string build from the start and end date of the given conference.
#
# If the conference is only one day long
@@ -31,8 +35,7 @@ def date_string(start_date, end_date)
endstr = end_date.strftime('%B %d, %Y')
end
- result = startstr + endstr
- result
+ startstr + endstr
end
# Returns time with conference timezone
@@ -46,15 +49,18 @@ def resource_name
end
def add_association_link(association_name, form_builder, div_class, html_options = {})
- link_to_add_association 'Add ' + association_name.to_s.singularize, form_builder, div_class, html_options.merge(class: 'assoc btn btn-success')
+ link_to_add_association 'Add ' + association_name.to_s.singularize, form_builder, div_class,
+ html_options.merge(class: 'assoc btn btn-success')
end
def remove_association_link(association_name, form_builder)
- link_to_remove_association('Remove ' + association_name.to_s.singularize, form_builder, class: 'assoc btn btn-danger') + tag(:hr)
+ link_to_remove_association('Remove ' + association_name.to_s.singularize, form_builder,
+ class: 'assoc btn btn-danger') + tag.hr
end
def dynamic_association(association_name, title, form_builder, options = {})
- render 'shared/dynamic_association', association_name: association_name, title: title, f: form_builder, hint: options[:hint]
+ render 'shared/dynamic_association', association_name: association_name, title: title, f: form_builder,
+hint: options[:hint]
end
def tracks(conference)
@@ -75,24 +81,25 @@ def unread_notifications(user)
# Output will be 'title, description and conference'
def updated_attributes(version)
version.changeset
- .reject{ |_, values| values[0].blank? && values[1].blank? }
- .keys.map{ |key| key.gsub('_id', '').tr('_', ' ')}.join(', ')
- .reverse.sub(',', ' dna ').reverse
+ .reject { |_, values| values[0].blank? && values[1].blank? }
+ .keys.map { |key| key.gsub('_id', '').tr('_', ' ') }.join(', ')
+ .reverse.sub(',', ' dna ').reverse
end
def normalize_array_length(hashmap, length)
hashmap.each_value do |value|
- if value.length < length
- value.fill(value[-1], value.length...length)
- end
+ value.fill(value[-1], value.length...length) if value.length < length
end
end
+ # TODO: Move to the event model.
def concurrent_events(event)
return nil unless event.scheduled? && event.program.selected_event_schedules
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 }
+ other_event_schedules = event.program.selected_event_schedules.reject do |other_event_schedule|
+ other_event_schedule == event_schedule
+ end
concurrent_events = []
event_time_range = (event_schedule.start_time.strftime '%Y-%m-%d %H:%M')...(event_schedule.end_time.strftime '%Y-%m-%d %H:%M')
@@ -100,15 +107,19 @@ def concurrent_events(event)
next unless other_event_schedule.event.confirmed?
other_event_time_range = (other_event_schedule.start_time.strftime '%Y-%m-%d %H:%M')...(other_event_schedule.end_time.strftime '%Y-%m-%d %H:%M')
- if (event_time_range.to_a & other_event_time_range.to_a).present?
- concurrent_events << other_event_schedule.event
- end
+ concurrent_events << other_event_schedule.event if (event_time_range.to_a & other_event_time_range.to_a).present?
end
- concurrent_events
+ concurrent_events.sort_by { |schedule| schedule.room&.order }
end
def speaker_links(event)
- safe_join(event.speakers.map{ |speaker| link_to speaker.name, admin_user_path(speaker) }, ',')
+ safe_join(event.speakers.map { |speaker| link_to speaker.name, admin_user_path(speaker) }, ',')
+ end
+
+ def volunteer_links(event)
+ safe_join(event.volunteers.map do |volunteer|
+ link_to(volunteer.name, admin_user_path(volunteer))
+ end, ', ')
end
def event_types_sentence(conference)
@@ -138,18 +149,37 @@ def hidden_if_conference_over(conference)
'hidden' if Date.today > conference.end_date
end
- def nav_root_link_for(conference)
- link_text = (
- conference.try(:organization).try(:name) || ENV.fetch('OSEM_NAME', 'OSEM')
- )
+ # TODO-SNAPCON: Replace this with a search for a conference logo.
+ # TODO: If conference is defined, the alt text should be conference name.
+ def nav_root_link_for(conference = nil)
+ path = conference&.id.present? ? conference_path(conference) : root_path
link_to(
- link_text,
- root_path,
+ image_tag(conference_logo_url(conference), alt: nav_link_text(conference)),
+ path,
class: 'navbar-brand',
- title: 'Open Source Event Manager'
+ title: nav_link_text(conference)
)
end
+ # TODO-SNAPCON: This should be the conference title.
+ def nav_link_text(conference = nil)
+ conference.try(:organization).try(:name) || ENV.fetch('OSEM_NAME', 'OSEM')
+ end
+
+ # TODO: Consider Renaming this?
+ # TODO: Allow passing in an organization
+ def conference_logo_url(conference = nil)
+ return DEFAULT_LOGO unless conference
+
+ if conference.picture.present?
+ conference.picture.thumb.url
+ elsif conference.organization&.picture.present?
+ conference.organization.picture.thumb.url
+ else
+ DEFAULT_LOGO
+ end
+ end
+
# returns the url to be used for logo on basis of sponsorship level position
def get_logo(object)
if object.try(:sponsorship_level)
@@ -164,4 +194,19 @@ def get_logo(object)
object.picture.large.url
end
end
+
+ # Embed links with a localized timezone URL
+ # Timestamps are stored at UTC but in the real timezone.
+ # We must convert then shift the time back to get the correct value.
+ # TODO: just take in an object?
+ def inyourtz(time, timezone, &)
+ time = time.in_time_zone(timezone)
+ time -= time.utc_offset
+ link_to("https://inyourtime.zone/t?#{time.to_i}", target: '_blank', rel: 'noopener', &)
+ end
+
+ def visible_conference_links
+ @visible_conference_links ||=
+ Conference.all.select(:id, :organization_id, :title, :short_title, :start_date).includes(:splashpage, :organization).select { |conf| can?(:show, conf) }.group_by(&:organization)
+ end
end
diff --git a/app/helpers/chart_helper.rb b/app/helpers/chart_helper.rb
index 7503e55b3..ac350dceb 100644
--- a/app/helpers/chart_helper.rb
+++ b/app/helpers/chart_helper.rb
@@ -2,11 +2,9 @@
module ChartHelper
def chart_values(distribution_hash)
- Hash[
- distribution_hash.collect do |key, data|
- [key, data['value']]
- end
- ]
+ distribution_hash.collect do |key, data|
+ [key, data['value']]
+ end.to_h
end
def chart_colors(distribution_hash)
diff --git a/app/helpers/conference_helper.rb b/app/helpers/conference_helper.rb
index f685ed894..d393b6099 100644
--- a/app/helpers/conference_helper.rb
+++ b/app/helpers/conference_helper.rb
@@ -8,7 +8,7 @@ def one_call_open(*calls)
# Return true if exactly two of those calls are open: call_for_papers , call_for_tracks , call_for_booths
def two_calls_open(*calls)
- calls.count{ |call| call.try(:open?) } == 2
+ calls.count { |call| call.try(:open?) } == 2
end
# URL for sponsorship emails
@@ -22,12 +22,22 @@ def sponsorship_mailto(conference)
].join
end
+ def short_ticket_description(ticket)
+ return unless ticket.description
+
+ markdown(ticket.description.split("\n").first&.strip)
+ end
+
+ def conference_color(conference)
+ conference.color.presence || Rails.configuration.conference[:default_color]
+ end
+
# adds events to icalendar for proposals in a conference
def icalendar_proposals(calendar, proposals, conference)
proposals.each do |proposal|
calendar.event do |e|
e.dtstart = proposal.time
- e.dtend = proposal.time + proposal.event_type.length * 60
+ e.dtend = proposal.time + (proposal.event_type.length * 60)
e.duration = "PT#{proposal.event_type.length}M"
e.created = proposal.created_at
e.last_modified = proposal.updated_at
@@ -45,9 +55,50 @@ def icalendar_proposals(calendar, proposals, conference)
location += "#{v.country_name}, " if v.country_name
e.location = location
end
- e.categories = conference.title, "Difficulty: #{proposal.difficulty_level.title}", "Track: #{proposal.track.name}"
+ e.categories = conference.title, "Difficulty: #{proposal.difficulty_level.title}",
+ "Track: #{proposal.track.name}"
end
end
calendar
end
+
+ def get_happening_now_events_schedules(conference)
+ events_schedules = filter_events_schedules(conference, :happening_now?)
+ events_schedules ||= []
+ events_schedules
+ end
+
+ def get_happening_next_events_schedules(conference)
+ events_schedules = filter_events_schedules(conference, :happening_later?)
+
+ return [] if events_schedules.empty?
+
+ # events_schedules have been sorted by start_time in selected_event_schedules
+ happening_next_time = events_schedules[0].start_time
+ events_schedules.select { |s| s.start_time == happening_next_time }
+ end
+
+ def load_happening_now
+ events_schedules_list = get_happening_now_events_schedules(@conference)
+ @is_happening_next = false
+ if events_schedules_list.empty?
+ events_schedules_list = get_happening_next_events_schedules(@conference)
+ @is_happening_next = true
+ end
+ @events_schedules_limit = Rails.configuration.conference[:events_per_page]
+ @events_schedules_length = events_schedules_list.length
+ @pagy, @events_schedules = pagy_array(events_schedules_list,
+ items: @events_schedules_limit,
+ link_extra: 'data-remote="true"')
+ end
+
+ private
+
+ # TODO: Move this to using the cached method on program/schedule
+ def filter_events_schedules(conference, filter)
+ conference.program.selected_event_schedules(
+ includes: [:event, :room, { event:
+ %i[event_type speakers speaker_event_users track program] }]
+ ).select(&filter)
+ end
end
diff --git a/app/helpers/date_time_helper.rb b/app/helpers/date_time_helper.rb
index 420b086a8..4e3337286 100644
--- a/app/helpers/date_time_helper.rb
+++ b/app/helpers/date_time_helper.rb
@@ -21,9 +21,17 @@ def length_timestamp(length)
def format_datetime(obj)
return unless obj
+ obj = DateTime.parse(obj) unless obj.respond_to?(:strftime)
+
obj.strftime('%Y-%m-%d %H:%M')
end
+ def format_all_timestamps(lst, conference)
+ lst.map do |ts|
+ "#{format_datetime(ts.in_time_zone(conference.timezone))} #{timezone_text(conference)}"
+ end.to_sentence
+ end
+
def show_time(length)
return '0 h 0 min' if length.blank?
diff --git a/app/helpers/event_types_helper.rb b/app/helpers/event_types_helper.rb
index e200e33cc..ca7c3e511 100644
--- a/app/helpers/event_types_helper.rb
+++ b/app/helpers/event_types_helper.rb
@@ -8,6 +8,16 @@ module EventTypesHelper
# ====Returns
# * +String+ -> number of registrations / max allowed registrations
def event_type_select_options(event_types = {})
- event_types.map { |type| ["#{type.title} - #{show_time(type.length)}", type.id] }
+ event_types.map do |type|
+ [
+ "#{type.title} - #{show_time(type.length)}",
+ type.id,
+ { data: {
+ min_words: type.minimum_abstract_length,
+ max_words: type.maximum_abstract_length,
+ instructions: type.submission_template
+ } }
+ ]
+ end
end
end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index ddb16e4ca..86f129806 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+# TODO: Split this module into smaller modules
+# rubocop:disable Metrics/ModuleLength
module EventsHelper
##
# Includes functions related to events
@@ -13,6 +15,7 @@ def registered_text(event)
"Registered: #{event.registrations.count}"
end
+ # TODO-SNAPCON: Move to admin helper
def rating_stars(rating, max, options = {})
Array.new(max) do |counter|
content_tag(
@@ -24,25 +27,27 @@ def rating_stars(rating, max, options = {})
end.join.html_safe
end
+ # TODO-SNAPCON: Move to admin helper
def rating_fraction(rating, max, options = {})
content_tag('span', "#{rating}/#{max}", **options)
end
- def replacement_event_notice(event_schedule)
+ def replacement_event_notice(event_schedule, styles: '')
if event_schedule.present? && event_schedule.replacement?(@withdrawn_event_schedules)
replaced_event = event_schedule.replaced_event_schedule.try(:event)
content_tag :span do
- concat content_tag :span, 'Please note that this talk replaces '
- concat link_to replaced_event.title, conference_program_proposal_path(@conference.short_title, replaced_event.id)
+ concat content_tag :span, 'Please note that this event replaces '
+ concat link_to replaced_event.title,
+ conference_program_proposal_path(@conference.short_title, replaced_event.id), style: styles
end
end
end
def canceled_replacement_event_label(event, event_schedule, *label_classes)
if event.state == 'canceled' || event.state == 'withdrawn'
- content_tag :span, 'CANCELED', class: (['label', 'label-danger'] + label_classes)
+ content_tag :span, 'CANCELED', class: (%w[label label-danger] + label_classes)
elsif event_schedule.present? && event_schedule.replacement?(@withdrawn_event_schedules)
- content_tag :span, 'REPLACEMENT', class: (['label', 'label-info'] + label_classes)
+ content_tag :span, 'REPLACEMENT', class: (%w[label label-info] + label_classes)
end
end
@@ -50,6 +55,7 @@ def rating_tooltip(event, max_rating)
"#{event.average_rating}/#{max_rating}, #{pluralize(event.voters.length, 'vote')}"
end
+ # TODO-SNAPCON: Move to admin helper
def event_type_dropdown(event, event_types, conference_id)
selection = event.event_type.try(:title) || 'Event Type'
options = event_types.collect do |event_type|
@@ -65,6 +71,7 @@ def event_type_dropdown(event, event_types, conference_id)
active_dropdown(selection, options)
end
+ # TODO-SNAPCON: Move to admin helper
def track_dropdown(event, tracks, conference_id)
selection = event.track.try(:name) || 'Track'
options = tracks.collect do |track|
@@ -80,6 +87,7 @@ def track_dropdown(event, tracks, conference_id)
active_dropdown(selection, options)
end
+ # TODO-SNAPCON: Move to admin helper
def difficulty_dropdown(event, difficulties, conference_id)
selection = event.difficulty_level.try(:title) || 'Difficulty'
options = difficulties.collect do |difficulty|
@@ -95,6 +103,7 @@ def difficulty_dropdown(event, difficulties, conference_id)
active_dropdown(selection, options)
end
+ # TODO-SNAPCON: Move to admin helper
def state_dropdown(event, conference_id, email_settings)
selection = event.state.humanize
options = []
@@ -151,6 +160,7 @@ def state_dropdown(event, conference_id, email_settings)
active_dropdown(selection, options)
end
+ # TODO-SNAPCON: Move to admin helper
def event_switch_checkbox(event, attribute, conference_id)
check_box_tag(
conference_id,
@@ -165,8 +175,144 @@ def event_switch_checkbox(event, attribute, conference_id)
)
end
+ def event_favourited?(event, current_user)
+ return false unless current_user
+
+ event.favourite_users.exists?(current_user.id)
+ end
+
+ # TODO-SNAPCON: These need to be refactored.
+ # It's not clear which should be an object vs when to use a tz string.
+ def display_timezone(user, conference)
+ return conference.timezone unless user
+
+ user.timezone.presence || conference.timezone
+ end
+
+ def timezone_offset(object)
+ Time.now.in_time_zone(object.timezone).utc_offset / 1.hour
+ end
+
+ def timezone_text(object)
+ Time.now.in_time_zone(object.timezone).strftime('%Z')
+ end
+
+ # timezone: Eastern Time (US & Canada) (UTC -5)
+ def timezone_mapping(timezone)
+ return unless timezone
+
+ offset = Time.now.in_time_zone(timezone).utc_offset / 1.hour
+ text = Time.now.in_time_zone(timezone).strftime('%Z')
+ "#{text} (UTC #{offset})"
+ end
+
+ def convert_timezone(date, old_timezone, new_timezone)
+ if date && old_timezone && new_timezone
+ date.strftime('%Y-%m-%dT%H:%M:%S').in_time_zone(old_timezone).in_time_zone(new_timezone)
+ end
+ end
+
+ def join_event_link(event, event_schedule, current_user, small: false)
+ return if !event_schedule || event.ended?
+
+ unless event_schedule.room_url.present?
+ return content_tag :span, 'In-person only', class: 'label label-default'
+ end
+
+ unless current_user
+ return content_tag :span, 'Log in to view join link', class: 'label label-default'
+ end
+
+ conference = event.conference
+ is_now = event_schedule.happening_now? # 30 minute threshold.
+ is_registered = conference.user_registered?(current_user)
+ admin = current_user.roles.where(id: conference.roles).any? || current_user.is_admin
+ # is_presenter = event.speakers.include?(current_user) || event.volunteers.include?(current_user)
+
+ if admin || (is_now && is_registered)
+ join_btn = link_to("Join Event Now #{'(Early)' unless is_now}",
+ join_conference_program_proposal_path(conference, event),
+ target: '_blank', class: "btn btn-primary #{'btn-xs' if small}",
+ 'aria-label': "Join #{event.title}", rel: 'noopener')
+ if event_schedule.room.discussion_url.present?
+ discussion_link = link_to('Open Chat',
+ event_schedule.room.discussion_url,
+ target: '_blank', class: "btn btn-info #{'btn-xs' if small}",
+ 'aria-label': "Join #{event.title}", rel: 'noopener')
+ content_tag(:span, join_btn + discussion_link, class: 'btn-group')
+ else
+ join_btn
+ end
+ elsif is_registered
+ content_tag :span, class: 'label label-primary' do
+ 'Click to Join Online During Event'
+ end
+ else
+ link_to('Register for the conference to join this event.',
+ conference_conference_registration_path(conference),
+ class: 'btn btn-info btn-xs',
+ 'aria-label': "Register for #{event.title}")
+ end
+ end
+
+ def calendar_timestamp(timestamp, _timezone)
+ timestamp = timestamp.in_time_zone('GMT')
+ timestamp -= timestamp.utc_offset
+ timestamp.strftime('%Y%m%dT%H%M%S')
+ end
+
+ def google_calendar_link(event_schedule)
+ event = event_schedule.event
+ conference = event.conference
+ calendar_base = 'https://www.google.com/calendar/render'
+ start_timestamp = calendar_timestamp(event_schedule.start_time, conference.timezone)
+ end_timestamp = calendar_timestamp(event_schedule.end_time, conference.timezone)
+ event_details = {
+ action: 'TEMPLATE',
+ text: "#{event.title} at #{conference.title}",
+ details: calendar_event_text(event, event_schedule, conference),
+ location: "#{event_schedule.room.name} #{event_schedule.room_url}",
+ dates: "#{start_timestamp}/#{end_timestamp}",
+ ctz: event_schedule.timezone
+ }
+ "#{calendar_base}?#{event_details.to_param}"
+ end
+
+ def css_background_color(color)
+ "background-color: #{color}; color: #{contrast_color(color)};"
+ end
+
+ def user_options_for_dropdown(event, column)
+ users = event.send(column).pluck(:id, :name, :username, :email)
+ options_for_select(users.map { |u| ["#{u[1]} (#{u[2]} #{u[3]})", u[0]] }, users.map(&:first))
+ end
+
+ def committee_only_actions(user, conference, roles: %i[organizer cfp], &block)
+ return unless user
+
+ role_map = roles.map { |role| { name: role, resource: conference } }
+ return unless user.is_admin || user.has_any_role?(*role_map)
+
+ content_tag(:div, class: 'panel panel-info') do
+ concat content_tag(:div, 'Conference Organizers', class: 'panel-heading')
+ concat content_tag(:div, capture(&block), class: 'panel-body')
+ end
+ end
+
private
+ def calendar_event_text(event, event_schedule, conference)
+ <<~TEXT
+ #{conference.title} - #{event.title}
+ #{event_schedule.start_time.strftime('%Y %B %e - %H:%M')} #{event_schedule.timezone}
+
+ More Info: #{conference_program_proposal_url(conference, event)}
+ Join: #{event.url}
+
+ #{truncate(event.abstract, length: 200)}
+ TEXT
+ end
+
def active_dropdown(selection, options)
# Consistent rendering of dropdown lists that submit patched changes
#
@@ -193,3 +339,4 @@ def active_dropdown(selection, options)
end
end
end
+# rubocop:enable Metrics/ModuleLength
diff --git a/app/helpers/format_helper.rb b/app/helpers/format_helper.rb
index 152ac239a..b6b492b4a 100644
--- a/app/helpers/format_helper.rb
+++ b/app/helpers/format_helper.rb
@@ -1,9 +1,8 @@
# frozen_string_literal: true
+require 'redcarpet/render_strip'
+
module FormatHelper
- ##
- # Includes functions related to formatting (like adding classes, colors)
- ##
def status_icon(object)
case object.state
when 'new', 'to_reject', 'to_accept'
@@ -42,26 +41,24 @@ def variant_from_delta(delta, reverse: false)
def target_progress_color(progress)
progress = progress.to_i
- result =
- case
- when progress >= 90 then 'green'
- when progress < 90 && progress >= 80 then 'orange'
- else 'red'
+ if progress >= 90
+ 'green'
+ elsif progress < 90 && progress >= 80
+ 'orange'
+ else
+ 'red'
end
-
- result
end
def days_left_color(days_left)
days_left = days_left.to_i
if days_left > 30
- result = 'green'
+ 'green'
elsif days_left < 30 && days_left > 10
- result = 'orange'
+ 'orange'
else
- result = 'red'
+ 'red'
end
- result
end
def bootstrap_class_for(flash_type)
@@ -80,48 +77,32 @@ def bootstrap_class_for(flash_type)
end
def label_for(event_state)
- result = ''
case event_state
when 'new'
- result = 'label label-primary'
- when 'withdrawn'
- result = 'label label-danger'
- when 'unconfirmed'
- result = 'label label-success'
- when 'confirmed'
- result = 'label label-success'
+ 'label label-primary'
+ when 'withdrawn', 'cancelled'
+ 'label label-danger'
+ when 'unconfirmed', 'confirmed'
+ 'label label-success'
when 'rejected'
- result = 'label label-warning'
- when 'canceled'
- result = 'label label-danger'
+ 'label label-warning'
end
- result
end
def icon_for_todo(bool)
- if bool
- 'fa-solid fa-check'
- else
- 'fa-solid fa-xmark'
- end
+ bool ? 'fa-solid fa-check' : 'fa-solid fa-xmark'
end
def class_for_todo(bool)
- if bool
- 'todolist-ok'
- else
- 'todolist-missing'
- end
+ bool ? 'todolist-ok' : 'todolist-missing'
end
def word_pluralize(count, singular, plural = nil)
- word = if (count == 1 || count =~ /^1(\.0+)?$/)
- singular
- else
- plural || singular.pluralize
- end
-
- word
+ if count.positive? && count < 2
+ singular
+ else
+ plural || singular.pluralize
+ end
end
# Returns black or white deppending on what of them contrast more with the
@@ -133,7 +114,7 @@ def contrast_color(hexcolor)
g = hexcolor[3..4].to_i(16)
b = hexcolor[5..6].to_i(16)
yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000
- (yiq >= 128) ? 'black' : 'white'
+ yiq >= 128 ? 'black' : 'white'
end
def td_height(rooms)
@@ -173,40 +154,43 @@ def speaker_width(rooms)
speaker_height(rooms) - 4
end
- def carousel_item_class(number, carousel_number, num_cols, col)
- item_class = 'item'
- item_class += ' first' if number == 0
- item_class += ' last' if number == (carousel_number - 1)
- if (col && ((col / num_cols) == number)) || (!col && number == 0)
- item_class += ' active'
- end
- item_class
- end
-
def selected_scheduled?(schedule)
- (schedule == @selected_schedule) ? 'Yes' : 'No'
+ schedule == @selected_schedule ? 'Yes' : 'No'
end
- def markdown(text, escape_html=true)
+ def markdown(text, escape_html = true)
return '' if text.nil?
markdown_options = {
autolink: true,
space_after_headers: true,
- no_intra_emphasis: true,
+ # no_intra_emphasis: true, # SNAPCON
fenced_code_blocks: true,
- disable_indented_code_blocks: true
+ disable_indented_code_blocks: true,
+ tables: true, # SNAPCON
+ strikethrough: true, # SNAPCON
+ footnotes: true, # SNAPCON
+ superscript: true # SNAPCON
}
render_options = {
escape_html: escape_html,
safe_links_only: true
}
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(render_options), markdown_options)
- sanitize(sanitize(markdown.render(text)), scrubber: Loofah::Scrubbers::NoFollow.new)
+ rendered = sanitize(markdown.render(text))
+ escape_html ? sanitize(rendered, scrubber: Loofah::Scrubbers::NoFollow.new) : rendered.html_safe
+ end
+
+ def markdown_hint(text = '')
+ link = link_to('**Markdown Syntax**',
+ 'https://daringfireball.net/projects/markdown/syntax',
+ target: '_blank', rel: 'noopener')
+ markdown("#{text} Please look at #{link} to format your text", false)
end
- def markdown_hint(text='')
- markdown("#{text} Please look at #{link_to '**Markdown Syntax**', 'https://daringfireball.net/projects/markdown/syntax', target: '_blank'} to format your text", false)
+ # Return a plain text markdown stripped of formatting.
+ def plain_text(content)
+ Redcarpet::Markdown.new(Redcarpet::Render::StripDown).render(content)
end
def quantity_left_of(resource)
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 906e6f3b8..11c455c2b 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -19,7 +19,8 @@ def omniauth_configured
unless Rails.application.secrets.send(provider_key).blank? || Rails.application.secrets.send(provider_secret).blank?
providers << provider
end
- providers << provider if ENV.fetch("OSEM_#{provider.upcase}_KEY", nil).present? && ENV.fetch("OSEM_#{provider.upcase}_SECRET", nil).present?
+ providers << provider if ENV.fetch("OSEM_#{provider.upcase}_KEY",
+ nil).present? && ENV.fetch("OSEM_#{provider.upcase}_SECRET", nil).present?
end
providers.uniq
@@ -29,6 +30,6 @@ def omniauth_configured
# Outputs the roles of a user, including the conferences for which the user has the roles
# Eg. organizer(oSC13, oSC14), cfp(oSC12, oSC13)
def show_roles(roles)
- roles.map{ |x| x[0].titleize + ' (' + x[1].join(', ') + ')' }.join ', '
+ roles.map { |x| x[0].titleize + ' (' + x[1].join(', ') + ')' }.join ', '
end
end
diff --git a/app/helpers/versions_helper.rb b/app/helpers/versions_helper.rb
index 0f355f5a0..04af38a1c 100644
--- a/app/helpers/versions_helper.rb
+++ b/app/helpers/versions_helper.rb
@@ -38,7 +38,7 @@ def link_to_user(user_id)
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}"
+ "#{name || 'Unknown user'} with ID #{user_id}"
end
end
@@ -63,20 +63,36 @@ def current_or_last_object_state(model_name, id)
end
def subscription_change_description(version)
- user_id = current_or_last_object_state(version.item_type, version.item_id).user_id
+ user_id = current_or_last_object_state(version.item_type, version.item_id)&.user_id
+ unless user_id
+ if version.event == 'create'
+ return 'subscribed (unkown user) to'
+ else
+ return 'unsubscribed (unknown user) from'
+ end
+ 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 unless user_id.to_s == version.whodunnit
version.event == 'create' ? "subscribed #{user_name} to" : "unsubscribed #{user_name} from"
end
+ # rubocop:disable Metrics/CyclomaticComplexity:
def registration_change_description(version)
if version.item_type == 'Registration'
- user_id = current_or_last_object_state(version.item_type, version.item_id).user_id
+ 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_id = current_or_last_object_state('Registration', registration_id).user_id
+ 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
+ unless user_id
+ case version.event
+ when 'create' then return 'registered to (unkown user)'
+ when 'update' then return 'updated (unkown user) of the registration for'
+ when 'destroy' then return 'unregistered from'
+ end
+ end
+
if user_id.to_s == version.whodunnit
case version.event
when 'create' then 'registered to'
@@ -91,6 +107,7 @@ def registration_change_description(version)
end
end
end
+ # rubocop:enable Metrics/CyclomaticComplexity:
def comment_change_description(version)
user = current_or_last_object_state(version.item_type, version.item_id).user
@@ -123,10 +140,10 @@ def general_change_description(version)
end
def event_change_description(version)
- case
- when version.event == 'create' then 'submitted new'
+ if version.event == 'create'
+ 'submitted new'
- when version.changeset['state']
+ elsif version.changeset['state']
case version.changeset['state'][1]
when 'unconfirmed' then 'accepted'
when 'withdrawn' then 'withdrew'
diff --git a/app/jobs/mailbluster_create_lead_job.rb b/app/jobs/mailbluster_create_lead_job.rb
new file mode 100644
index 000000000..a3934ce82
--- /dev/null
+++ b/app/jobs/mailbluster_create_lead_job.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class MailblusterCreateLeadJob < ApplicationJob
+ queue_as :default
+
+ def perform(user_id)
+ MailblusterManager.create_lead(User.find(user_id))
+ end
+end
diff --git a/app/jobs/mailbluster_delete_lead_job.rb b/app/jobs/mailbluster_delete_lead_job.rb
new file mode 100644
index 000000000..387d1cc00
--- /dev/null
+++ b/app/jobs/mailbluster_delete_lead_job.rb
@@ -0,0 +1,7 @@
+class MailblusterDeleteLeadJob < ApplicationJob
+ queue_as :default
+
+ def perform(user)
+ MailblusterManager.delete_lead(user)
+ end
+end
diff --git a/app/jobs/mailbluster_edit_lead_job.rb b/app/jobs/mailbluster_edit_lead_job.rb
new file mode 100644
index 000000000..ae159335f
--- /dev/null
+++ b/app/jobs/mailbluster_edit_lead_job.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class MailblusterEditLeadJob < ApplicationJob
+ queue_as :default
+
+ def perform(user_id, add_tags: [], remove_tags: [], old_email: nil)
+ user = User.find(user_id)
+ MailblusterManager.edit_lead(user, add_tags: add_tags, remove_tags: remove_tags, old_email: old_email)
+ end
+end
diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb
new file mode 100644
index 000000000..5a1e0bc83
--- /dev/null
+++ b/app/mailers/application_mailer.rb
@@ -0,0 +1,3 @@
+class ApplicationMailer < ActionMailer::Base
+ default from: ENV.fetch('OSEM_EMAIL_ADDRESS', 'no-reply@osem')
+end
diff --git a/app/mailers/mailbot.rb b/app/mailers/mailbot.rb
index 52e15b800..32dfb7c2a 100644
--- a/app/mailers/mailbot.rb
+++ b/app/mailers/mailbot.rb
@@ -1,129 +1,163 @@
# frozen_string_literal: true
-class Mailbot < ActionMailer::Base
+YTLF_TICKET_ID = Rails.configuration.mailbot[:ytlf_ticket_id]
+
+class Mailbot < ApplicationMailer
+ helper ApplicationHelper
+ helper ConferenceHelper
+
+ default bcc: Rails.configuration.mailbot[:bcc_address],
+ template_name: 'email_template',
+ content_type: 'text/html',
+ to: -> { @user.email },
+ from: -> { @conference.contact.email }
+
def registration_mail(conference, user)
- mail(to: user.email,
- from: conference.contact.email,
- subject: conference.email_settings.registration_subject,
- body: conference.email_settings.generate_email_on_conf_updates(conference,
- user,
- conference.email_settings.registration_body))
+ @user = user
+ @conference = conference
+ @email_body = @conference.email_settings.generate_email_on_conf_updates(@conference, @user,
+ @conference.email_settings.registration_body)
+
+ mail(subject: @conference.email_settings.registration_subject)
end
def ticket_confirmation_mail(ticket_purchase)
@ticket_purchase = ticket_purchase
- @conference = ticket_purchase.conference
@user = ticket_purchase.user
+ @conference = ticket_purchase.conference
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}")
+ 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!")
+ if @ticket_purchase.ticket_id == YTLF_TICKET_ID
+ template_name = 'young_thinkers_ticket_confirmation_template'
+ mail(subject: "#{@conference.title} | Ticket Confirmation and PDF!", template_name: template_name)
+ end
+
+ # if email subject is empty, use custom template
+ if @ticket_purchase.ticket.email_subject.empty? && !@ticket_purchase.ticket.email_body.empty?
+ @ticket_purchase.ticket.email_body = @ticket_purchase.generate_confirmation_mail(@ticket_purchase.ticket.email_body)
+ mail(subject: "#{@conference.title} | Ticket Confirmation and PDF!", template_name: 'custom_ticket_confirmation_template')
+ # if email body is empty, use default template with subject
+ elsif !@ticket_purchase.ticket.email_subject.empty? && @ticket_purchase.ticket.email_body.empty?
+ @ticket_purchase.ticket.email_subject = @ticket_purchase.generate_confirmation_mail(@ticket_purchase.ticket.email_subject)
+ mail(subject: @ticket_purchase.ticket.email_subject, template_name: 'ticket_confirmation_template')
+ # if both exist, use custom
+ elsif !@ticket_purchase.ticket.email_subject.empty? && !@ticket_purchase.ticket.email_body.empty?
+ @ticket_purchase.ticket.email_body = @ticket_purchase.generate_confirmation_mail(@ticket_purchase.ticket.email_body)
+ @ticket_purchase.ticket.email_subject = @ticket_purchase.generate_confirmation_mail(@ticket_purchase.ticket.email_subject)
+ mail(subject: @ticket_purchase.ticket.email_subject, template_name: 'custom_ticket_confirmation_template')
+ # if both empty, use default
+ else
+ mail(subject: "#{@conference.title} | Ticket Confirmation and PDF!", template_name: 'ticket_confirmation_template')
+ end
end
def acceptance_mail(event)
- conference = event.program.conference
+ @user = event.submitter
+ @conference = event.program.conference
+ @speakers = event.speakers.map(&:email)
+ @email_body = @conference.email_settings.generate_event_mail(event, @conference.email_settings.accepted_body)
- mail(to: event.submitter.email,
- from: conference.contact.email,
- subject: conference.email_settings.accepted_subject,
- body: conference.email_settings.generate_event_mail(event, conference.email_settings.accepted_body))
+ mail(subject: @conference.email_settings.accepted_subject, cc: @speakers)
end
def submitted_proposal_mail(event)
- conference = event.program.conference
+ @user = event.submitter
+ @speakers = event.speakers.map(&:email)
+ @conference = event.program.conference
+ @email_body = @conference.email_settings.generate_event_mail(event,
+ @conference.email_settings.submitted_proposal_body)
- mail(to: event.submitter.email,
- from: conference.contact.email,
- subject: conference.email_settings.submitted_proposal_subject,
- body: conference.email_settings.generate_event_mail(event, conference.email_settings.submitted_proposal_body))
+ mail(subject: @conference.email_settings.submitted_proposal_subject, cc: @speakers)
end
def rejection_mail(event)
- conference = event.program.conference
+ @user = event.submitter
+ @speakers = event.speakers.map(&:email)
+ @conference = event.program.conference
+ @email_body = @conference.email_settings.generate_event_mail(event, @conference.email_settings.rejected_body)
- mail(to: event.submitter.email,
- from: conference.contact.email,
- subject: conference.email_settings.rejected_subject,
- body: conference.email_settings.generate_event_mail(event, conference.email_settings.rejected_body))
+ mail(subject: @conference.email_settings.rejected_subject, cc: @speakers)
end
- def confirm_reminder_mail(event)
- conference = event.program.conference
+ def confirm_reminder_mail(event, user: nil)
+ @user = user || event.submitter
+ @conference = event.program.conference
+ @email_body = @conference.email_settings.generate_event_mail(event,
+ @conference.email_settings.confirmed_without_registration_body)
- mail(to: event.submitter.email,
- from: conference.contact.email,
- subject: conference.email_settings.confirmed_without_registration_subject,
- body: conference.email_settings.generate_event_mail(event,
- conference.email_settings.confirmed_without_registration_body))
+ mail(subject: @conference.email_settings.confirmed_without_registration_subject)
end
def conference_date_update_mail(conference, user)
- mail(to: user.email,
- from: conference.contact.email,
- subject: conference.email_settings.conference_dates_updated_subject,
- body: conference.email_settings.generate_email_on_conf_updates(conference,
- user,
- conference.email_settings.conference_dates_updated_body))
+ @user = user
+ @conference = conference
+ @email_body = @conference.email_settings.generate_email_on_conf_updates(@conference, @user,
+ @conference.email_settings.conference_dates_updated_body)
+
+ mail(subject: @conference.email_settings.conference_dates_updated_subject)
end
def conference_registration_date_update_mail(conference, user)
- mail(to: user.email,
- from: conference.contact.email,
- subject: conference.email_settings.conference_registration_dates_updated_subject,
- body: conference.email_settings.generate_email_on_conf_updates(conference,
- user,
- conference.email_settings.conference_registration_dates_updated_body))
+ @user = user
+ @conference = conference
+ @email_body = @conference.email_settings.generate_email_on_conf_updates(@conference, @user,
+ @conference.email_settings.conference_registration_dates_updated_body)
+
+ mail(subject: @conference.email_settings.conference_registration_dates_updated_subject)
end
def conference_venue_update_mail(conference, user)
- mail(to: user.email,
- from: conference.contact.email,
- subject: conference.email_settings.venue_updated_subject,
- body: conference.email_settings.generate_email_on_conf_updates(conference,
- user,
- conference.email_settings.venue_updated_body))
+ @user = user
+ @conference = conference
+ @email_body = @conference.email_settings.generate_email_on_conf_updates(@conference, @user,
+ @conference.email_settings.venue_updated_body)
+
+ mail(subject: @conference.email_settings.venue_updated_subject)
end
def conference_schedule_update_mail(conference, user)
- mail(to: user.email,
- from: conference.contact.email,
- subject: conference.email_settings.program_schedule_public_subject,
- body: conference.email_settings.generate_email_on_conf_updates(conference,
- user,
- conference.email_settings.program_schedule_public_body))
+ @user = user
+ @conference = conference
+ @email_body = @conference.email_settings.generate_email_on_conf_updates(@conference, @user,
+ @conference.email_settings.program_schedule_public_body)
+
+ mail(bcc: nil,
+ subject: @conference.email_settings.program_schedule_public_subject)
end
def conference_cfp_update_mail(conference, user)
- mail(to: user.email,
- from: conference.contact.email,
- subject: conference.email_settings.cfp_dates_updated_subject,
- body: conference.email_settings.generate_email_on_conf_updates(conference,
- user,
- conference.email_settings.cfp_dates_updated_body))
+ @user = user
+ @conference = conference
+ @email_body = @conference.email_settings.generate_email_on_conf_updates(@conference, @user,
+ @conference.email_settings.cfp_dates_updated_body)
+
+ mail(bcc: nil,
+ subject: @conference.email_settings.cfp_dates_updated_subject)
end
def conference_booths_acceptance_mail(booth)
- conference = booth.conference
+ @user = booth.submitter
+ @conference = booth.conference
+ @email_body = @conference.email_settings.generate_booth_mail(booth,
+ @conference.email_settings.booths_acceptance_body)
- 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))
+ mail(bcc: nil,
+ subject: @conference.email_settings.booths_acceptance_subject)
end
def conference_booths_rejection_mail(booth)
- conference = booth.conference
+ @user = booth.submitter
+ @conference = booth.conference
+ @email_body = @conference.email_settings.generate_booth_mail(booth,
+ @conference.email_settings.booths_rejection_body)
- 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))
+ mail(bcc: nil,
+ subject: @conference.email_settings.booths_rejection_subject)
end
def event_comment_mail(comment, user)
@@ -132,8 +166,7 @@ def event_comment_mail(comment, user)
@conference = @event.program.conference
@user = user
- mail(to: @user.email,
- from: @conference.contact.email,
+ mail(bcc: nil,
template_name: 'comment_template',
subject: "New comment has been posted for #{@event.title}")
end
diff --git a/app/models/.lodging.rb.swp b/app/models/.lodging.rb.swp
deleted file mode 100644
index 70c3567d0..000000000
Binary files a/app/models/.lodging.rb.swp and /dev/null differ
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 77746620d..0211ee8bc 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -17,7 +17,7 @@ def initialize(user)
# Abilities for not signed in users (guests)
def not_signed_in
- can [:index, :conferences, :code_of_conduct], Organization
+ can %i[index conferences code_of_conduct], Organization
can [:index], Conference
can [:show], Conference do |conference|
conference.splashpage&.public == true
@@ -31,7 +31,7 @@ def not_signed_in
event.state == 'confirmed'
end
- can [:show, :events, :app], Schedule do |schedule|
+ can [:show, :events, :happening_now, :app, :vertical_schedule], Schedule do |schedule|
schedule.program.schedule_public
end
@@ -39,7 +39,7 @@ def not_signed_in
can :show, Commercial, commercialable: Event.where(state: 'confirmed')
can [:show, :create], User
- can [:index, :show], Survey, surveyable_type: 'Conference'
+ can %i[index show], Survey, surveyable_type: 'Conference'
# Things that are possible without ichain enabled that are **not*+ possible with ichain mode enabled.
if ENV.fetch('OSEM_ICHAIN_ENABLED', nil) != 'true'
@@ -68,6 +68,7 @@ def not_signed_in
end
# Abilities for signed in users
+ # TODO: Refactor into multiple functions
def signed_in(user)
# Abilities from not_signed_in user are also inherited
not_signed_in
@@ -87,10 +88,12 @@ def signed_in(user)
end
can :index, Organization
- can :index, Ticket
+ can :index, Ticket do |ticket|
+ ticket.visible
+ end
can :manage, TicketPurchase, user_id: user.id
- can [:new, :create], Payment, user_id: user.id
- can [:index, :show], PhysicalTicket, user: user
+ can %i[new create], Payment, user_id: user.id
+ can %i[index show], PhysicalTicket, user: user
can [:new, :create], Booth do |booth|
booth.new_record? && booth.conference.program.cfps.for_booths.try(:open?)
@@ -100,7 +103,7 @@ def signed_in(user)
booth.users.include?(user)
end
- can [:create, :destroy], Subscription, user_id: user.id
+ can %i[create destroy], Subscription, user_id: user.id
can [:new, :create], Event do |event|
event.program.cfp_open? && event.new_record?
@@ -110,12 +113,17 @@ def signed_in(user)
event.users.include?(user)
end
+ can :toggle_favorite, Event do |event|
+ event.scheduled?
+ end
+
# can manage the commercials of their own events
can :manage, Commercial, commercialable_type: 'Event', commercialable_id: user.events.pluck(:id)
# can view and reply to a survey
- can [:index, :show, :reply], Survey, surveyable_type: 'Conference'
- can [:index, :show, :reply], Survey, surveyable_type: 'Registration', surveyable_id: user.registrations.pluck(:conference_id)
+ can %i[index show reply], Survey, surveyable_type: 'Conference'
+ can %i[index show reply], Survey, surveyable_type: 'Registration',
+ surveyable_id: user.registrations.pluck(:conference_id)
# TODO: this needs to check for more, eg.
# if survey target is after_conference, check whether or not the conference is over
@@ -130,7 +138,7 @@ def signed_in(user)
track.new_record? && track.program.cfps.for_tracks.try(:open?)
end
- can [:index, :show, :restart, :confirm, :withdraw], Track, submitter_id: user.id
+ can %i[index show restart confirm withdraw], Track, submitter_id: user.id
can [:edit, :update], Track do |track|
user == track.submitter && !(track.accepted? || track.confirmed?)
diff --git a/app/models/admin_ability.rb b/app/models/admin_ability.rb
index 6100455c0..07e72ec20 100644
--- a/app/models/admin_ability.rb
+++ b/app/models/admin_ability.rb
@@ -27,15 +27,15 @@ def common_abilities_for_roles(user)
can [:new, :create], Registration do |registration|
conference = registration.conference
- conference.registration_open? && !conference.registration_limit_exceeded? || conference.program.speakers.confirmed.include?(user)
+ (conference.registration_open? && !conference.registration_limit_exceeded?) || conference.program.speakers.confirmed.include?(user)
end
- can [:index, :admins], Organization
+ can %i[index admins], Organization
can :index, Ticket
can :manage, TicketPurchase, user_id: user.id
- can [:new, :create], Payment, user_id: user.id
+ can %i[new create], Payment, user_id: user.id
- can [:create, :destroy], Subscription, user_id: user.id
+ can %i[create destroy], Subscription, user_id: user.id
can [:new, :create], Event do |event|
event.program.cfp_open? && event.new_record?
@@ -47,13 +47,13 @@ def common_abilities_for_roles(user)
can [:destroy], Openid
can :access, Admin
can :index, Commercial, commercialable_type: 'Conference'
- cannot [:edit, :update, :destroy], Question, global: true
+ cannot %i[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.fetch('OSEM_ICHAIN_ENABLED', nil) == 'true'
+ cannot %i[new create], User if ENV.fetch('OSEM_ICHAIN_ENABLED', nil) == 'true'
cannot :revert_object, PaperTrail::Version do |version|
- (version.event == 'create' && %w[Conference User Event].include?(version.item_type))
+ 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?
@@ -94,10 +94,14 @@ 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 %i[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
+ can %i[index show], Role
+
+ can %i[index revert_object revert_attribute], PaperTrail::Version,
+ organization_id: org_ids_for_organization_admin
signed_in_with_organizer_role(user, conf_ids_for_organization_admin)
end
@@ -110,7 +114,7 @@ def signed_in_with_organizer_role(user, conf_ids_for_organization_admin = [])
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 %i[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
@@ -160,12 +164,12 @@ def signed_in_with_organizer_role(user, conf_ids_for_organization_admin = [])
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)
+ (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
+ can %i[index revert_object revert_attribute], PaperTrail::Version, item_type: 'User'
+ can %i[index revert_object revert_attribute], PaperTrail::Version, conference_id: conf_ids
end
def signed_in_with_cfp_role(user)
@@ -175,7 +179,7 @@ def signed_in_with_cfp_role(user)
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 %i[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 }
@@ -185,7 +189,8 @@ def signed_in_with_cfp_role(user)
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 :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',
@@ -204,7 +209,7 @@ def signed_in_with_cfp_role(user)
(Conference.with_role(:cfp, user).pluck(:id).include? role.resource_id)
end
- can [:index, :revert_object, :revert_attribute], PaperTrail::Version,
+ can %i[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|
@@ -220,7 +225,7 @@ def signed_in_with_info_desk_role(user)
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 %i[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|
@@ -241,7 +246,8 @@ def signed_in_with_info_desk_role(user)
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
+ can %i[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)
@@ -251,7 +257,7 @@ def signed_in_with_volunteers_coordinator_role(user)
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 %i[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
diff --git a/app/models/answer.rb b/app/models/answer.rb
index abada979b..31c32bf19 100644
--- a/app/models/answer.rb
+++ b/app/models/answer.rb
@@ -1,5 +1,14 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: answers
+#
+# id :bigint not null, primary key
+# title :string
+# created_at :datetime
+# updated_at :datetime
+#
class Answer < ApplicationRecord
has_many :qanswers
has_many :questions, through: :qanswers
diff --git a/app/models/booth.rb b/app/models/booth.rb
index 85c6c7a84..b2cd0ff3b 100644
--- a/app/models/booth.rb
+++ b/app/models/booth.rb
@@ -1,5 +1,21 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: booths
+#
+# id :bigint not null, primary key
+# description :text
+# logo_link :string
+# reasoning :text
+# state :string
+# submitter_relationship :text
+# title :string
+# website_url :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# conference_id :integer
+#
class Booth < ApplicationRecord
include ActiveRecord::Transitions
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
@@ -43,28 +59,28 @@ class Booth < ApplicationRecord
state :confirmed
event :restart do
- transitions to: :new, from: [:withdrawn, :rejected, :canceled]
+ transitions to: :new, from: %i[withdrawn rejected canceled]
end
event :withdraw do
- transitions to: :withdrawn, from: [:new, :to_accept, :accepted, :to_reject, :rejected, :confirmed]
+ transitions to: :withdrawn, from: %i[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]
+ transitions to: :to_accept, from: %i[new to_reject]
end
event :to_reject do
- transitions to: :to_reject, from: [:new, :to_accept]
+ transitions to: :to_reject, from: %i[new to_accept]
end
event :accept do
- transitions to: :accepted, from: [:new, :to_accept]
+ transitions to: :accepted, from: %i[new to_accept]
end
event :reject do
- transitions to: :rejected, from: [:new, :to_reject]
+ transitions to: :rejected, from: %i[new to_reject]
end
event :cancel do
- transitions to: :canceled, from: [:accepted, :rejected, :to_accept, :to_reject, :confirmed]
+ transitions to: :canceled, from: %i[accepted rejected to_accept to_reject confirmed]
end
end
diff --git a/app/models/booth_request.rb b/app/models/booth_request.rb
index 5cce46a17..b7ce7b954 100644
--- a/app/models/booth_request.rb
+++ b/app/models/booth_request.rb
@@ -1,5 +1,21 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: booth_requests
+#
+# id :bigint not null, primary key
+# role :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# booth_id :integer
+# user_id :integer
+#
+# Indexes
+#
+# index_booth_requests_on_booth_id (booth_id)
+# index_booth_requests_on_user_id (user_id)
+#
class BoothRequest < ApplicationRecord
belongs_to :booth
belongs_to :user
diff --git a/app/models/cfp.rb b/app/models/cfp.rb
index 6571e7011..87cb70cd1 100644
--- a/app/models/cfp.rb
+++ b/app/models/cfp.rb
@@ -1,9 +1,22 @@
# frozen_string_literal: true
-# cannot delete program if there are events submitted
+# == Schema Information
+#
+# Table name: cfps
+#
+# id :bigint not null, primary key
+# cfp_type :string
+# description :text
+# enable_registrations :boolean default(FALSE)
+# end_date :date not null
+# start_date :date not null
+# created_at :datetime
+# updated_at :datetime
+# program_id :integer
+#
class Cfp < ApplicationRecord
- TYPES = %w(events booths tracks).freeze
+ TYPES = %w[events booths tracks].freeze
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
belongs_to :program
@@ -29,11 +42,11 @@ class Cfp < ApplicationRecord
# * +True+ -> If cfp dates is updated and all other parameters are set
# * +False+ -> Either cfp date is not updated or one or more parameter is not set
def notify_on_cfp_date_update?
- !end_date.blank? && !start_date.blank?\
- && (start_date_changed? || end_date_changed?)\
- && program.conference.email_settings.send_on_cfp_dates_updated\
- && !program.conference.email_settings.cfp_dates_updated_subject.blank?\
- && !program.conference.email_settings.cfp_dates_updated_body.blank?
+ end_date.present? && start_date.present? \
+ && (start_date_changed? || end_date_changed?) \
+ && program.conference.email_settings.send_on_cfp_dates_updated \
+ && program.conference.email_settings.cfp_dates_updated_subject.present? \
+ && program.conference.email_settings.cfp_dates_updated_body.present?
end
##
@@ -124,8 +137,10 @@ def before_end_of_conference
end
def start_after_end_date
- errors
- .add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date
+ if start_date && end_date && start_date > end_date
+ errors
+ .add(:start_date, "can't be after the end date")
+ end
end
def conference_id
diff --git a/app/models/comment.rb b/app/models/comment.rb
index ee1a07a8e..976c54d89 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -1,21 +1,44 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: comments
+#
+# id :bigint not null, primary key
+# body :text
+# commentable_type :string
+# lft :integer
+# rgt :integer
+# subject :string
+# title :string(50) default("")
+# created_at :datetime
+# updated_at :datetime
+# commentable_id :integer
+# parent_id :integer
+# user_id :integer
+#
+# Indexes
+#
+# index_comments_on_commentable_id (commentable_id)
+# index_comments_on_commentable_type (commentable_type)
+# index_comments_on_user_id (user_id)
+#
class Comment < ApplicationRecord
- acts_as_nested_set scope: %i(commentable_id commentable_type)
+ acts_as_nested_set scope: %i[commentable_id commentable_type]
validates :body, presence: true
validates :user, presence: true
after_create :send_notification
# NOTE: install the acts_as_votable plugin if you
# want user to vote on the quality of comments.
- #acts_as_votable
+ # acts_as_votable
belongs_to :commentable, counter_cache: true, polymorphic: true
# NOTE: Comments belong to a user
belongs_to :user
- has_paper_trail on: %i(create destroy), meta: { conference_id: :conference_id }
+ has_paper_trail on: %i[create destroy], meta: { conference_id: :conference_id }
# Helper class method that allows you to build a comment
# by passing a commentable object, a user_id, and comment text
@@ -27,7 +50,7 @@ def self.build_from(obj, user_id, comment)
user_id: user_id
end
- #helper method to check if a comment has children
+ # helper method to check if a comment has children
def has_children?
children.any?
end
diff --git a/app/models/commercial.rb b/app/models/commercial.rb
index 7bb2a0812..d5ac9a463 100644
--- a/app/models/commercial.rb
+++ b/app/models/commercial.rb
@@ -1,14 +1,28 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: commercials
+#
+# id :bigint not null, primary key
+# commercial_type :string
+# commercialable_type :string
+# title :string
+# url :string
+# created_at :datetime
+# updated_at :datetime
+# commercial_id :string
+# commercialable_id :integer
+#
class Commercial < ApplicationRecord
require 'oembed'
- belongs_to :commercialable, polymorphic: true
+ belongs_to :commercialable, polymorphic: true, touch: true
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
validates :url, presence: true, uniqueness: { scope: :commercialable }
- validates :url, format: URI::regexp(%w(http https))
+ validates :url, format: URI::DEFAULT_PARSER.make_regexp(%w[https])
validate :valid_url
@@ -17,11 +31,16 @@ def self.render_from_url(url)
begin
resource = OEmbed::Providers.get(url, maxwidth: 560, maxheight: 315)
{ html: resource.html.html_safe }
- rescue StandardError => exception
- { error: exception.message }
+ rescue StandardError
+ { html: iframe_fallback(url) }
+ # { error: exception.message }
end
end
+ def self.iframe_fallback(url)
+ "".html_safe
+ end
+
def self.read_file(file)
errors = {}
errors[:no_event] = []
@@ -35,11 +54,11 @@ def self.read_file(file)
event = Event.find_by(id: id)
# Go to next event, if the event is not found
- errors[:no_event] << id && next unless event
+ (errors[:no_event] << id) && next unless event
commercial = event.commercials.new(url: url)
unless commercial.save
- errors[:validation_errors] << "Could not create commercial for event with ID #{event.id} (" + commercial.errors.full_messages.to_sentence + ')'
+ errors[:validation_errors] << ("Could not create materials for event with ID #{event.id} (" + commercial.errors.full_messages.to_sentence + ')')
end
end
errors
@@ -49,9 +68,7 @@ def self.read_file(file)
def valid_url
result = Commercial.render_from_url(url)
- if result[:error]
- errors.add(:base, result[:error])
- end
+ errors.add(:base, result[:error]) if result[:error]
end
def self.register_provider
@@ -60,13 +77,17 @@ def self.register_provider
speakerdeck << 'http://speakerdeck.com/*'
OEmbed::Providers.register(
- OEmbed::Providers::Youtube,
- OEmbed::Providers::Vimeo,
- OEmbed::Providers::Slideshare,
- OEmbed::Providers::Flickr,
- OEmbed::Providers::Instagram,
- speakerdeck
+ OEmbed::Providers::Youtube,
+ OEmbed::Providers::Vimeo,
+ OEmbed::Providers::Slideshare,
+ OEmbed::Providers::Flickr,
+ OEmbed::Providers::Instagram,
+ speakerdeck
)
+ # OEmbed::Providers.register_fallback(
+ # OEmbed::ProviderDiscovery,
+ # OEmbed::Providers::Noembed
+ # )
end
def conference_id
diff --git a/app/models/concerns/track_saved_changes.rb b/app/models/concerns/track_saved_changes.rb
new file mode 100644
index 000000000..db75e6257
--- /dev/null
+++ b/app/models/concerns/track_saved_changes.rb
@@ -0,0 +1,48 @@
+# https://github.com/ccmcbeck/after-commit
+module TrackSavedChanges
+ extend ActiveSupport::Concern
+
+ included do
+ # expose the details if consumer wants to do more
+ # attr_reader :ts_saved_changes_history, :ts_saved_changes_unfiltered
+ after_initialize :ts_reset_saved_changes
+ after_save :ts_track_saved_changes
+ end
+
+ # on initalize, but useful for fine grain control
+ def ts_reset_saved_changes
+ @ts_saved_changes_unfiltered = {}
+ @ts_saved_changes_history = []
+ end
+
+ # filter out any changes that result in the original value
+ def ts_saved_changes
+ @ts_saved_changes_unfiltered.reject { |_k, v| v[0] == v[1] }
+ end
+
+ private
+
+ # on save
+ def ts_track_saved_changes
+ # maintain an array of ActiveModel::Dirty.changes
+ @ts_saved_changes_history << previous_changes.dup
+ # accumulate the most recent changes
+ @ts_saved_changes_history.last.each_pair { |k, v| ts_track_saved_change k, v }
+ end
+
+ # v is an an array of [prev, current]
+ def ts_track_saved_change(key, value)
+ if @ts_saved_changes_unfiltered.key? key
+ @ts_saved_changes_unfiltered[key][1] = ts_track_saved_value value[1]
+ else
+ @ts_saved_changes_unfiltered[key] = value.dup
+ end
+ end
+
+ # type safe dup inspred by http://stackoverflow.com/a/20955038
+ def ts_track_saved_value(value)
+ value.dup
+ rescue TypeError
+ value
+ end
+end
diff --git a/app/models/conference.rb b/app/models/conference.rb
index cac3a8493..dcf525754 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -1,5 +1,41 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: conferences
+#
+# id :bigint not null, primary key
+# booth_limit :integer default(0)
+# color :string
+# custom_css :text
+# custom_domain :string
+# description :text
+# end_date :date not null
+# end_hour :integer default(20)
+# events_per_week :text
+# guid :string not null
+# logo_file_name :string
+# picture :string
+# registration_limit :integer default(0)
+# revision :integer default(0), not null
+# short_title :string not null
+# start_date :date not null
+# start_hour :integer default(9)
+# ticket_layout :integer default("portrait")
+# timezone :string not null
+# title :string not null
+# use_vdays :boolean default(FALSE)
+# use_volunteers :boolean
+# use_vpositions :boolean default(FALSE)
+# created_at :datetime
+# updated_at :datetime
+# organization_id :integer
+#
+# Indexes
+#
+# index_conferences_on_organization_id (organization_id)
+#
+# rubocop:disable Metrics/ClassLength
class Conference < ApplicationRecord
include RevisionCount
require 'uri'
@@ -9,13 +45,13 @@ class Conference < ApplicationRecord
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) })
+ scope :upcoming, -> { where('end_date >= ?', Date.current) }
+ scope :past, -> { where('end_date < ?', Date.current) }
belongs_to :organization
delegate :code_of_conduct, to: :organization
- has_paper_trail ignore: %i(updated_at guid revision events_per_week), meta: { conference_id: :id }
+ has_paper_trail ignore: %i[updated_at guid revision events_per_week], meta: { conference_id: :id }
has_and_belongs_to_many :questions
@@ -25,7 +61,8 @@ class Conference < ApplicationRecord
has_one :email_settings, dependent: :destroy
has_one :program, dependent: :destroy
has_one :venue, dependent: :destroy
- delegate :city, :country_name, to: :venue, allow_nil: true
+
+ delegate :city, :country_name, :rooms, to: :venue, allow_nil: true
delegate :name, :street, to: :venue, prefix: true, allow_nil: true
has_many :ticket_purchases, dependent: :destroy
@@ -33,6 +70,7 @@ class Conference < ApplicationRecord
has_many :payments, dependent: :destroy
has_many :supporters, through: :ticket_purchases, source: :user
has_many :tickets, dependent: :destroy
+ has_many :registration_tickets, -> { for_registration }, class_name: 'Ticket'
has_many :resources, dependent: :destroy
has_many :booths, dependent: :destroy
has_many :confirmed_booths, -> { where(state: 'confirmed') }, class_name: 'Booth'
@@ -55,6 +93,7 @@ class Conference < ApplicationRecord
through: :program,
source: :events
has_many :event_types, through: :program
+ has_many :currency_conversions
has_many :surveys, as: :surveyable, dependent: :destroy do
def for_registration
@@ -100,7 +139,7 @@ def after_conference
after_create :create_free_ticket
after_update :delete_event_schedules
- enum ticket_layout: [:portrait, :landscape]
+ enum ticket_layout: { portrait: 0, landscape: 1 }
##
# Checks if the user is registered to the conference
@@ -110,8 +149,35 @@ def after_conference
# ====Returns
# * +false+ -> If the user is registered
# * +true+ - If the user isn't registered
- def user_registered? user
- user.present? && registrations.where(user_id: user.id).count > 0
+ def user_registered?(user)
+ user.present? && registrations.where(user_id: user.id).present?
+ end
+
+ ##
+ # True when there is at least one ticket marked as "registration"
+ # A user must get a registration ticket before registering.
+ def registration_ticket_required?
+ registration_tickets.any?
+ end
+
+ ##
+ # Creates a registration to the conference for the user
+ #
+ # ====Args
+ # * +user+ -> The user we check for
+ # ====Returns
+ # * +false+ -> If the user is registered
+ # * +true+ - If the user isn't registered
+ def register_user(user)
+ registration = registrations.new
+ registration.user = user
+ if registration.save
+ MailblusterEditLeadJob.perform_later(
+ user.id, add_tags: ["#{organization.name}-#{short_title}"]
+ )
+ return registration
+ end
+ false
end
##
@@ -121,8 +187,8 @@ def delete_event_schedules
if saved_change_to_start_hour? || saved_change_to_end_hour?
event_schedules = program.event_schedules.select do |event_schedule|
event_schedule.start_time.hour < start_hour ||
- event_schedule.end_time.hour > end_hour ||
- (event_schedule.end_time.hour == end_hour && event_schedule.end_time.minute > 0)
+ event_schedule.end_time.hour > end_hour ||
+ (event_schedule.end_time.hour == end_hour && event_schedule.end_time.min > 0)
end
event_schedules.each(&:destroy)
end
@@ -201,9 +267,9 @@ def get_submissions_data
# * +Array+ -> e.g. [0, 3, 3, 5] -> first week 0, second week 3 registrations
def get_registrations_per_week
return [] unless registrations &&
- registration_period &&
- registration_period.start_date &&
- registration_period.end_date
+ registration_period &&
+ registration_period.start_date &&
+ registration_period.end_date
reg = registrations.group(:week).order(:week).count
start_week = get_registration_start_week
@@ -269,9 +335,9 @@ def registration_weeks
result = 0
weeks = 0
if registration_period&.start_date &&
- registration_period&.end_date
+ registration_period&.end_date
weeks = Date.new(registration_period.start_date.year, 12, 31)
- .strftime('%W').to_i
+ .strftime('%W').to_i
result = get_registration_end_week - get_registration_start_week + 1
end
@@ -368,8 +434,8 @@ def self.get_top_submitter(limit = 5)
# * +hash+ -> user: submissions
def get_top_submitter(limit = 5)
submitter = EventUser.joins(:event).select(:user_id)
- .where('event_role = ? and program_id = ?', 'submitter', Conference.find(id).program.id)
- .limit(limit).group(:user_id)
+ .where('event_role = ? and program_id = ?', 'submitter', Conference.find(id).program.id)
+ .limit(limit).group(:user_id)
counter = submitter.order('count_all desc').count(:all)
Conference.calculate_user_submission_hash(submitter, counter)
end
@@ -579,12 +645,12 @@ def tracks_distribution(state = nil)
# * +ActiveRecord+
def self.get_active_conferences_for_dashboard
result = Conference.where('start_date > ?', Time.now)
- .select('id, short_title, color, start_date, organization_id')
+ .select('id, short_title, color, start_date, organization_id')
if result.empty?
result = Conference
- .select('id, short_title, color, start_date, organization_id').limit(2)
- .order(start_date: :desc)
+ .select('id, short_title, color, start_date, organization_id').limit(2)
+ .order(start_date: :desc)
end
result
end
@@ -626,9 +692,7 @@ def self.write_event_distribution_to_db
result[state.name] = count
end
- unless conference.events_per_week
- conference.events_per_week = {}
- end
+ conference.events_per_week = {} unless conference.events_per_week
# Write to database
conference.events_per_week[week] = result
@@ -648,7 +712,7 @@ def notify_on_dates_changed?
return false unless saved_change_to_start_date? || saved_change_to_end_date?
# do not notify unless the mail content is set up
- (email_settings.conference_dates_updated_subject.present? && email_settings.conference_dates_updated_body.present?)
+ email_settings.conference_dates_updated_subject.present? && email_settings.conference_dates_updated_body.present?
end
##
@@ -665,7 +729,7 @@ def notify_on_registration_dates_changed?
return false unless registration_period.saved_change_to_start_date? || registration_period.saved_change_to_end_date?
# do not notify unless the mail content is set up
- (email_settings.conference_registration_dates_updated_subject.present? && email_settings.conference_registration_dates_updated_body.present?)
+ email_settings.conference_registration_dates_updated_subject.present? && email_settings.conference_registration_dates_updated_body.present?
end
##
@@ -752,8 +816,8 @@ def next_color(i)
# the color. We make use of big prime numbers to avoid repetition and to make
# consecutive colors clearly different.
def next_color_component(component, i)
- big_prime_numbers = {r: 113, g: 67, b: 151}
- ((i * big_prime_numbers[component]) % 239 + 16).to_s(16)
+ big_prime_numbers = { r: 113, g: 67, b: 151 }
+ (((i * big_prime_numbers[component]) % 239) + 16).to_s(16)
end
after_create do
@@ -767,7 +831,8 @@ def next_color_component(component, i)
# after the conference has been successfully created
# Will create 1 new record for 'free' ticket
def create_free_ticket
- tickets.where(title: 'Free Access', price_cents: 0).first_or_create!(description: 'Get free access tickets for the conference.')
+ tickets.where(title: 'Free Access',
+ price_cents: 0).first_or_create!(description: 'Get free access tickets for the conference.')
end
##
@@ -775,10 +840,12 @@ def create_free_ticket
# after the conference has been successfully created
# Will create 4 new records for roles
def create_roles
- Role.where(name: 'organizer', resource: self).first_or_create(description: 'For the organizers of the conference (who shall have full access)')
+ Role.where(name: 'organizer',
+ resource: self).first_or_create(description: 'For the organizers of the conference (who shall have full access)')
Role.where(name: 'cfp', resource: self).first_or_create(description: 'For the members of the CfP team')
Role.where(name: 'info_desk', resource: self).first_or_create(description: 'For the members of the Info Desk team')
- Role.where(name: 'volunteers_coordinator', resource: self).first_or_create(description: 'For the people in charge of volunteers')
+ Role.where(name: 'volunteers_coordinator',
+ resource: self).first_or_create(description: 'For the people in charge of volunteers')
end
##
@@ -827,13 +894,12 @@ def get_events_per_week_by_state
# Completed weeks
events_per_week.each do |week, values|
+ week = Date.parse(week) unless week.respond_to?(:strftime)
values.each do |state, value|
- if %i(confirmed unconfirmed).include?(state)
- unless result[state.to_s.capitalize]
- result[state.to_s.capitalize] = {}
- end
- result[state.to_s.capitalize][week.strftime('%W').to_i] = value
- end
+ next unless %i[confirmed unconfirmed].include?(state)
+
+ result[state.to_s.capitalize] = {} unless result[state.to_s.capitalize]
+ result[state.to_s.capitalize][week.strftime('%W').to_i] = value
end
end
@@ -890,9 +956,7 @@ def cumulative_sum(array)
# * +Array+
def pad_left(first_week, start_week)
left = []
- if first_week > start_week
- left = Array.new(first_week - start_week - 1, 0)
- end
+ left = Array.new(first_week - start_week - 1, 0) if first_week > start_week
left
end
@@ -905,11 +969,9 @@ def pad_left(first_week, start_week)
def assert_keys_are_continuously(hash)
keys = hash.keys
(keys.min..keys.max).each do |key|
- unless hash[key]
- hash[key] = 0
- end
+ hash[key] = 0 unless hash[key]
end
- Hash[hash.sort]
+ hash.sort.to_h
end
##
@@ -1015,12 +1077,12 @@ def calculate_distribution_hash(grouped, counter, symbol)
grouped.each do |event|
object = event.send(symbol)
- if object
- result[object.title] = {
- 'value' => counter[object.id],
- 'color' => object.color
- }
- end
+ next unless object
+
+ result[object.title] = {
+ 'value' => counter[object.id],
+ 'color' => object.color
+ }
end
result
end
@@ -1034,12 +1096,12 @@ def calculate_distribution_hash(grouped, counter, symbol)
def calculate_track_distribution_hash(tracks_grouped, tracks_counter)
result = {}
tracks_grouped.each do |event|
- if event.track
- result[event.track.name] = {
- 'value' => tracks_counter[event.track_id],
- 'color' => event.track.color
- }
- end
+ next unless event.track
+
+ result[event.track.name] = {
+ 'value' => tracks_counter[event.track_id],
+ 'color' => event.track.color
+ }
end
result
end
@@ -1093,12 +1155,10 @@ def self.calculate_user_distribution_hash(active_user, unconfirmed_user, dead_us
def self.calculate_event_distribution_hash(counts)
return {} if counts.values.sum == 0
- Hash[
- Event.state_machine.states.collect do |state|
- state_name = state.name.to_s
- [state_name.capitalize, counts[state_name] || 0]
- end
- ]
+ Event.state_machine.states.collect do |state|
+ state_name = state.name.to_s
+ [state_name.capitalize, counts[state_name] || 0]
+ end.to_h
end
##
@@ -1111,9 +1171,7 @@ def self.calculate_user_submission_hash(submitters, counter)
counter.each do |key, value|
# 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
+ result[submitter.user] = value if submitter
end
result
end
@@ -1130,9 +1188,9 @@ def create_email_settings
#
def generate_guid
guid = SecureRandom.urlsafe_base64
-# begin
-# guid = SecureRandom.urlsafe_base64
-# end while User.where(:guid => guid).exists?
+ # begin
+ # guid = SecureRandom.urlsafe_base64
+ # end while User.where(:guid => guid).exists?
self.guid = guid
end
@@ -1140,19 +1198,17 @@ def generate_guid
# Adds a random color to the conference
#
def add_color
- unless color
- self.color = get_color
- end
+ self.color = get_color unless color
end
def get_color
- %w(
- #000000 #0000FF #00FF00 #FF0000 #FFFF00 #9900CC
- #CC0066 #00FFFF #FF00FF #C0C0C0 #00008B #FFD700
- #FFA500 #FF1493 #FF00FF #F0FFFF #EE82EE #D2691E
- #C0C0C0 #A52A2A #9ACD32 #9400D3 #8B008B #8B0000
- #87CEEB #808080 #800080 #008B8B #006400
- ).sample
+ %w[
+ #000000 #0000FF #00FF00 #FF0000 #FFFF00 #9900CC
+ #CC0066 #00FFFF #FF00FF #C0C0C0 #00008B #FFD700
+ #FFA500 #FF1493 #FF00FF #F0FFFF #EE82EE #D2691E
+ #C0C0C0 #A52A2A #9ACD32 #9400D3 #8B008B #8B0000
+ #87CEEB #808080 #800080 #008B8B #006400
+ ].sample
end
# Calculates items per week from a hash.
@@ -1171,9 +1227,7 @@ def calculate_items_per_week(start_week, weeks, items)
items.each do |key, value|
# Padding
- if last_key < (key.to_i - 1)
- result += Array.new(key.to_i - last_key - 1, sum)
- end
+ result += Array.new(key.to_i - last_key - 1, sum) if last_key < (key.to_i - 1)
sum += value
result.push(sum)
@@ -1181,16 +1235,13 @@ def calculate_items_per_week(start_week, weeks, items)
end
# Padding right
- if result.length < weeks
- result += Array.new(weeks - result.length, sum)
- end
+ result += Array.new(weeks - result.length, sum) if result.length < weeks
add_week_indices(result)
end
def add_week_indices(values)
- Hash[
- values.collect.with_index { |value, index| ["Wk #{index + 1}", value] }
- ]
+ values.collect.with_index { |value, index| ["Wk #{index + 1}", value] }.to_h
end
end
+# rubocop:enable Metrics/ClassLength
diff --git a/app/models/contact.rb b/app/models/contact.rb
index e69f9dbbc..4984f9d45 100644
--- a/app/models/contact.rb
+++ b/app/models/contact.rb
@@ -1,5 +1,24 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: contacts
+#
+# id :bigint not null, primary key
+# blog :string
+# email :string
+# facebook :string
+# googleplus :string
+# instagram :string
+# mastodon :string
+# social_tag :string
+# sponsor_email :string
+# twitter :string
+# youtube :string
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class Contact < ApplicationRecord
has_paper_trail on: [:update], ignore: [:updated_at], meta: { conference_id: :conference_id }
@@ -8,10 +27,12 @@ class Contact < ApplicationRecord
validates :conference, presence: true
# Conferences only have one contact
validates :facebook, :twitter, :googleplus, :instagram, :mastodon,
- format: URI::regexp(%w(http https)), allow_blank: true
+ format: URI::DEFAULT_PARSER.make_regexp(%w[http https]), allow_blank: true
def has_social_media?
- return true if facebook.present? || twitter.present? || googleplus.present? || instagram.present? || mastodon.present? || email.present?
+ if facebook.present? || twitter.present? || googleplus.present? || instagram.present? || mastodon.present? || email.present?
+ return true
+ end
false
end
diff --git a/app/models/currency_conversion.rb b/app/models/currency_conversion.rb
new file mode 100644
index 000000000..c675b5a27
--- /dev/null
+++ b/app/models/currency_conversion.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# == Schema Information
+#
+# Table name: currency_conversions
+#
+# rate :decimal
+# from_currency :string
+# to_currency :string
+# conference_id :integer
+#
+class CurrencyConversion < ApplicationRecord
+ belongs_to :conference
+ validates :rate, numericality: { greater_than: 0 }
+ validates :from_currency, uniqueness: { scope: :to_currency }, on: :create
+end
diff --git a/app/models/difficulty_level.rb b/app/models/difficulty_level.rb
index 981866932..5006f877d 100644
--- a/app/models/difficulty_level.rb
+++ b/app/models/difficulty_level.rb
@@ -1,5 +1,17 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: difficulty_levels
+#
+# id :bigint not null, primary key
+# color :string
+# description :text
+# title :string
+# created_at :datetime
+# updated_at :datetime
+# program_id :integer
+#
class DifficultyLevel < ApplicationRecord
belongs_to :program, touch: true
has_many :events, dependent: :nullify
diff --git a/app/models/email_settings.rb b/app/models/email_settings.rb
index 053a82dbb..bd2debddb 100644
--- a/app/models/email_settings.rb
+++ b/app/models/email_settings.rb
@@ -1,84 +1,72 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: email_settings
+#
+# id :bigint not null, primary key
+# accepted_body :text
+# accepted_subject :string
+# booths_acceptance_body :text
+# booths_acceptance_subject :string
+# booths_rejection_body :text
+# booths_rejection_subject :string
+# cfp_dates_updated_body :text
+# cfp_dates_updated_subject :string
+# conference_dates_updated_body :text
+# conference_dates_updated_subject :string
+# conference_registration_dates_updated_body :text
+# conference_registration_dates_updated_subject :string
+# confirmed_without_registration_body :text
+# confirmed_without_registration_subject :string
+# program_schedule_public_body :text
+# program_schedule_public_subject :string
+# registration_body :text
+# registration_subject :string
+# rejected_body :text
+# rejected_subject :string
+# send_on_accepted :boolean default(FALSE)
+# send_on_booths_acceptance :boolean default(FALSE)
+# send_on_booths_rejection :boolean default(FALSE)
+# send_on_cfp_dates_updated :boolean default(FALSE)
+# send_on_conference_dates_updated :boolean default(FALSE)
+# send_on_conference_registration_dates_updated :boolean default(FALSE)
+# send_on_confirmed_without_registration :boolean default(FALSE)
+# send_on_program_schedule_public :boolean default(FALSE)
+# send_on_registration :boolean default(FALSE)
+# send_on_rejected :boolean default(FALSE)
+# send_on_submitted_proposal :boolean default(FALSE)
+# send_on_venue_updated :boolean default(FALSE)
+# submitted_proposal_body :text
+# submitted_proposal_subject :string
+# venue_updated_body :text
+# venue_updated_subject :string
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class EmailSettings < ApplicationRecord
belongs_to :conference
has_paper_trail on: [:update], ignore: [:updated_at], meta: { conference_id: :conference_id }
def get_values(conference, user, event = nil, booth = nil)
- h = {
- 'email' => user.email,
- 'name' => user.name,
- 'conference' => conference.title,
- 'conference_start_date' => conference.start_date,
- 'conference_end_date' => conference.end_date,
- 'registrationlink' => Rails.application.routes.url_helpers.conference_conference_registration_url(
- conference.short_title, host: ENV.fetch('OSEM_HOSTNAME', 'localhost:3000')),
- 'conference_splash_link' => Rails.application.routes.url_helpers.conference_url(
- conference.short_title, host: ENV.fetch('OSEM_HOSTNAME', 'localhost:3000')),
-
- 'schedule_link' => Rails.application.routes.url_helpers.conference_schedule_url(
- conference.short_title, host: ENV.fetch('OSEM_HOSTNAME', 'localhost:3000'))
- }
-
- if conference.program.cfp
- h['cfp_start_date'] = conference.program.cfp.start_date
- h['cfp_end_date'] = conference.program.cfp.end_date
- else
- h['cfp_start_date'] = 'Unknown'
- h['cfp_end_date'] = 'Unknown'
- end
-
- if conference.venue
- h['venue'] = conference.venue.name
- h['venue_address'] = conference.venue.address
- else
- h['venue'] = 'Unknown'
- h['venue_address'] = 'Unknown'
- end
-
- if conference.registration_period
- h['registration_start_date'] = conference.registration_period.start_date
- h['registration_end_date'] = conference.registration_period.end_date
- end
-
- if event
- h['eventtitle'] = event.title
- h['proposalslink'] = Rails.application.routes.url_helpers.conference_program_proposals_url(
- conference.short_title, host: ENV.fetch('OSEM_HOSTNAME', 'localhost:3000'))
- end
-
- if booth
- h['booth_title'] = booth.title
- end
- h
+ parser = EmailTemplateParser.new(conference, user)
+ parser.retrieve_values(event, booth)
end
def generate_event_mail(event, event_template)
values = get_values(event.program.conference, event.submitter, event)
- parse_template(event_template, values)
+ EmailTemplateParser.parse_template(event_template, values)
end
def generate_email_on_conf_updates(conference, user, conf_update_template)
values = get_values(conference, user)
- parse_template(conf_update_template, values)
+ EmailTemplateParser.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)
- values.each do |key, value|
- if value.kind_of?(Date)
- text = text.gsub "{#{key}}", value.strftime('%Y-%m-%d') unless text.blank?
- else
- text = text.gsub "{#{key}}", value unless text.blank? || value.blank?
- end
- end
- text
+ values = get_values(conference: booth.conference, user: booth.submitter, booth: booth)
+ EmailTemplateParser.parse_template(booth_template, values)
end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index dc7202c28..ea9ab7fbf 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -1,13 +1,56 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: events
+#
+# id :bigint not null, primary key
+# abstract :text
+# comments_count :integer default(0), not null
+# committee_review :text
+# description :text
+# guid :string not null
+# is_highlight :boolean default(FALSE)
+# language :string
+# max_attendees :integer
+# presentation_mode :integer
+# progress :string default("new"), not null
+# proposal_additional_speakers :text
+# public :boolean default(TRUE)
+# require_registration :boolean
+# start_time :datetime
+# state :string default("new"), not null
+# submission_text :text
+# subtitle :string
+# superevent :boolean
+# title :string not null
+# week :integer
+# created_at :datetime
+# updated_at :datetime
+# difficulty_level_id :integer
+# event_type_id :integer
+# parent_id :integer
+# program_id :integer
+# room_id :integer
+# track_id :integer
+#
+# Foreign Keys
+#
+# fk_rails_... (parent_id => events.id)
+#
+# rubocop:disable Metrics/ClassLength
class Event < ApplicationRecord
include ActionView::Helpers::NumberHelper # for number_with_precision
+ include ActionView::Helpers::SanitizeHelper
include ActiveRecord::Transitions
include RevisionCount
- has_paper_trail on: [:create, :update], ignore: [:updated_at, :guid, :week], meta: { conference_id: :conference_id }
+ include FormatHelper
+
+ has_paper_trail on: %i[create update], ignore: %i[updated_at guid week], meta: { conference_id: :conference_id }
acts_as_commentable
+ before_create :generate_guid
after_create :set_week
has_many :event_users, dependent: :destroy
@@ -17,7 +60,10 @@ class Event < ApplicationRecord
has_many :speakers, through: :speaker_event_users, source: :user
has_one :submitter_event_user, -> { where(event_role: 'submitter') }, class_name: 'EventUser'
- has_one :submitter, through: :submitter_event_user, source: :user
+ has_one :submitter, through: :submitter_event_user, source: :user
+
+ has_many :volunteer_event_users, -> { where(event_role: 'volunteer') }, class_name: 'EventUser'
+ has_many :volunteers, through: :volunteer_event_users, source: :user
has_many :votes, dependent: :destroy
has_many :voters, through: :votes, source: :user
@@ -28,18 +74,30 @@ class Event < ApplicationRecord
has_many :events_registrations
has_many :registrations, through: :events_registrations
has_many :event_schedules, dependent: :destroy
+ has_many :subevents, class_name: 'Event', foreign_key: :parent_id
belongs_to :track
belongs_to :difficulty_level
- belongs_to :program
+ belongs_to :program, touch: true
+ belongs_to :room
+ delegate :url, to: :room, allow_nil: true
+
+ # Multiple events can be contained within a larger parent event.
+ belongs_to :parent_event, class_name: 'Event', foreign_key: :parent_id, touch: true
+
+ has_one :conference, through: :program
+ delegate :timezone, to: :conference
+
+ # Stored on the events_users table, notably not event_users
+ has_and_belongs_to_many :favourite_users, class_name: 'User'
accepts_nested_attributes_for :event_users, allow_destroy: true
accepts_nested_attributes_for :speakers, allow_destroy: true
accepts_nested_attributes_for :users
-
- before_create :generate_guid
+ accepts_nested_attributes_for :favourite_users
validate :abstract_limit
+ validate :submission_limit
validate :before_end_of_conference, on: :create
validates :title, presence: true
validates :abstract, presence: true
@@ -51,10 +109,13 @@ class Event < ApplicationRecord
validate :valid_track
scope :confirmed, -> { where(state: 'confirmed') }
+ scope :unconfirmed, -> { where(state: 'unconfirmed') }
scope :canceled, -> { where(state: 'canceled') }
scope :withdrawn, -> { where(state: 'withdrawn') }
scope :highlighted, -> { where(is_highlight: true) }
+ enum :presentation_mode, { in_person: 0, online: 1 }
+
state_machine initial: :new do
state :new
state :withdrawn
@@ -64,10 +125,10 @@ class Event < ApplicationRecord
state :rejected
event :restart do
- transitions to: :new, from: [:rejected, :withdrawn, :canceled]
+ transitions to: :new, from: %i[rejected withdrawn canceled]
end
event :withdraw do
- transitions to: :withdrawn, from: [:new, :unconfirmed, :confirmed]
+ transitions to: :withdrawn, from: %i[new unconfirmed confirmed]
end
event :accept do
transitions to: :unconfirmed, from: [:new], on_transition: :process_acceptance
@@ -76,7 +137,7 @@ class Event < ApplicationRecord
transitions to: :confirmed, from: :unconfirmed, on_transition: :process_confirmation
end
event :cancel do
- transitions to: :canceled, from: [:unconfirmed, :confirmed]
+ transitions to: :canceled, from: %i[unconfirmed confirmed]
end
event :reject do
transitions to: :rejected, from: [:new], on_transition: :process_rejection
@@ -107,6 +168,16 @@ def registration_possible?
registrations.count < max_attendees
end
+ ##
+ # Used to determine a user's personal schedule.
+ # Has the user favourited the event, or are the assigned to present (speaking or volunteering)?
+ # "submitter" is excluded as a check, primarily due to admin users.
+ # =====Returns
+ # * +boolean+ -> if the user should attend the event
+ def planned_for_user?(user)
+ speakers.include?(user) || volunteers.include?(user) || favourite_users.include?(user)
+ end
+
##
# Finds the rating of the user for the event
# ====Returns
@@ -121,7 +192,7 @@ def user_rating(user)
# ====Returns
# * +true+ -> If the event has votes (optionally, by the user)
# * +false+ -> If the event does not have any votes (optionally, by the user)
- def voted?(user=nil)
+ def voted?(user = nil)
return votes.where(user: user).any? if user
votes.any?
@@ -133,7 +204,12 @@ def average_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
+ if @total_rating > 0
+ number_with_precision(@total_rating / @total.to_f, precision: 2,
+ strip_insignificant_zeros: true)
+ else
+ 0
+ end
end
# get event speakers with the event sumbmitter at the first position
@@ -141,9 +217,7 @@ def average_rating
def speakers_ordered
speakers_list = speakers.to_a
- if speakers_list.reject! { |speaker| speaker == submitter }
- speakers_list.unshift(submitter)
- end
+ speakers_list.unshift(submitter) if speakers_list.reject! { |speaker| speaker == submitter }
speakers_list
end
@@ -154,28 +228,28 @@ def transition_possible?(transition)
def process_confirmation
if program.conference.email_settings.send_on_confirmed_without_registration? &&
- program.conference.email_settings.confirmed_without_registration_body &&
- program.conference.email_settings.confirmed_without_registration_subject
- if program.conference.registrations.where(user_id: submitter.id).first.nil?
- Mailbot.confirm_reminder_mail(self).deliver_later
- end
+ program.conference.email_settings.confirmed_without_registration_body &&
+ program.conference.email_settings.confirmed_without_registration_subject
+ users = [submitter] + speakers
+ users.reject! { |user| user.registrations.for_conference(program.conference).present? }
+ users.each { |user| Mailbot.confirm_reminder_mail(self, user: user).deliver_later }
end
end
def process_acceptance(options)
if program.conference.email_settings.send_on_accepted &&
- program.conference.email_settings.accepted_body &&
- program.conference.email_settings.accepted_subject &&
- !options[:send_mail].blank?
+ program.conference.email_settings.accepted_body &&
+ program.conference.email_settings.accepted_subject &&
+ options[:send_mail].present?
Mailbot.acceptance_mail(self).deliver_later
end
end
def process_rejection(options)
if program.conference.email_settings.send_on_rejected &&
- program.conference.email_settings.rejected_body &&
- program.conference.email_settings.rejected_subject &&
- !options[:send_mail].blank?
+ program.conference.email_settings.rejected_body &&
+ program.conference.email_settings.rejected_subject &&
+ options[:send_mail].present?
Mailbot.rejection_mail(self).deliver_later
end
end
@@ -184,34 +258,36 @@ def abstract_word_count
abstract.to_s.split.size
end
+ def submission_word_count
+ submission_text.to_s.split.size
+ end
+
def self.get_state_color(state)
COLORS[state.to_sym] || '#00FFFF' # azure
end
def update_state(transition, mail = false, subject = false, send_mail = false, send_mail_param)
alert = ''
- if mail && send_mail_param && subject && send_mail
- alert = 'Update Email Subject before Sending Mails'
- end
- begin
- if mail
- send(transition,
- send_mail: send_mail_param)
- else
- send(transition)
- end
- save
- # If the event was previously scheduled, and then withdrawn or cancelled
- # its event_schedule will have enabled set to false
- # If the event is now confirmed again, we want it to be available for scheduling
- Rails.logger.debug "transition is #{transition}"
- if transition == :confirm
- Rails.logger.debug "schedules #{EventSchedule.unscoped.where(event: self, enabled: false)}"
- EventSchedule.unscoped.where(event: self, enabled: false).destroy_all
- end
- rescue Transitions::InvalidTransition => e
- alert = "Update state failed. #{e.message}"
+ alert = 'Update Email Subject before Sending Mails' if mail && send_mail_param && subject && send_mail
+ begin
+ if mail
+ send(transition,
+ send_mail: send_mail_param)
+ else
+ send(transition)
+ end
+ save
+ # If the event was previously scheduled, and then withdrawn or cancelled
+ # its event_schedule will have enabled set to false
+ # If the event is now confirmed again, we want it to be available for scheduling
+ Rails.logger.debug { "transition is #{transition}" }
+ if transition == :confirm
+ Rails.logger.debug { "schedules #{EventSchedule.unscoped.where(event: self, enabled: false)}" }
+ EventSchedule.unscoped.where(event: self, enabled: false).destroy_all
end
+ rescue Transitions::InvalidTransition => e
+ alert = "Update state failed. #{e.message}"
+ end
alert
end
@@ -224,6 +300,10 @@ def speaker_emails
speakers.map(&:email).join(', ')
end
+ def self.display_presentation_modes
+ presentation_modes.map { |key, _| [key.humanize.titlecase, key] }
+ end
+
##
#
# Returns +Hash+
@@ -231,10 +311,10 @@ def progress_status
{
registered: speakers.all? { |speaker| program.conference.user_registered? speaker },
commercials: commercials.any?,
- biographies: speakers.all? { |speaker| !speaker.biography.blank? },
- subtitle: !subtitle.blank?,
- track: (!track.blank? unless program.tracks.empty?),
- difficulty_level: !difficulty_level.blank?,
+ biographies: speakers.all? { |speaker| speaker.biography.present? },
+ subtitle: subtitle.present?,
+ track: (track.present? unless program.tracks.empty?),
+ difficulty_level: (difficulty_level.present? unless program.difficulty_levels.empty?),
title: true,
abstract: true
}.with_indifferent_access
@@ -270,6 +350,20 @@ def time
event_schedules.find_by(schedule_id: selected_schedule_id).try(:start_time)
end
+ ##
+ # Returns the start time at which this event is scheduled
+ #
+ def happening_now?
+ event_schedules.find_by(schedule_id: selected_schedule_id).try(:happening_now?)
+ end
+
+ ##
+ # Returns the list of subevents that can be displayed in the program
+ #
+ def program_subevents
+ subevents.confirmed.sort_by { |event| event.try(:time) || event.parent_event.try(:time) }
+ end
+
##
# Returns true or false, if the event is already over or not
#
@@ -277,14 +371,17 @@ def time
# * +true+ -> If the event is over
# * +false+ -> If the event is not over yet
def ended?
- event_schedule = event_schedules.find_by(schedule_id: selected_schedule_id)
- return false unless event_schedule
+ event_schedules.find_by(schedule_id: selected_schedule_id).try(:ended?)
+ end
- event_schedule.end_time < Time.current
+ delegate :conference, to: :program
+
+ def <=>(other)
+ time <=> other.time
end
- def conference
- program.conference
+ def serializable_hash(options = {})
+ super(options).merge('rendered_abstract' => markdown(abstract))
end
private
@@ -294,19 +391,34 @@ def conference
def max_attendees_no_more_than_room_size
return unless room && max_attendees_changed?
- errors.add(:max_attendees, "cannot be more than the room's capacity (#{room.size})") if max_attendees && (max_attendees > room.size)
+ if max_attendees && (max_attendees > room.size)
+ errors.add(:max_attendees,
+ "cannot be more than the room's capacity (#{room.size})")
+ end
end
- def abstract_limit
- # If we don't have an event type, there is no need to count anything
- return unless event_type && abstract
+ def word_limit(field)
+ # If we don't have an event type or the requested field, don't count
+ return unless event_type && respond_to?(field) && self[field]
- len = abstract.split.size
+ len = self[field].split.size
+ # TODO: Use different limits for different text fields
+ # Uncomment the two lines below this when the separate word limits are implemented.
+ # max_words = event_type["maximum_#{field}_length"]
+ # min_words = event_type["minimum_#{field}_length"]
max_words = event_type.maximum_abstract_length
min_words = event_type.minimum_abstract_length
- errors.add(:abstract, "cannot have less than #{min_words} words") if len < min_words
- errors.add(:abstract, "cannot have more than #{max_words} words") if len > max_words
+ errors.add(field.to_sym, "cannot have less than #{min_words} words") if len < min_words
+ errors.add(field.to_sym, "cannot have more than #{max_words} words") if len > max_words
+ end
+
+ def abstract_limit
+ word_limit(:abstract)
+ end
+
+ def submission_limit
+ word_limit(:submission_text)
end
# TODO: create a module to be mixed into model to perform same operation
@@ -325,13 +437,13 @@ def set_week
end
def before_end_of_conference
- errors
- .add(:created_at, "can't be after the conference end date!") if program.conference&.end_date &&
- (Date.today > program.conference.end_date)
- end
+ return if submitter&.is_admin?
- def conference_id
- program.conference_id
+ if program.conference&.end_date &&
+ (Date.today > program.conference.end_date)
+ errors
+ .add(:created_at, "can't be after the conference end date!")
+ end
end
##
@@ -356,3 +468,4 @@ def selected_schedule_id
end
end
end
+# rubocop:enable Metrics/ClassLength
diff --git a/app/models/event_schedule.rb b/app/models/event_schedule.rb
index 5358f0bc9..834971b02 100644
--- a/app/models/event_schedule.rb
+++ b/app/models/event_schedule.rb
@@ -1,10 +1,32 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: event_schedules
+#
+# id :bigint not null, primary key
+# enabled :boolean default(TRUE)
+# start_time :datetime
+# created_at :datetime not null
+# updated_at :datetime not null
+# event_id :integer
+# room_id :integer
+# schedule_id :integer
+#
+# Indexes
+#
+# index_event_schedules_on_event_id (event_id)
+# index_event_schedules_on_event_id_and_schedule_id (event_id,schedule_id) UNIQUE
+# index_event_schedules_on_room_id (room_id)
+# index_event_schedules_on_schedule_id (schedule_id)
+#
class EventSchedule < ApplicationRecord
default_scope { where(enabled: true) }
- belongs_to :schedule
- belongs_to :event
+
+ belongs_to :schedule, touch: true
+ belongs_to :event, touch: true
belongs_to :room
+ delegate :url, to: :room, prefix: true, allow_nil: true
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
@@ -23,10 +45,41 @@ class EventSchedule < ApplicationRecord
scope :canceled, -> { joins(:event).where('state = ?', 'canceled') }
scope :withdrawn, -> { joins(:event).where('state = ?', 'withdrawn') }
- scope :with_event_states, ->(*states){ joins(:event).where('events.state IN (?)', states) }
+ scope :with_event_states, ->(*states) { joins(:event).where('events.state IN (?)', states) }
delegate :guid, to: :room, prefix: true
+ def timezone
+ event.conference.timezone
+ end
+
+ ##
+ # True within `threshold` before and after the event.
+ #
+ def happening_now?(threshold = 15.minutes)
+ # TODO: Save start_time with local timezone info when making an event schedule
+ in_tz_start = start_time.in_time_zone(timezone)
+ in_tz_end = end_time.in_time_zone(timezone)
+ in_tz_start -= in_tz_start.utc_offset
+ in_tz_end -= in_tz_end.utc_offset
+
+ return false if in_tz_end < Time.now
+
+ begin_range = Time.now - threshold
+ end_range = Time.now + threshold
+ event_time_range = in_tz_start..in_tz_end
+ now_range = begin_range..end_range
+ event_time_range.overlaps?(now_range)
+ end
+
+ def happening_later?
+ # TODO: Save start_time with local timezone info when making an event schedule
+ in_tz_start = start_time.in_time_zone(timezone)
+ in_tz_start -= in_tz_start.utc_offset
+
+ in_tz_start >= Time.now
+ end
+
def self.withdrawn_or_canceled_event_schedules(schedule_ids)
EventSchedule
.unscoped
@@ -41,6 +94,20 @@ def end_time
start_time + event.event_type.length.minutes
end
+ ##
+ # Returns if the event is in the past.
+ #
+ def ended?
+ !happening_now? && end_time_in_conference_timezone < time_in_conference_timezone(Time.current)
+ end
+
+ ##
+ # Returns a time + room number string for sorting.
+ #
+ def sortable_timestamp
+ "#{start_time.to_i}-#{room&.order}"
+ end
+
##
# Returns event schedules that are scheduled in the same room and start_time as event
#
@@ -52,7 +119,7 @@ def intersecting_event_schedules
end
# event_schedule_source is a cached enumerable object that helps
- # avoid repetitive EXISTS queries when rendering the schedule carousel partial
+ # avoid repetitive EXISTS queries when rendering the schedule partial
def replacement?(event_schedule_source = nil)
return false unless event.state == 'confirmed'
return replaced_event_schedules.exists? unless event_schedule_source
@@ -75,6 +142,14 @@ def intersects_with?(other)
other.schedule_id == schedule_id
end
+ def start_time_in_conference_timezone
+ time_in_conference_timezone(start_time)
+ end
+
+ def end_time_in_conference_timezone
+ time_in_conference_timezone(end_time)
+ end
+
private
def replaced_event_schedules
@@ -84,13 +159,21 @@ def replaced_event_schedules
def start_after_end_hour
return unless event && start_time && event.program && event.program.conference && event.program.conference.end_hour
- errors.add(:start_time, "can't be after the conference end hour (#{event.program.conference.end_hour})") if start_time.hour >= event.program.conference.end_hour
+ if start_time.hour >= event.program.conference.end_hour
+ errors.add(:start_time,
+ "can't be after the conference end hour (#{event.program.conference.end_hour})")
+ end
end
def start_before_start_hour
- return unless event && start_time && event.program && event.program.conference && event.program.conference.start_hour
+ unless event && start_time && event.program && event.program.conference && event.program.conference.start_hour
+ return
+ end
- errors.add(:start_time, "can't be before the conference start hour (#{event.program.conference.start_hour})") if start_time.hour < event.program.conference.start_hour
+ if start_time.hour < event.program.conference.start_hour
+ errors.add(:start_time,
+ "can't be before the conference start hour (#{event.program.conference.start_hour})")
+ end
end
def conference_id
@@ -127,6 +210,15 @@ def during_track
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)
+ unless event.track.schedules.include?(schedule)
+ errors.add(:schedule,
+ "must be one of #{event.track.name} track's schedules")
+ end
+ end
+
+ def time_in_conference_timezone(time)
+ time_in_tz = time.in_time_zone(timezone)
+ time_in_tz -= time_in_tz.utc_offset
+ time_in_tz
end
end
diff --git a/app/models/event_type.rb b/app/models/event_type.rb
index 691744c46..0d2943dc9 100644
--- a/app/models/event_type.rb
+++ b/app/models/event_type.rb
@@ -1,5 +1,21 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: event_types
+#
+# id :bigint not null, primary key
+# color :string
+# description :string
+# length :integer default(30)
+# maximum_abstract_length :integer default(500)
+# minimum_abstract_length :integer default(0)
+# submission_template :text
+# title :string not null
+# created_at :datetime
+# updated_at :datetime
+# program_id :integer
+#
class EventType < ApplicationRecord
belongs_to :program, touch: true
has_many :events, dependent: :restrict_with_error
@@ -8,7 +24,7 @@ class EventType < ApplicationRecord
ignore: %i[updated_at]
validates :title, presence: true
- validates :length, numericality: {greater_than: 0}
+ validates :length, numericality: { greater_than: 0 }
validates :minimum_abstract_length, presence: true
validates :maximum_abstract_length, presence: true
validate :length_step
@@ -24,7 +40,10 @@ class EventType < ApplicationRecord
# Check if length is a divisor of program schedule cell size. Used as validation.
#
def length_step
- errors.add(:length, "must be a divisor of #{program.schedule_interval}") if program && length % program.schedule_interval != 0
+ if program && length % program.schedule_interval != 0
+ errors.add(:length,
+ "must be a divisor of #{program.schedule_interval}")
+ end
end
def capitalize_color
diff --git a/app/models/event_user.rb b/app/models/event_user.rb
index 5e4d9b202..19cf04cee 100644
--- a/app/models/event_user.rb
+++ b/app/models/event_user.rb
@@ -1,9 +1,23 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: event_users
+#
+# id :bigint not null, primary key
+# comment :string
+# event_role :string default("participant"), not null
+# created_at :datetime
+# updated_at :datetime
+# event_id :integer
+# user_id :integer
+#
class EventUser < ApplicationRecord
- # TODO: Do we need these roles?
- ROLES = [%w[Speaker speaker], %w[Submitter submitter], %w[Moderator moderator]]
+ ROLES = [%w[Speaker speaker], %w[Submitter submitter], %w[Moderator moderator],
+ %w[Volunteer volunteer]]
belongs_to :event, touch: true
belongs_to :user
+
+ has_paper_trail on: %i[create update], ignore: [:updated_at]
end
diff --git a/app/models/events_registration.rb b/app/models/events_registration.rb
index 735df2a74..6e9a09725 100644
--- a/app/models/events_registration.rb
+++ b/app/models/events_registration.rb
@@ -1,5 +1,15 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: events_registrations
+#
+# id :bigint not null, primary key
+# attended :boolean default(FALSE), not null
+# created_at :datetime
+# event_id :integer
+# registration_id :integer
+#
class EventsRegistration < ApplicationRecord
belongs_to :registration
belongs_to :event
diff --git a/app/models/favourite_events.rb b/app/models/favourite_events.rb
new file mode 100644
index 000000000..c2f1a6a35
--- /dev/null
+++ b/app/models/favourite_events.rb
@@ -0,0 +1,15 @@
+# == Schema Information
+#
+# Table name: events_users
+#
+# event_id :bigint
+# user_id :bigint
+#
+# Indexes
+#
+# index_events_users_on_event_id (event_id)
+# index_events_users_on_user_id (user_id)
+#
+class FavouriteEvents < ApplicationRecord
+ self.table_name = 'events_users'
+end
diff --git a/app/models/lodging.rb b/app/models/lodging.rb
index 2f7a87ad0..822f38bd6 100644
--- a/app/models/lodging.rb
+++ b/app/models/lodging.rb
@@ -1,5 +1,22 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: lodgings
+#
+# id :bigint not null, primary key
+# description :text
+# name :string
+# photo_content_type :string
+# photo_file_name :string
+# photo_file_size :integer
+# photo_updated_at :datetime
+# picture :string
+# website_link :string
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class Lodging < ApplicationRecord
belongs_to :conference
diff --git a/app/models/openid.rb b/app/models/openid.rb
index feaa6a9d9..043427f60 100644
--- a/app/models/openid.rb
+++ b/app/models/openid.rb
@@ -1,5 +1,17 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: openids
+#
+# id :bigint not null, primary key
+# email :string
+# provider :string
+# uid :string
+# created_at :datetime
+# updated_at :datetime
+# user_id :integer
+#
class Openid < ApplicationRecord
belongs_to :user
validates :provider, :uid, :email, presence: true
diff --git a/app/models/organization.rb b/app/models/organization.rb
index 877de9dff..d1930403e 100644
--- a/app/models/organization.rb
+++ b/app/models/organization.rb
@@ -1,5 +1,15 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: organizations
+#
+# id :bigint not null, primary key
+# code_of_conduct :text
+# description :text
+# name :string not null
+# picture :string
+#
class Organization < ApplicationRecord
resourcify :roles, dependent: :delete_all
diff --git a/app/models/payment.rb b/app/models/payment.rb
index e313f8dd0..d6319e422 100644
--- a/app/models/payment.rb
+++ b/app/models/payment.rb
@@ -1,12 +1,25 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: payments
+#
+# id :bigint not null, primary key
+# amount :integer
+# authorization_code :string
+# last4 :string
+# status :integer default("unpaid"), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# conference_id :integer not null
+# user_id :integer not null
+#
class Payment < ApplicationRecord
has_many :ticket_purchases
belongs_to :user
belongs_to :conference
- attr_accessor :stripe_customer_email
- attr_accessor :stripe_customer_token
+ attr_accessor :stripe_customer_email, :stripe_customer_token
validates :status, presence: true
validates :user_id, presence: true
@@ -22,10 +35,14 @@ def amount_to_pay
Ticket.total_price(conference, user, paid: false).cents
end
+ def stripe_description
+ "Tickets for #{conference.title} #{user.name} #{user.email}"
+ end
+
def purchase
gateway_response = Stripe::Charge.create source: stripe_customer_token,
receipt_email: stripe_customer_email,
- description: "ticket purchases(#{user.username})",
+ description: stripe_description,
amount: amount_to_pay,
currency: conference.tickets.first.price_currency
@@ -34,9 +51,8 @@ def purchase
self.authorization_code = gateway_response[:id]
self.status = 'success'
true
-
- rescue Stripe::StripeError => error
- errors.add(:base, error.message)
+ rescue Stripe::StripeError => e
+ errors.add(:base, e.message)
self.status = 'failure'
false
end
diff --git a/app/models/physical_ticket.rb b/app/models/physical_ticket.rb
index 2493cbbea..a476888ab 100644
--- a/app/models/physical_ticket.rb
+++ b/app/models/physical_ticket.rb
@@ -1,5 +1,19 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: physical_tickets
+#
+# id :bigint not null, primary key
+# token :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# ticket_purchase_id :integer not null
+#
+# Indexes
+#
+# index_physical_tickets_on_token (token) UNIQUE
+#
class PhysicalTicket < ApplicationRecord
belongs_to :ticket_purchase
has_one :ticket, through: :ticket_purchase
diff --git a/app/models/program.rb b/app/models/program.rb
index f367d1b2e..3adc2f744 100644
--- a/app/models/program.rb
+++ b/app/models/program.rb
@@ -1,6 +1,27 @@
# frozen_string_literal: true
-# cannot delete program if there are events submitted
+# == Schema Information
+#
+# Table name: programs
+#
+# id :bigint not null, primary key
+# blind_voting :boolean default(FALSE)
+# languages :string
+# rating :integer default(0)
+# schedule_fluid :boolean default(FALSE)
+# schedule_interval :integer default(15), not null
+# schedule_public :boolean default(FALSE)
+# voting_end_date :datetime
+# voting_start_date :datetime
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+# selected_schedule_id :integer
+#
+# Indexes
+#
+# index_programs_on_selected_schedule_id (selected_schedule_id)
+#
class Program < ApplicationRecord
has_paper_trail on: [:update], ignore: [:updated_at], meta: { conference_id: :conference_id }
@@ -12,7 +33,6 @@ class Program < ApplicationRecord
has_many :tracks, dependent: :destroy
has_many :difficulty_levels, dependent: :destroy
has_many :schedules, dependent: :destroy
- has_many :event_schedules, through: :schedules
belongs_to :selected_schedule, class_name: 'Schedule'
has_many :events, dependent: :destroy do
def require_registration
@@ -20,7 +40,7 @@ def require_registration
end
def with_registration_open
- select { |e| e if e.registration_possible? }
+ select { |e| e if e.registration_possible? }.sort
end
# All confirmed events of the conference with attribute require_registration
@@ -44,7 +64,7 @@ def highlights
has_many :event_schedules, through: :events
has_many :event_users, through: :events
- has_many :program_events_speakers, -> {where(event_role: 'speaker')}, through: :events, source: :event_users
+ has_many :program_events_speakers, -> { where(event_role: 'speaker') }, through: :events, source: :event_users
has_many :speakers, -> { distinct }, through: :program_events_speakers, source: :user do
def confirmed
joins(:events).where(events: { state: :confirmed })
@@ -63,9 +83,9 @@ def unregistered(conference)
accepts_nested_attributes_for :tracks, reject_if: proc { |r| r['name'].blank? }, allow_destroy: true
accepts_nested_attributes_for :difficulty_levels, allow_destroy: true
-# validates :conference_id, presence: true, uniqueness: true
+ # validates :conference_id, presence: true, uniqueness: true
validates :rating, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 10, only_integer: true }
- validates :schedule_interval, numericality: { greater_than_or_equal_to: 5, less_than_or_equal_to: 60 }, presence: true
+ validates :schedule_interval, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 60 }, presence: true
validate :schedule_interval_divisor_60
validate :voting_start_date_before_end_date
validate :voting_dates_exist
@@ -80,23 +100,23 @@ def unregistered(conference)
def selected_event_schedules(includes: [:event])
event_schedules = []
event_schedules = selected_schedule.event_schedules.includes(*includes).order(start_time: :asc) if selected_schedule
+
tracks.self_organized.confirmed.includes(selected_schedule: { event_schedules: includes }).order(start_date: :asc).each do |track|
next unless track.selected_schedule
event_schedules += track.selected_schedule.event_schedules
end
- event_schedules.sort_by(&:start_time)
+ event_schedules.sort_by(&:sortable_timestamp)
end
##
- # Checks if blind_voting is enabled and if voting period is over
+ # Checks if blind_voting is enabled or if voting period is over
# ====Returns
# * +true+ -> If we can show voting details
# * +false+ -> If we cannot show voting details
def show_voting?
- return true unless blind_voting
-
- Time.current > voting_end_date
+ # TODO-SNAPCON: Figure out if this is the best behavior?
+ !blind_voting || Time.current > voting_end_date
end
##
@@ -115,9 +135,15 @@ def voting_period?
# ====Returns
# Errors when the condition is not true
def voting_dates_exist
- errors.add(:voting_start_date, 'must be set, when blind voting is enabled') if blind_voting && !voting_start_date && !voting_end_date
+ if blind_voting && !voting_start_date && !voting_end_date
+ errors.add(:voting_start_date,
+ 'must be set, when blind voting is enabled')
+ end
- errors.add(:voting_end_date, 'must be set, when blind voting is enabled') if blind_voting && !voting_start_date && !voting_end_date
+ if blind_voting && !voting_start_date && !voting_end_date
+ errors.add(:voting_end_date,
+ 'must be set, when blind voting is enabled')
+ end
errors.add(:voting_end_date, 'must be set, when voting_start_date is set') if voting_start_date && !voting_end_date
@@ -129,7 +155,10 @@ def voting_dates_exist
# ====Returns
# Errors when the condition is not true
def voting_start_date_before_end_date
- errors.add(:voting_start_date, 'must be before voting end date') if voting_start_date && voting_end_date && voting_start_date > voting_end_date
+ if voting_start_date && voting_end_date && voting_start_date > voting_end_date
+ errors.add(:voting_start_date,
+ 'must be before voting end date')
+ end
end
##
@@ -160,11 +189,11 @@ def notify_on_schedule_public?
return false unless schedule_public
# do not notify unless the mail content is set up
- (!conference.email_settings.program_schedule_public_subject.blank? && !conference.email_settings.program_schedule_public_body.blank?)
+ conference.email_settings.program_schedule_public_subject.present? && conference.email_settings.program_schedule_public_body.present?
end
def languages_list
- languages.split(',').map {|l| ISO_639.find(l).english_name} if languages.present?
+ languages.split(',').map { |l| ISO_639.find(l).english_name } if languages.present?
end
##
@@ -174,6 +203,7 @@ def languages_list
# * +True+ -> If there is any event for the given date
# * +False+ -> If there is not any event for the given date
def any_event_for_this_date?(date)
+ return false if date.nil? || date == ''
return false unless selected_schedule.present?
parsed_date = DateTime.parse("#{date} 00:00").utc
@@ -199,8 +229,32 @@ def remaining_cfp_types
Cfp::TYPES - cfps.pluck(:cfp_type)
end
+ def event_schedule_for_fullcalendar
+ Rails.cache.fetch("#{cache_key_for_schedule}}/fullcalendar=X") do
+ selected_event_schedules(includes: [:event, :room,
+ { event: %i[event_type track program parent_event] }])
+ end
+ end
+
+ def event_schedule_program_view
+ Rails.cache.fetch("#{cache_key_for_schedule}}/program") do
+ selected_event_schedules(includes: [:event, :room,
+ { event: %i[event_type speakers speaker_event_users
+ submitter submitter_event_user
+ track program] }])
+ end
+ end
+
+ def super_events
+ events.where(superevent: true)
+ end
+
private
+ def cache_key_for_schedule
+ "#{cache_key_with_version}#{selected_schedule&.cache_key_with_version}"
+ end
+
##
# Creates default EventTypes for this Conference. Used as before_create.
#
@@ -238,11 +292,15 @@ def check_languages_format
self.languages = languages.delete(' ').downcase
errors.add(:languages, 'must be two letters separated by commas') && return unless
languages.match(/^$|(\A[a-z][a-z](,[a-z][a-z])*\z)/).present?
+
languages_array = languages.split(',')
# We check that languages are not repeated
errors.add(:languages, "can't be repeated") && return unless languages_array.uniq!.nil?
+
# We check if every language is a valid ISO 639-1 language
- errors.add(:languages, 'must be ISO 639-1 valid codes') unless languages_array.select{ |x| ISO_639.find(x).nil? }.empty?
+ errors.add(:languages, 'must be ISO 639-1 valid codes') unless languages_array.none? do |x|
+ ISO_639.find(x).nil?
+ end
end
##
diff --git a/app/models/qanswer.rb b/app/models/qanswer.rb
index 535da3217..0e532032a 100644
--- a/app/models/qanswer.rb
+++ b/app/models/qanswer.rb
@@ -1,5 +1,15 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: qanswers
+#
+# id :bigint not null, primary key
+# created_at :datetime
+# updated_at :datetime
+# answer_id :integer
+# question_id :integer
+#
class Qanswer < ApplicationRecord
belongs_to :question
belongs_to :answer, dependent: :delete
diff --git a/app/models/question.rb b/app/models/question.rb
index 702a3784e..e9ca7f8ca 100644
--- a/app/models/question.rb
+++ b/app/models/question.rb
@@ -1,5 +1,17 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: questions
+#
+# id :bigint not null, primary key
+# global :boolean
+# title :string
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+# question_type_id :integer
+#
class Question < ApplicationRecord
belongs_to :question_type
has_and_belongs_to_many :conferences
diff --git a/app/models/question_type.rb b/app/models/question_type.rb
index 2a5ebc247..17bdc8c6d 100644
--- a/app/models/question_type.rb
+++ b/app/models/question_type.rb
@@ -1,5 +1,14 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: question_types
+#
+# id :bigint not null, primary key
+# title :string
+# created_at :datetime
+# updated_at :datetime
+#
class QuestionType < ApplicationRecord
has_many :questions
end
diff --git a/app/models/registration.rb b/app/models/registration.rb
index 759003fd8..9b5d04fa9 100644
--- a/app/models/registration.rb
+++ b/app/models/registration.rb
@@ -1,5 +1,20 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: registrations
+#
+# id :bigint not null, primary key
+# accepted_code_of_conduct :boolean
+# attended :boolean default(FALSE)
+# other_special_needs :text
+# volunteer :boolean
+# week :integer
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+# user_id :integer
+#
class Registration < ApplicationRecord
require 'csv'
belongs_to :user
@@ -11,7 +26,7 @@ class Registration < ApplicationRecord
has_many :events_registrations
has_many :events, through: :events_registrations, dependent: :destroy
- has_paper_trail ignore: %i(updated_at week), meta: { conference_id: :conference_id }
+ has_paper_trail ignore: %i[updated_at week], meta: { conference_id: :conference_id }
accepts_nested_attributes_for :user
accepts_nested_attributes_for :qanswers
@@ -32,6 +47,8 @@ class Registration < ApplicationRecord
if: -> { conference.try(:code_of_conduct).present? }
}
+ validate :user_has_registration_ticket, if: -> { conference.registration_ticket_required? }
+
after_create :set_week, :subscribe_to_conference, :send_registration_mail
##
@@ -51,9 +68,7 @@ def subscribe_to_conference
end
def send_registration_mail
- if conference.email_settings.send_on_registration?
- Mailbot.registration_mail(conference, user).deliver_later
- end
+ Mailbot.registration_mail(conference, user).deliver_later if conference.email_settings.send_on_registration?
end
def set_week
@@ -65,4 +80,14 @@ def registration_limit_not_exceed
errors.add(:base, 'Registration limit exceeded')
end
end
+
+ def user_has_registration_ticket
+ return if conference.registration_ticket_required? &&
+ TicketPurchase.where(user: user, ticket: conference.registration_tickets).paid.any?
+
+ errors.add(:base, 'You must purchase a registration ticket before registering')
+ if TicketPurchase.where(user: user, ticket: conference.registration_tickets).unpaid.any?
+ errors.add(:base, 'You currently have a ticket with an unfinished purchase')
+ end
+ end
end
diff --git a/app/models/registration_period.rb b/app/models/registration_period.rb
index 66f02fe0a..32043ef17 100644
--- a/app/models/registration_period.rb
+++ b/app/models/registration_period.rb
@@ -1,5 +1,16 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: registration_periods
+#
+# id :bigint not null, primary key
+# end_date :date
+# start_date :date
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class RegistrationPeriod < ApplicationRecord
belongs_to :conference
@@ -12,15 +23,21 @@ class RegistrationPeriod < ApplicationRecord
private
def before_end_of_conference
- errors
- .add(:start_date, "can't be after the conference end date (#{conference.end_date})") if conference&.end_date && start_date && (start_date > conference.end_date)
+ if conference&.end_date && start_date && (start_date > conference.end_date)
+ errors
+ .add(:start_date, "can't be after the conference end date (#{conference.end_date})")
+ end
- errors
- .add(:end_date, "can't be after the conference end date (#{conference.end_date})") if conference&.end_date && end_date && (end_date > conference.end_date)
+ if conference&.end_date && end_date && (end_date > conference.end_date)
+ errors
+ .add(:end_date, "can't be after the conference end date (#{conference.end_date})")
+ end
end
def start_date_before_end_date
- errors
- .add(:start_date, "can't be after the end date") if start_date && end_date && start_date > end_date
+ if start_date && end_date && start_date > end_date
+ errors
+ .add(:start_date, "can't be after the end date")
+ end
end
end
diff --git a/app/models/resource.rb b/app/models/resource.rb
index 991ce0cf7..a5e435fa9 100644
--- a/app/models/resource.rb
+++ b/app/models/resource.rb
@@ -1,5 +1,16 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: resources
+#
+# id :bigint not null, primary key
+# description :text
+# name :string
+# quantity :integer
+# used :integer default(0)
+# conference_id :integer
+#
class Resource < ApplicationRecord
belongs_to :conference
validates :name, :used, :quantity, presence: true
diff --git a/app/models/role.rb b/app/models/role.rb
index 2be87d7b3..5ad6e85d3 100644
--- a/app/models/role.rb
+++ b/app/models/role.rb
@@ -1,11 +1,30 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: roles
+#
+# id :bigint not null, primary key
+# description :string
+# name :string
+# resource_type :string
+# created_at :datetime
+# updated_at :datetime
+# resource_id :integer
+#
+# Indexes
+#
+# index_roles_on_name (name)
+# index_roles_on_name_and_resource_type_and_resource_id (name,resource_type,resource_id)
+#
class Role < ApplicationRecord
belongs_to :resource, polymorphic: true
has_many :users_roles
has_many :users, through: :users_roles
- has_paper_trail on: [:create, :update], only: [:name, :description], meta: { conference_id: :resource_id }
+ has_paper_trail on: %i[create update],
+ only: %i[name description],
+ meta: { conference_id: :conference_id, organization_id: :organization_id }
before_destroy :cancel
scopify
@@ -14,6 +33,14 @@ class Role < ApplicationRecord
validates :name, uniqueness: { scope: :resource }
+ def conference_id
+ resource_type == 'Conference' ? resource_id : nil
+ end
+
+ def organization_id
+ resource_type == 'Organization' ? resource_id : nil
+ end
+
private
# Needed to ensure that removing all user from role doesn't remove role.
diff --git a/app/models/room.rb b/app/models/room.rb
index 4bd88834f..8bb55931e 100644
--- a/app/models/room.rb
+++ b/app/models/room.rb
@@ -1,5 +1,18 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: rooms
+#
+# id :bigint not null, primary key
+# discussion_url :string
+# guid :string not null
+# name :string not null
+# order :integer
+# size :integer
+# url :string
+# venue_id :integer not null
+#
class Room < ApplicationRecord
include RevisionCount
belongs_to :venue
@@ -13,9 +26,22 @@ class Room < ApplicationRecord
validates :name, :venue_id, presence: true
validates :size, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
+ validates :order, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
- def conference
- venue.conference
+ default_scope { order(order: :asc) }
+
+ # Cache Busting on the events page, touch all events.
+ after_update :touch_conference_program
+ delegate :conference, to: :venue
+
+ def embed_url
+ return if url.blank?
+
+ if url.match?(/zoom.us/) & !url.match?('/zoom.us/wc')
+ return url.gsub('/j', '/wc/join')
+ end
+
+ url
end
private
@@ -28,4 +54,8 @@ def generate_guid
def conference_id
venue.conference_id
end
+
+ def touch_conference_program
+ conference.program.touch
+ end
end
diff --git a/app/models/schedule.rb b/app/models/schedule.rb
index 1584733f5..40a2c37ff 100644
--- a/app/models/schedule.rb
+++ b/app/models/schedule.rb
@@ -1,13 +1,41 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: schedules
+#
+# id :bigint not null, primary key
+# created_at :datetime not null
+# updated_at :datetime not null
+# program_id :integer
+# track_id :integer
+#
+# Indexes
+#
+# index_schedules_on_program_id (program_id)
+# index_schedules_on_track_id (track_id)
+#
class Schedule < ApplicationRecord
- belongs_to :program
+ belongs_to :program, touch: true
belongs_to :track
has_many :event_schedules, dependent: :destroy
has_many :events, through: :event_schedules
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
+ def with_all_associated_data
+ includes(event_schedule: { event: [:event_type] })
+ end
+
+ # TODO: User this or remove.
+ # A user's schedule includes:
+ # favorited events
+ # events speaking at
+ # volunteer events.
+ def for_user(user)
+ events.select { |event| event.planned_for_user?(user) }
+ end
+
private
def conference_id
diff --git a/app/models/splashpage.rb b/app/models/splashpage.rb
index 5cd9ef0d3..650e06575 100644
--- a/app/models/splashpage.rb
+++ b/app/models/splashpage.rb
@@ -1,7 +1,35 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: splashpages
+#
+# id :bigint not null, primary key
+# banner_photo_content_type :string
+# banner_photo_file_name :string
+# banner_photo_file_size :integer
+# banner_photo_updated_at :datetime
+# include_booths :boolean
+# include_cfp :boolean default(FALSE)
+# include_happening_now :boolean
+# include_lodgings :boolean
+# include_program :boolean
+# include_registrations :boolean
+# include_social_media :boolean
+# include_sponsors :boolean
+# include_tickets :boolean
+# include_tracks :boolean
+# include_venue :boolean
+# public :boolean
+# shuffle_highlights :boolean default(FALSE), not null
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class Splashpage < ApplicationRecord
belongs_to :conference
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
+
+ mount_uploader :banner_photo, PictureUploader, mount_on: :banner_photo_file_name
end
diff --git a/app/models/sponsor.rb b/app/models/sponsor.rb
index fa63411fa..4f7b21cc2 100644
--- a/app/models/sponsor.rb
+++ b/app/models/sponsor.rb
@@ -1,5 +1,20 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: sponsors
+#
+# id :bigint not null, primary key
+# description :text
+# logo_file_name :string
+# name :string
+# picture :string
+# website_url :string
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+# sponsorship_level_id :integer
+#
class Sponsor < ApplicationRecord
belongs_to :sponsorship_level
belongs_to :conference
diff --git a/app/models/sponsorship_level.rb b/app/models/sponsorship_level.rb
index 48f41594a..63634e27d 100644
--- a/app/models/sponsorship_level.rb
+++ b/app/models/sponsorship_level.rb
@@ -1,5 +1,16 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: sponsorship_levels
+#
+# id :bigint not null, primary key
+# position :integer
+# title :string
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class SponsorshipLevel < ApplicationRecord
validates :title, presence: true
belongs_to :conference
diff --git a/app/models/subscription.rb b/app/models/subscription.rb
index 0bb966407..e4cb7d4d3 100644
--- a/app/models/subscription.rb
+++ b/app/models/subscription.rb
@@ -1,10 +1,20 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: subscriptions
+#
+# id :bigint not null, primary key
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+# user_id :integer
+#
class Subscription < ApplicationRecord
belongs_to :conference
belongs_to :user
- has_paper_trail on: %i(create destroy), ignore: [:updated_at], meta: { conference_id: :conference_id }
+ has_paper_trail on: %i[create destroy], ignore: [:updated_at], meta: { conference_id: :conference_id }
validates :user_id, uniqueness: { scope: :conference_id, message: 'already subscribed!' }
end
diff --git a/app/models/survey.rb b/app/models/survey.rb
index 66783e90d..75465bd4f 100644
--- a/app/models/survey.rb
+++ b/app/models/survey.rb
@@ -1,11 +1,30 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: surveys
+#
+# id :bigint not null, primary key
+# description :text
+# end_date :datetime
+# start_date :datetime
+# surveyable_type :string
+# target :integer default("after_conference")
+# title :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# surveyable_id :integer
+#
+# Indexes
+#
+# index_surveys_on_surveyable_type_and_surveyable_id (surveyable_type,surveyable_id)
+#
class Survey < ActiveRecord::Base
belongs_to :surveyable, polymorphic: true
has_many :survey_questions, dependent: :destroy
has_many :survey_submissions, dependent: :destroy
- enum target: [:after_conference, :during_registration, :after_event]
+ enum target: { after_conference: 0, during_registration: 1, after_event: 2 }
validates :title, presence: true
##
diff --git a/app/models/survey_question.rb b/app/models/survey_question.rb
index 748f70dd9..c53f153c2 100644
--- a/app/models/survey_question.rb
+++ b/app/models/survey_question.rb
@@ -1,13 +1,27 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: survey_questions
+#
+# id :bigint not null, primary key
+# kind :integer default("boolean")
+# mandatory :boolean default(FALSE)
+# max_choices :integer
+# min_choices :integer
+# possible_answers :text
+# title :string
+# survey_id :integer
+#
class SurveyQuestion < ActiveRecord::Base
belongs_to :survey
has_many :survey_replies, dependent: :destroy
# Order of this list should not be changed without proper action!
- enum kind: [:boolean, :choice, :string, :text, :datetime, :numeric]
+ enum kind: { boolean: 0, choice: 1, string: 2, text: 3, datetime: 4, numeric: 5 }
- ICONS = { boolean: 'circle-dot', choice: 'square-check', string: 'pen-to-square', text: 'align-left', datetime: 'clock', numeric: 'hashtag' }.freeze
+ ICONS = { boolean: 'circle-dot', choice: 'square-check', string: 'pen-to-square', text: 'align-left',
+datetime: 'clock', numeric: 'hashtag' }.freeze
validates :title, presence: true
validates :possible_answers, :max_choices, :min_choices, presence: true, if: :choice?
@@ -39,6 +53,9 @@ def max_choices=(value)
private
def max_choices_greater_than_min
- errors.add(:max_choices, 'Max choices should not be less than min choices') if choice? && max_choices.to_i < min_choices.to_i
+ if choice? && max_choices.to_i < min_choices.to_i
+ errors.add(:max_choices,
+ 'Max choices should not be less than min choices')
+ end
end
end
diff --git a/app/models/survey_reply.rb b/app/models/survey_reply.rb
index bfba843b7..4659ac992 100644
--- a/app/models/survey_reply.rb
+++ b/app/models/survey_reply.rb
@@ -1,5 +1,16 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: survey_replies
+#
+# id :bigint not null, primary key
+# text :text
+# created_at :datetime not null
+# updated_at :datetime not null
+# survey_question_id :integer
+# user_id :integer
+#
class SurveyReply < ActiveRecord::Base
belongs_to :user
belongs_to :survey_question
diff --git a/app/models/survey_submission.rb b/app/models/survey_submission.rb
index f31198d4f..4d1e591f0 100644
--- a/app/models/survey_submission.rb
+++ b/app/models/survey_submission.rb
@@ -1,5 +1,15 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: survey_submissions
+#
+# id :bigint not null, primary key
+# created_at :datetime not null
+# updated_at :datetime not null
+# survey_id :integer
+# user_id :integer
+#
class SurveySubmission < ActiveRecord::Base
belongs_to :user
belongs_to :survey
diff --git a/app/models/ticket.rb b/app/models/ticket.rb
index 677ef90f6..fd6efeace 100644
--- a/app/models/ticket.rb
+++ b/app/models/ticket.rb
@@ -1,9 +1,27 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: tickets
+#
+# id :bigint not null, primary key
+# description :text
+# email_body :text
+# email_subject :string
+# price_cents :integer default(0), not null
+# price_currency :string default("USD"), not null
+# registration_ticket :boolean default(FALSE)
+# title :string not null
+# visible :boolean default(TRUE)
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class Ticket < ApplicationRecord
belongs_to :conference
has_many :ticket_purchases, dependent: :destroy
has_many :buyers, -> { distinct }, through: :ticket_purchases, source: :user
+ has_many :currency_conversions, through: :conferences
has_paper_trail meta: { conference_id: :conference_id },
ignore: %i[updated_at]
@@ -20,6 +38,8 @@ class Ticket < ApplicationRecord
validates :price_cents, numericality: { greater_than_or_equal_to: 0 }
+ scope :visible, -> { where(visible: true) }
+
def bought?(user)
buyers.include?(user)
end
@@ -57,12 +77,12 @@ def self.total_price(conference, user, paid: false)
rescue Money::Bank::UnknownRate
result = Money.new(-1, 'USD')
end
- result ? result : Money.new(0, 'USD')
+ result || Money.new(0, 'USD')
end
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) }
+ tickets.inject(0) { |sum, ticket| sum + (ticket.amount_paid * ticket.quantity) }
end
def tickets_turnover_total(id)
@@ -83,7 +103,7 @@ def tickets_of_conference_have_same_currency
tickets = Ticket.where(conference_id: conference_id)
return if tickets.count.zero? || (tickets.count == 1 && self == tickets.first)
- unless tickets.all?{|t| t.price_currency == price_currency }
+ unless tickets.all? { |t| t.price_currency == price_currency }
errors.add(:price_currency, 'is different from the existing tickets of this conference.')
end
end
diff --git a/app/models/ticket_purchase.rb b/app/models/ticket_purchase.rb
index f8e6c2105..5a076ea9d 100644
--- a/app/models/ticket_purchase.rb
+++ b/app/models/ticket_purchase.rb
@@ -1,5 +1,21 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: ticket_purchases
+#
+# id :bigint not null, primary key
+# amount_paid :float default(0.0)
+# paid :boolean default(FALSE)
+# quantity :integer default(1)
+# week :integer
+# created_at :datetime
+# conference_id :integer
+# payment_id :integer
+# ticket_id :integer
+# user_id :integer
+#
+
class TicketPurchase < ApplicationRecord
belongs_to :ticket
belongs_to :user
@@ -32,7 +48,7 @@ def self.purchase(conference, user, purchases)
errors.push('You cannot buy more than one registration tickets.')
else
ActiveRecord::Base.transaction do
- conference.tickets.each do |ticket|
+ conference.tickets.visible.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)
@@ -40,9 +56,7 @@ def self.purchase(conference, user, purchases)
else
purchase_ticket(conference, quantity, ticket, user)
end
- if purchase && !purchase.save
- errors.push(purchase.errors.full_messages)
- end
+ errors.push(purchase.errors.full_messages) if purchase && !purchase.save
end
end
end
@@ -95,6 +109,12 @@ def registration_ticket_already_purchased
errors.add(:quantity, 'cannot be greater than one for registration tickets.')
end
end
+
+ def generate_confirmation_mail(event_template)
+ parser = EmailTemplateParser.new(conference, user)
+ values = parser.retrieve_values(nil, nil, quantity, ticket)
+ EmailTemplateParser.parse_template(event_template, values)
+ end
end
private
@@ -105,6 +125,9 @@ def set_week
end
def count_purchased_registration_tickets(conference, purchases)
+ # TODO: WHAT CAUSED THIS???
+ return 0 unless purchases
+
conference.tickets.for_registration.inject(0) do |sum, registration_ticket|
sum + purchases[registration_ticket.id.to_s].to_i
end
diff --git a/app/models/ticket_scanning.rb b/app/models/ticket_scanning.rb
index 6b422100e..6f9eda7fc 100644
--- a/app/models/ticket_scanning.rb
+++ b/app/models/ticket_scanning.rb
@@ -1,5 +1,14 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: ticket_scannings
+#
+# id :bigint not null, primary key
+# created_at :datetime not null
+# updated_at :datetime not null
+# physical_ticket_id :integer not null
+#
class TicketScanning < ApplicationRecord
belongs_to :physical_ticket
diff --git a/app/models/track.rb b/app/models/track.rb
index d34bfbefc..d9c51f6b5 100644
--- a/app/models/track.rb
+++ b/app/models/track.rb
@@ -1,5 +1,33 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: tracks
+#
+# id :bigint not null, primary key
+# cfp_active :boolean not null
+# color :string
+# description :text
+# end_date :date
+# guid :string not null
+# name :string not null
+# relevance :text
+# short_name :string not null
+# start_date :date
+# state :string default("new"), not null
+# created_at :datetime
+# updated_at :datetime
+# program_id :integer
+# room_id :integer
+# selected_schedule_id :integer
+# submitter_id :integer
+#
+# Indexes
+#
+# index_tracks_on_room_id (room_id)
+# index_tracks_on_selected_schedule_id (selected_schedule_id)
+# index_tracks_on_submitter_id (submitter_id)
+#
class Track < ApplicationRecord
include ActiveRecord::Transitions
include RevisionCount
@@ -15,6 +43,7 @@ class Track < ApplicationRecord
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
+ before_validation :capitalize_color
before_create :generate_guid
validates :name, presence: true
validates :color, format: /\A#[0-9A-F]{6}\z/
@@ -26,7 +55,7 @@ class Track < ApplicationRecord
}
validates :state,
presence: true,
- inclusion: { in: %w(new to_accept accepted confirmed to_reject rejected canceled withdrawn) }
+ 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?
@@ -38,8 +67,6 @@ class Track < ApplicationRecord
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) }
@@ -56,34 +83,34 @@ class Track < ApplicationRecord
state :withdrawn
event :restart do
- transitions to: :new, from: [:rejected, :withdrawn, :canceled]
+ transitions to: :new, from: %i[rejected withdrawn canceled]
end
event :to_accept do
- transitions to: :to_accept, from: [:new, :to_reject]
+ transitions to: :to_accept, from: %i[new to_reject]
end
event :accept do
- transitions to: :accepted, from: [:new, :to_accept], on_transition: :create_organizer_role
+ transitions to: :accepted, from: %i[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]
+ transitions to: :to_reject, from: %i[new to_accept]
end
event :reject do
- transitions to: :rejected, from: [:new, :to_reject]
+ transitions to: :rejected, from: %i[new to_reject]
end
event :cancel do
- transitions to: :canceled, from: [:to_accept, :to_reject, :accepted, :confirmed], on_transition: :revoke_role_and_cleanup
+ transitions to: :canceled, from: %i[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
+ transitions to: :withdrawn, from: %i[new to_accept to_reject accepted confirmed],
+ on_transition: :revoke_role_and_cleanup
end
end
- def conference
- program.conference
- end
+ delegate :conference, to: :program
##
# Checks if the track is self-organized
@@ -176,9 +203,9 @@ def update_state(transition)
def generate_guid
guid = SecureRandom.urlsafe_base64
-# begin
-# guid = SecureRandom.urlsafe_base64
-# end while Person.where(:guid => guid).exists?
+ # begin
+ # guid = SecureRandom.urlsafe_base64
+ # end while Person.where(:guid => guid).exists?
self.guid = guid
end
@@ -200,10 +227,18 @@ def create_organizer_role
# 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)
+ unless start_date && end_date && program.try(:conference).try(:start_date) && program.try(:conference).try(:end_date)
+ return
+ end
- 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)
+ unless (program.conference.start_date..program.conference.end_date).cover?(start_date)
+ errors.add(:start_date,
+ "can't be outside of the conference's dates (#{program.conference.start_date}-#{program.conference.end_date})")
+ end
+ unless (program.conference.start_date..program.conference.end_date).cover?(end_date)
+ errors.add(:end_date,
+ "can't be outside of the conference's dates (#{program.conference.start_date}-#{program.conference.end_date})")
+ end
end
##
@@ -221,7 +256,10 @@ def start_date_before_end_date
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
+ unless room.venue.conference == program.conference
+ errors.add(:room,
+ "must be a room of #{program.conference.venue.name}")
+ end
end
##
@@ -233,12 +271,12 @@ def overlapping
(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
+ next unless (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
diff --git a/app/models/user.rb b/app/models/user.rb
index 993d48928..bcf38b4db 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,5 +1,52 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: users
+#
+# id :bigint not null, primary key
+# affiliation :string
+# avatar_content_type :string
+# avatar_file_name :string
+# avatar_file_size :integer
+# avatar_updated_at :datetime
+# biography :text
+# confirmation_sent_at :datetime
+# confirmation_token :string
+# confirmed_at :datetime
+# current_sign_in_at :datetime
+# current_sign_in_ip :string
+# email :string default(""), not null
+# email_public :boolean default(FALSE)
+# encrypted_password :string default(""), not null
+# is_admin :boolean default(FALSE)
+# is_disabled :boolean default(FALSE)
+# languages :string
+# last_sign_in_at :datetime
+# last_sign_in_ip :string
+# mobile :string
+# name :string
+# nickname :string
+# picture :string
+# remember_created_at :datetime
+# reset_password_sent_at :datetime
+# reset_password_token :string
+# sign_in_count :integer default(0)
+# timezone :string
+# tshirt :string
+# unconfirmed_email :string
+# username :string
+# volunteer_experience :text
+# created_at :datetime
+# updated_at :datetime
+#
+# Indexes
+#
+# index_users_on_confirmation_token (confirmation_token) UNIQUE
+# index_users_on_email (email) UNIQUE
+# index_users_on_reset_password_token (reset_password_token) UNIQUE
+# index_users_on_username (username) UNIQUE
+#
class IChainRecordNotFound < StandardError
end
@@ -7,6 +54,8 @@ class UserDisabled < StandardError
end
class User < ApplicationRecord
+ include TrackSavedChanges
+
# prevent N+1 queries with has_cached_role? by preloading roles *always*
default_scope { preload(:roles) }
@@ -17,7 +66,7 @@ def by_conference(conference)
end
end
has_many :tickets, through: :ticket_purchases, source: :ticket do
- def for_registration conference
+ def for_registration(conference)
where(conference: conference, registration_ticket: true).first
end
end
@@ -26,18 +75,34 @@ def for_registration conference
rolify
has_many :roles, through: :users_roles, dependent: :destroy
- has_paper_trail on: [:create, :update], ignore: [:sign_in_count, :remember_created_at, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip, :unconfirmed_email,
- :avatar_content_type, :avatar_file_size, :avatar_updated_at, :updated_at, :confirmation_sent_at, :confirmation_token, :reset_password_token]
+ has_paper_trail on: %i[create update], ignore: %i[sign_in_count remember_created_at current_sign_in_at last_sign_in_at current_sign_in_ip last_sign_in_ip unconfirmed_email
+ avatar_content_type avatar_file_size avatar_updated_at updated_at confirmation_sent_at confirmation_token reset_password_token]
+ # A user may have an uploaded avatar or use gravatar.
+ # The uploaded picture takes precedence.
include Gravtastic
gravtastic size: 32
+ mount_uploader :picture, PictureUploader, mount_on: :picture
+
before_create :setup_role
after_save :touch_events
+ # Note that using after_create_commit and after_update_commit does not work.
+ # See https://github.com/CactusPuppy/snapcon/pull/43#discussion_r609458034
+ after_commit :mailbluster_create_lead, on: :create
+ after_commit :mailbluster_delete_lead, on: :destroy
+ after_commit :mailbluster_update_lead, on: :update, if: lambda { |user|
+ %w[name email].any? do |key|
+ user.ts_saved_changes.key? key
+ end
+ }
+
# add scope
- scope :comment_notifiable, ->(conference) {joins(:roles).where('roles.name IN (?)', [:organizer, :cfp]).where('roles.resource_type = ? AND roles.resource_id = ?', 'Conference', conference.id)}
+ scope :comment_notifiable, lambda { |conference|
+ joins(:roles).where('roles.name IN (?)', %i[organizer cfp]).where('roles.resource_type = ? AND roles.resource_id = ?', 'Conference', conference.id)
+ }
# scopes for user distributions
scope :recent, lambda {
@@ -52,11 +117,13 @@ def for_registration conference
devise_modules = []
devise_modules += if ENV.fetch('OSEM_ICHAIN_ENABLED', nil) == 'true'
- [:ichain_authenticatable, :ichain_registerable, :omniauthable, omniauth_providers: []]
+ [:ichain_authenticatable, :ichain_registerable, :omniauthable, { omniauth_providers: [] }]
else
[:database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable,
- :omniauthable, omniauth_providers: [:suse, :google, :facebook, :github]]
+ :omniauthable,
+ { omniauth_providers: %i[suse google facebook github discourse] }]
+ # omniauth_providers: [:google, :discourse]
end
devise(*devise_modules)
@@ -67,9 +134,11 @@ def for_registration conference
has_many :event_users, dependent: :destroy
has_many :events, -> { distinct }, through: :event_users
- has_many :presented_events, -> { joins(:event_users).where(event_users: {event_role: 'speaker'}).distinct }, through: :event_users, source: :event
+ has_many :presented_events, lambda {
+ joins(:event_users).where(event_users: { event_role: 'speaker' }).distinct
+ }, through: :event_users, source: :event
has_many :registrations, dependent: :destroy do
- def for_conference conference
+ def for_conference(conference)
where(conference: conference).first
end
end
@@ -80,11 +149,12 @@ def for_conference conference
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
has_many :survey_replies
has_many :survey_submissions
+ has_and_belongs_to_many :favourite_events, class_name: 'Event'
+
accepts_nested_attributes_for :roles
scope :admin, -> { where(is_admin: true) }
@@ -96,7 +166,7 @@ def for_conference conference
validates :username,
uniqueness: {
- case_sensitive: false
+ case_sensitive: false
},
presence: true
@@ -116,7 +186,7 @@ def for_conference conference
# === Returns
# * +true+ if the user attended the event
# * +false+ if the user did not attend the event
- def attended_event? event
+ def attended_event?(event)
event_registration = event.events_registrations.find_by(registration: registrations)
return false unless event_registration.present?
@@ -124,30 +194,61 @@ def attended_event? event
event_registration.attended
end
- def mark_attendance_for_conference conference
+ def mark_attendance_for_conference(conference)
registration = registrations.for_conference(conference)
+ return true if registration&.attended
+
registration.attended = true
registration.save
end
+ def mark_attendance_for_event(event)
+ event_registration = event.events_registrations.find_by(registration: registrations)
+ return true if event_registration&.attended
+
+ if event_registration.blank?
+ conference_registration = registrations.for_conference(event.conference)
+ event_registration = event.events_registrations.build
+ event_registration.registration = conference_registration
+ end
+ event_registration.attended = true
+ event_registration.save
+ end
+
def name
- self[:name].blank? ? username : self[:name]
+ self[:name].presence || username
end
##
# Checks if a user has registered to an event
# ====Returns
# * +true+ or +false+
- def registered_to_event? event
+ def registered_to_event?(event)
event.registrations.include? registrations.find_by(conference: event.program.conference)
end
- def subscribed? conference
+ def subscribed?(conference)
subscriptions.find_by(conference_id: conference.id).present?
end
- def supports? conference
- ticket_purchases.find_by(conference_id: conference.id).present?
+ def supports?(conference)
+ ticket_purchases.find_by(conference_id: conference.id, paid: true).present?
+ end
+
+ ##
+ # Returns a user's profile picture URL.
+ # Partials should *not* directly call `gravatar_url`
+ def profile_picture(opts = {})
+ return gravatar_url(opts) unless picture.present?
+
+ size = (opts[:size] || 0).to_i
+ if size < 50
+ picture.tiny.url
+ elsif size <= 100
+ picture.thumb.url
+ else
+ picture.large.url
+ end
end
def self.for_ichain_username(username, attributes)
@@ -236,6 +337,12 @@ def get_roles
result
end
+ # TODO: Use a real authorization in the right place....
+ def manages_volunteers?(conference)
+ organizer_roles = get_roles['organizer']
+ organizer_roles&.include?(conference.short_title) # TODO: or Volunteer Coorinator.
+ end
+
def registered
registrations = self.registrations
if registrations.count == 0
@@ -263,23 +370,57 @@ def confirmed?
end
def proposals(conference)
- events.where('program_id = ? AND (event_users.event_role=? OR event_users.event_role=?)', conference.program.id, 'submitter', 'speaker')
+ events.where('program_id = ? AND (event_users.event_role=? OR event_users.event_role=?)', conference.program.id,
+ 'submitter', 'speaker')
end
def proposal_count(conference)
proposals(conference).count
end
+ def volunteer_duties(conference)
+ events.where(program_id: conference.program.id, 'event_users.event_role': 'volunteer')
+ end
+
+ def count_registration_tickets(conference)
+ count = 0
+ ticket_purchases.by_conference(conference).each do |ticket_purchase|
+ count += 1 if ticket_purchase.ticket.registration_ticket
+ end
+
+ count
+ end
+
def self.empty?
User.count == 1 && User.first.email == 'deleted@localhost.osem'
end
+ def dropdwon_display
+ more_info = email_public? ? username : "#{username} #{email}"
+ "#{name} (#{more_info})"
+ end
+
private
def setup_role
self.is_admin = true if User.empty?
end
+ def mailbluster_create_lead
+ MailblusterCreateLeadJob.perform_later(id)
+ ts_reset_saved_changes
+ end
+
+ def mailbluster_delete_lead
+ MailblusterDeleteLeadJob.perform_later(email)
+ ts_reset_saved_changes
+ end
+
+ def mailbluster_update_lead
+ MailblusterEditLeadJob.perform_later(id, old_email: ts_saved_changes.fetch('email', [nil])[0])
+ ts_reset_saved_changes
+ end
+
def touch_events
event_users.each(&:touch)
end
@@ -288,12 +429,10 @@ def touch_events
# Check if biography has an allowed number of words. Used as validation.
#
def biography_limit
- if biography.present?
- errors.add(:biography, 'is limited to 150 words.') if biography.split.length > 150
- end
+ errors.add(:biography, 'is limited to 200 words.') if biography.present? && (biography.split.length > 200)
end
- def send_devise_notification(notification, *args)
- devise_mailer.send(notification, self, *args).deliver_later
+ def send_devise_notification(notification, *)
+ devise_mailer.send(notification, self, *).deliver_later
end
end
diff --git a/app/models/users_role.rb b/app/models/users_role.rb
index b5dbe9fbd..621d42d37 100644
--- a/app/models/users_role.rb
+++ b/app/models/users_role.rb
@@ -1,14 +1,23 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: users_roles
+#
+# id :bigint not null, primary key
+# role_id :integer
+# user_id :integer
+#
+# Indexes
+#
+# index_users_roles_on_user_id_and_role_id (user_id,role_id)
+#
class UsersRole < ApplicationRecord
belongs_to :role
belongs_to :user
- has_paper_trail on: [:create, :destroy], meta: { conference_id: :conference_id }
+ delegate :conference_id, :organization_id, to: :role
- private
-
- def conference_id
- role.resource_id
- end
+ has_paper_trail on: %i[create destroy],
+ meta: { conference_id: :conference_id, organization_id: :organization_id }
end
diff --git a/app/models/vchoice.rb b/app/models/vchoice.rb
index bf5d05a6f..3481338c7 100644
--- a/app/models/vchoice.rb
+++ b/app/models/vchoice.rb
@@ -1,5 +1,13 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: vchoices
+#
+# id :bigint not null, primary key
+# vday_id :integer
+# vposition_id :integer
+#
class Vchoice < ApplicationRecord
belongs_to :vday
belongs_to :vposition
diff --git a/app/models/vday.rb b/app/models/vday.rb
index 5a9b52cfa..02ce8ecc4 100644
--- a/app/models/vday.rb
+++ b/app/models/vday.rb
@@ -1,5 +1,16 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: vdays
+#
+# id :bigint not null, primary key
+# day :date
+# description :text
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class Vday < ApplicationRecord
belongs_to :conference
diff --git a/app/models/venue.rb b/app/models/venue.rb
index 0064b5c46..992a027b6 100644
--- a/app/models/venue.rb
+++ b/app/models/venue.rb
@@ -1,25 +1,48 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: venues
+#
+# id :bigint not null, primary key
+# city :string
+# country :string
+# description :text
+# guid :string
+# latitude :string
+# longitude :string
+# name :string
+# photo_file_name :string
+# picture :string
+# postalcode :string
+# street :string
+# website :string
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class Venue < ApplicationRecord
belongs_to :conference
has_one :commercial, as: :commercialable, dependent: :destroy
has_many :rooms, dependent: :destroy
+ before_save :send_mail_notification
before_create :generate_guid
- has_paper_trail ignore: [:updated_at, :guid], meta: { conference_id: :conference_id }
+ has_paper_trail ignore: %i[updated_at guid], meta: { conference_id: :conference_id }
accepts_nested_attributes_for :commercial, allow_destroy: true
validates :name, :street, :city, :country, presence: true
mount_uploader :picture, PictureUploader, mount_on: :photo_file_name
- before_save :send_mail_notification
-
def address
"#{street}, #{city}, #{country_name}"
end
+ # TODO-SNAPCON: (mb) Fix this to use the country shortname?
def country_name
+ return unless country
+
I18nData.countries[country]
end
@@ -36,10 +59,12 @@ def send_mail_notification
def notify_on_venue_changed?
return false unless conference.try(:email_settings).try(:send_on_venue_updated)
# do not notify unless the address changed
- return false unless saved_change_to_name? || saved_change_to_street? || saved_change_to_city? || saved_change_to_country?
+ unless saved_change_to_name? || saved_change_to_street? || saved_change_to_city? || saved_change_to_country?
+ return false
+ end
# do not notify unless the mail content is set up
- (!conference.email_settings.venue_updated_subject.blank? && !conference.email_settings.venue_updated_body.blank?)
+ conference.email_settings.venue_updated_subject.present? && conference.email_settings.venue_updated_body.present?
end
# TODO: create a module to be mixed into model to perform same operation
diff --git a/app/models/vote.rb b/app/models/vote.rb
index cdd1e4cbe..85cf784f0 100644
--- a/app/models/vote.rb
+++ b/app/models/vote.rb
@@ -1,5 +1,16 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: votes
+#
+# id :bigint not null, primary key
+# rating :integer
+# created_at :datetime
+# updated_at :datetime
+# event_id :integer
+# user_id :integer
+#
class Vote < ApplicationRecord
belongs_to :user
belongs_to :event, touch: true
diff --git a/app/models/vposition.rb b/app/models/vposition.rb
index 6599bc8ab..5d726fe38 100644
--- a/app/models/vposition.rb
+++ b/app/models/vposition.rb
@@ -1,5 +1,16 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: vpositions
+#
+# id :bigint not null, primary key
+# description :text
+# title :string not null
+# created_at :datetime
+# updated_at :datetime
+# conference_id :integer
+#
class Vposition < ApplicationRecord
belongs_to :conference
diff --git a/app/pdfs/ticket_pdf.rb b/app/pdfs/ticket_pdf.rb
index cb624b1d1..de9c5ce3f 100644
--- a/app/pdfs/ticket_pdf.rb
+++ b/app/pdfs/ticket_pdf.rb
@@ -99,8 +99,8 @@ def draw_third_square
end
def draw_fourth_square
- x = @mid_horizontal + (@right - @mid_horizontal - 180) / 2
- y = cursor - (bounds.top - @mid_vertical - 180) / 2
+ 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 41eb2e28d..32d83dc71 100644
--- a/app/serializers/conference_serializer.rb
+++ b/app/serializers/conference_serializer.rb
@@ -1,5 +1,40 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: conferences
+#
+# id :bigint not null, primary key
+# booth_limit :integer default(0)
+# color :string
+# custom_css :text
+# custom_domain :string
+# description :text
+# end_date :date not null
+# end_hour :integer default(20)
+# events_per_week :text
+# guid :string not null
+# logo_file_name :string
+# picture :string
+# registration_limit :integer default(0)
+# revision :integer default(0), not null
+# short_title :string not null
+# start_date :date not null
+# start_hour :integer default(9)
+# ticket_layout :integer default("portrait")
+# timezone :string not null
+# title :string not null
+# use_vdays :boolean default(FALSE)
+# use_volunteers :boolean
+# use_vpositions :boolean default(FALSE)
+# created_at :datetime
+# updated_at :datetime
+# organization_id :integer
+#
+# Indexes
+#
+# index_conferences_on_organization_id (organization_id)
+#
class ConferenceSerializer < ActiveModel::Serializer
include ApplicationHelper
include Rails.application.routes.url_helpers
@@ -9,41 +44,41 @@ class ConferenceSerializer < ActiveModel::Serializer
:date_range, :revision
def difficulty_levels
- object.program.difficulty_levels.map do |difficulty_level| { id: difficulty_level.id,
- title: difficulty_level.title,
- description: difficulty_level.description
- }
+ object.program.difficulty_levels.map do |difficulty_level|
+ { id: difficulty_level.id,
+ title: difficulty_level.title,
+ description: difficulty_level.description }
end
end
def event_types
- object.program.event_types.map do |event_type| { id: event_type.id,
- title: event_type.title,
- length: event_type.length,
- description: event_type.description
- }
+ object.program.event_types.map do |event_type|
+ { id: event_type.id,
+ title: event_type.title,
+ length: event_type.length,
+ description: event_type.description }
end
end
def rooms
if object.venue
- object.venue.rooms.map do |room| { id: room.id,
- size: room.size,
- events: room.event_schedules.map do |event_schedule| { guid: event_schedule.event.title,
- title: event_schedule.event.title,
- subtitle: event_schedule.event.subtitle,
- abstract: event_schedule.event.abstract,
- description: event_schedule.event.description,
- is_highlight: event_schedule.event.is_highlight,
- require_registration: event_schedule.event.require_registration,
- start_time: event_schedule.start_time,
- event_type_id: event_schedule.event.event_type.id,
- difficulty_level_id: event_schedule.event.difficulty_level_id,
- track_id: event_schedule.event.track_id,
- speaker_names: event_schedule.event.speaker_names
- }
- end
- }
+ object.venue.rooms.map do |room|
+ { id: room.id,
+ size: room.size,
+ events: room.event_schedules.map do |event_schedule|
+ { guid: event_schedule.event.title,
+ title: event_schedule.event.title,
+ subtitle: event_schedule.event.subtitle,
+ abstract: event_schedule.event.abstract,
+ description: event_schedule.event.description,
+ is_highlight: event_schedule.event.is_highlight,
+ require_registration: event_schedule.event.require_registration,
+ start_time: event_schedule.start_time,
+ event_type_id: event_schedule.event.event_type.id,
+ difficulty_level_id: event_schedule.event.difficulty_level_id,
+ track_id: event_schedule.event.track_id,
+ speaker_names: event_schedule.event.speaker_names }
+ end }
end
else
[]
@@ -51,10 +86,10 @@ def rooms
end
def tracks
- object.program.tracks.map do |track| { 'id' => track.id,
- 'name' => track.name,
- 'description' => track.description
- }
+ object.program.tracks.map do |track|
+ { 'id' => track.id,
+ 'name' => track.name,
+ 'description' => track.description }
end
end
diff --git a/app/serializers/event_schedule_serializer.rb b/app/serializers/event_schedule_serializer.rb
index 44ecdbb96..086bfd210 100644
--- a/app/serializers/event_schedule_serializer.rb
+++ b/app/serializers/event_schedule_serializer.rb
@@ -1,5 +1,25 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: event_schedules
+#
+# id :bigint not null, primary key
+# enabled :boolean default(TRUE)
+# start_time :datetime
+# created_at :datetime not null
+# updated_at :datetime not null
+# event_id :integer
+# room_id :integer
+# schedule_id :integer
+#
+# Indexes
+#
+# index_event_schedules_on_event_id (event_id)
+# index_event_schedules_on_event_id_and_schedule_id (event_id,schedule_id) UNIQUE
+# index_event_schedules_on_room_id (room_id)
+# index_event_schedules_on_schedule_id (schedule_id)
+#
class EventScheduleSerializer < ActiveModel::Serializer
include ActionView::Helpers::TextHelper
diff --git a/app/serializers/event_serializer.rb b/app/serializers/event_serializer.rb
index 0f5b136fc..6f853a7b1 100644
--- a/app/serializers/event_serializer.rb
+++ b/app/serializers/event_serializer.rb
@@ -1,5 +1,43 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: events
+#
+# id :bigint not null, primary key
+# abstract :text
+# comments_count :integer default(0), not null
+# committee_review :text
+# description :text
+# guid :string not null
+# is_highlight :boolean default(FALSE)
+# language :string
+# max_attendees :integer
+# presentation_mode :integer
+# progress :string default("new"), not null
+# proposal_additional_speakers :text
+# public :boolean default(TRUE)
+# require_registration :boolean
+# start_time :datetime
+# state :string default("new"), not null
+# submission_text :text
+# subtitle :string
+# superevent :boolean
+# title :string not null
+# week :integer
+# created_at :datetime
+# updated_at :datetime
+# difficulty_level_id :integer
+# event_type_id :integer
+# parent_id :integer
+# program_id :integer
+# room_id :integer
+# track_id :integer
+#
+# Foreign Keys
+#
+# fk_rails_... (parent_id => events.id)
+#
class EventSerializer < ActiveModel::Serializer
include ActionView::Helpers::TextHelper
include Rails.application.routes.url_helpers
diff --git a/app/serializers/room_serializer.rb b/app/serializers/room_serializer.rb
index c75db316f..014aac4e6 100644
--- a/app/serializers/room_serializer.rb
+++ b/app/serializers/room_serializer.rb
@@ -1,5 +1,18 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: rooms
+#
+# id :bigint not null, primary key
+# discussion_url :string
+# guid :string not null
+# name :string not null
+# order :integer
+# size :integer
+# url :string
+# venue_id :integer not null
+#
class RoomSerializer < ActiveModel::Serializer
attributes :guid, :name, :description
diff --git a/app/serializers/track_serializer.rb b/app/serializers/track_serializer.rb
index 0eb7bebbd..8462b3fef 100644
--- a/app/serializers/track_serializer.rb
+++ b/app/serializers/track_serializer.rb
@@ -1,5 +1,33 @@
# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: tracks
+#
+# id :bigint not null, primary key
+# cfp_active :boolean not null
+# color :string
+# description :text
+# end_date :date
+# guid :string not null
+# name :string not null
+# relevance :text
+# short_name :string not null
+# start_date :date
+# state :string default("new"), not null
+# created_at :datetime
+# updated_at :datetime
+# program_id :integer
+# room_id :integer
+# selected_schedule_id :integer
+# submitter_id :integer
+#
+# Indexes
+#
+# index_tracks_on_room_id (room_id)
+# index_tracks_on_selected_schedule_id (selected_schedule_id)
+# index_tracks_on_submitter_id (submitter_id)
+#
class TrackSerializer < ActiveModel::Serializer
attributes :guid, :name, :color
end
diff --git a/app/services/email_template_parser.rb b/app/services/email_template_parser.rb
new file mode 100644
index 000000000..9c74e2d78
--- /dev/null
+++ b/app/services/email_template_parser.rb
@@ -0,0 +1,76 @@
+class EmailTemplateParser
+ def initialize(conference, user)
+ @conference = conference
+ @user = user
+ end
+
+ # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
+ def retrieve_values(event = nil, booth = nil, quantity = nil, ticket = nil)
+ h = {
+ 'email' => @user.email,
+ 'name' => @user.name,
+ 'conference' => @conference.title,
+ 'conference_start_date' => @conference.start_date,
+ 'conference_end_date' => @conference.end_date,
+ 'registrationlink' => Rails.application.routes.url_helpers.conference_conference_registration_url(
+ @conference.short_title, host: Rails.application.routes.default_url_options[:host]
+ ),
+ 'conference_splash_link' => Rails.application.routes.url_helpers.conference_url(
+ @conference.short_title, host: Rails.application.routes.default_url_options[:host]
+ ),
+
+ 'schedule_link' => Rails.application.routes.url_helpers.conference_schedule_url(
+ @conference.short_title, host: Rails.application.routes.default_url_options[:host]
+ )
+ }
+ if @conference.program.cfp
+ h['cfp_start_date'] = @conference.program.cfp.start_date
+ h['cfp_end_date'] = @conference.program.cfp.end_date
+ else
+ h['cfp_start_date'] = 'Unknown'
+ h['cfp_end_date'] = 'Unknown'
+ end
+ if @conference.venue
+ h['venue'] = @conference.venue.name
+ h['venue_address'] = @conference.venue.address
+ else
+ h['venue'] = 'Unknown'
+ h['venue_address'] = 'Unknown'
+ end
+ if @conference.registration_period
+ h['registration_start_date'] = @conference.registration_period.start_date
+ h['registration_end_date'] = @conference.registration_period.end_date
+ end
+ if event
+ h['eventtitle'] = event.title
+ h['proposalslink'] = Rails.application.routes.url_helpers.conference_program_proposals_url(
+ @conference.short_title, host: ENV.fetch('OSEM_HOSTNAME', 'localhost:3000')
+ )
+ h['committee_review'] = event.committee_review
+ h['committee_review_html'] = ApplicationController.helpers.markdown(event.committee_review)
+ end
+ if booth
+ h['booth_title'] = booth.title
+ end
+ if quantity
+ h['ticket_quantity'] = quantity.to_s
+ end
+ if ticket
+ h['ticket_title'] = ticket.title
+ h['ticket_purchase_id'] = ticket.id.to_s
+ end
+ h
+ end
+
+ def self.parse_template(text, values)
+ values.each do |key, value|
+ if value.is_a?(Date)
+ text = text.gsub "{#{key}}", value.strftime('%Y-%m-%d') if text.present?
+ else
+ text = text.gsub "{#{key}}", value unless text.blank? || value.blank?
+ end
+ end
+ text
+ end
+ # rubocop:enable Metrics/AbcSize, Metrics/ParameterLists
+end
diff --git a/app/services/full_calendar_formatter.rb b/app/services/full_calendar_formatter.rb
new file mode 100644
index 000000000..7f9a21594
--- /dev/null
+++ b/app/services/full_calendar_formatter.rb
@@ -0,0 +1,49 @@
+class FullCalendarFormatter
+ def self.rooms_to_resources(rooms)
+ rooms.map { |room| room_to_resource(room) }.to_json
+ end
+
+ def self.event_schedules_to_resources(event_schedules)
+ return '[]' if event_schedules.empty?
+
+ conference = event_schedules.first.schedule.program.conference
+ event_schedules.map { |event_schedule| event_schedule_to_resource(conference, event_schedule) }.to_json
+ end
+
+ class << self
+ include FormatHelper
+
+ private
+
+ def room_to_resource(room)
+ {
+ id: room.guid,
+ title: room.name,
+ order: room.order
+ }
+ end
+
+ def event_schedule_to_resource(conference, event_schedule)
+ event = event_schedule.event
+ event_type_color = event.event_type.color
+ url = Rails.application.routes.url_helpers.conference_program_proposal_path(conference.short_title, event.id)
+ background_event = event.event_type.title.match(/Break/i)
+ rooms = background_event ? conference.venue.rooms.pluck(:guid) : [event_schedule.room.guid]
+
+ {
+ id: event.guid,
+ title: event.title,
+ start: event_schedule.start_time_in_conference_timezone,
+ end: event_schedule.end_time_in_conference_timezone,
+ resourceIds: rooms,
+ url: url,
+ borderColor: event_type_color,
+ backgroundColor: event_type_color,
+ textColor: contrast_color(event_type_color),
+ className: "fc-event-track-#{event.track&.short_name || 'none'}",
+ display: background_event ? 'background' : 'auto',
+ has_parent: event.parent_event.present?
+ }
+ end
+ end
+end
diff --git a/app/services/mailbluster_manager.rb b/app/services/mailbluster_manager.rb
new file mode 100644
index 000000000..2bb06f4e9
--- /dev/null
+++ b/app/services/mailbluster_manager.rb
@@ -0,0 +1,40 @@
+class MailblusterManager
+ include HTTParty
+ base_uri 'https://api.mailbluster.com/api/leads/'
+ @auth_headers = {
+ headers: {
+ 'Content-Type' => 'application/json',
+ 'Authorization' => ENV.fetch('MAILBLUSTER_API_KEY', nil)
+ }
+ }
+
+ def self.query_api(method, path, body: {})
+ options = @auth_headers.merge(body: body.to_json)
+ send(method, path, options).parsed_response
+ end
+
+ def self.create_lead(user)
+ query_api(:post, '/', body: {
+ 'email' => user.email,
+ 'firstName' => user.name,
+ 'overrideExisting' => true,
+ 'subscribed' => true,
+ 'tags' => [ENV.fetch('OSEM_NAME', 'snapcon')]
+ })
+ end
+
+ def self.edit_lead(user, add_tags: [], remove_tags: [], old_email: nil)
+ email_hash = Digest::MD5.hexdigest(old_email.presence || user.email)
+ query_api(:put, "/#{email_hash}", body: {
+ 'email' => user.email,
+ 'firstName' => user.name,
+ 'addTags' => add_tags,
+ 'removeTags' => remove_tags
+ })
+ end
+
+ def self.delete_lead(email)
+ email_hash = Digest::MD5.hexdigest email
+ query_api(:delete, "/#{email_hash}")
+ end
+end
diff --git a/app/uploaders/picture_uploader.rb b/app/uploaders/picture_uploader.rb
index dba60332c..a243d9d9b 100644
--- a/app/uploaders/picture_uploader.rb
+++ b/app/uploaders/picture_uploader.rb
@@ -1,4 +1,3 @@
-# encoding: utf-8
# frozen_string_literal: true
class PictureUploader < CarrierWave::Uploader::Base
@@ -64,6 +63,10 @@ def store_dir
process resize_to_fit: [100, 100]
end
+ version :tiny do
+ process resize_to_fit: [32, 32]
+ end
+
version :first, if: :sponsor?
version :first do
process resize_and_pad: [320, 180, 'white']
@@ -85,11 +88,11 @@ def store_dir
end
def extension_allowlist
- %w(jpg jpeg gif png)
+ %w[jpg jpeg gif png]
end
def content_type_allowlist
- /image\//
+ %r{image/}
end
private
diff --git a/app/views/admin/booths/_all_booths.xlsx.axlsx b/app/views/admin/booths/_all_booths.xlsx.axlsx
index 5a0a1d48f..0c44a9e5b 100644
--- a/app/views/admin/booths/_all_booths.xlsx.axlsx
+++ b/app/views/admin/booths/_all_booths.xlsx.axlsx
@@ -2,7 +2,7 @@
wb.add_worksheet(name: "all #{(t 'booth').pluralize}") do |sheet|
bold_style = wb.styles.add_style(b: true)
- wrap_text = wb.styles.add_style alignment: {wrap_text: true}
+ wrap_text = wb.styles.add_style alignment: { wrap_text: true }
row = ["#{(t 'booth').capitalize} ID",
'Title',
'Description',
@@ -23,7 +23,7 @@ wb.add_worksheet(name: "all #{(t 'booth').pluralize}") do |sheet|
row << booth.submitter_relationship
row << booth.website_url
row << booth.state
- sheet.add_row row , style: wrap_text
- sheet.column_widths 10,20,35,35,20,30,35,10
+ sheet.add_row row, style: wrap_text
+ sheet.column_widths 10, 20, 35, 35, 20, 30, 35, 10
end
end
diff --git a/app/views/admin/booths/_confirmed_booths.xlsx.axlsx b/app/views/admin/booths/_confirmed_booths.xlsx.axlsx
index a6016703c..91da23c7c 100644
--- a/app/views/admin/booths/_confirmed_booths.xlsx.axlsx
+++ b/app/views/admin/booths/_confirmed_booths.xlsx.axlsx
@@ -2,7 +2,7 @@
wb.add_worksheet(name: "all #{(t 'booth').pluralize}") do |sheet|
bold_style = wb.styles.add_style(b: true)
- wrap_text = wb.styles.add_style alignment: {wrap_text: true}
+ wrap_text = wb.styles.add_style alignment: { wrap_text: true }
row = ["#{(t 'booth').capitalize} ID",
'Title',
'Description',
@@ -23,7 +23,7 @@ wb.add_worksheet(name: "all #{(t 'booth').pluralize}") do |sheet|
row << booth.submitter_relationship
row << booth.website_url
row << booth.state
- sheet.add_row row , style: wrap_text
- sheet.column_widths 10,20,35,35,20,30,35,10
+ sheet.add_row row, style: wrap_text
+ sheet.column_widths 10, 20, 35, 35, 20, 30, 35, 10
end
end
diff --git a/app/views/admin/cfps/_form.html.haml b/app/views/admin/cfps/_form.html.haml
index f6af36874..3baf8c219 100644
--- a/app/views/admin/cfps/_form.html.haml
+++ b/app/views/admin/cfps/_form.html.haml
@@ -7,10 +7,9 @@
= f.label :end_date, "End Date"
= f.text_field :end_date, class: 'form-control', id: 'registration-period-end-datepicker', start_date: @conference.start_date, end_date: @conference.end_date
.form-group
- = f.label :description, "Description"
- = f.text_area :description, rows: 2, data: { provide: 'markdown' }
- %span.help-block
- = markdown_hint
+ = f.label :description
+ = f.text_area :description, rows: 15, data: { provide: 'markdown' }
+ .help-block= markdown_hint
- if @cfp.cfp_type == 'events'
.checkbox
%label
diff --git a/app/views/admin/commercials/_update_form.haml b/app/views/admin/commercials/_update_form.haml
new file mode 100644
index 000000000..1da8551dc
--- /dev/null
+++ b/app/views/admin/commercials/_update_form.haml
@@ -0,0 +1,14 @@
+.caption.panel-footer
+ - if can?(:update, commercial)
+ = form_for commercial, url: form_url do |f|
+ .form-group
+ = f.label :title
+ = f.text_field :title, class: 'form-control'
+ .form-group
+ = f.label :url, 'URL'
+ %abbr{title: 'This field is required'} *
+ = f.url_field :url, class: 'form-control', id: "commercial_url_#{commercial.id}", required: 'required'
+ .help-block Just paste the url of your video/photo provider
+ = f.submit 'Update', class: 'btn btn-success'
+ = link_to 'Delete', admin_conference_commercial_path(conference.short_title, commercial.id),
+ method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger'
diff --git a/app/views/admin/commercials/index.html.haml b/app/views/admin/commercials/index.html.haml
index 0da9ffef4..3f260b8e0 100644
--- a/app/views/admin/commercials/index.html.haml
+++ b/app/views/admin/commercials/index.html.haml
@@ -1,11 +1,11 @@
.row
.col-md-12
.page-header
- %h1 Commercials
+ %h1 #{@conference.title} Materials
%p.text-muted
- Conference commercials will be displayed on the events in the
- = link_to 'schedule,', conference_schedule_path(@conference.short_title)
- if the event speaker didn't add an event commercial.
+ Conference materials will be displayed on the events in the
+ = link_to 'schedule,', vertical_schedule_conference_schedule_path(@conference.short_title)
+ if the event speaker didn't add any event materials.
- if can? :create, @conference.commercials.new
.row
.col-md-6
@@ -15,11 +15,14 @@
.col-md-6
= form_for(@commercial, url: admin_conference_commercials_path(conference_id: @conference.short_title)) do |f|
.form-group
- = f.label :url
+ = f.label :title
+ = f.text_field :title, class: 'form-control'
+ .form-group
+ = f.label :url, 'URL'
= f.url_field :url, required: 'required', autofocus: true, class: 'form-control'
%span.help-block
Just paste the url of your video/photo provider. YouTube, Vimeo, SpeakerDeck, SlideShare, Instagram, Flickr.
- = f.submit nil, { class: 'btn btn-primary pull-right', id: 'commercial_submit_action', disabled: true }
+ = f.submit 'Save Materials', { class: 'btn btn-primary pull-right', id: 'commercial_submit_action', disabled: true }
%hr
- @commercials.each_slice(3) do |slice|
@@ -27,18 +30,22 @@
- slice.each do |commercial|
- if commercial.persisted?
.col-md-4
- .thumbnail
- .flexvideo{ id: "resource-content-#{commercial.id}" }
- = render partial: 'shared/media_item', locals: { commercial: commercial }
- .caption
+ .panel.panel-default
+ %div{ id: "resource-content-#{commercial.id}" }
+ = render 'shared/media_item', commercial: commercial
+ .caption.panel-footer
- if can? :update, commercial
= form_for commercial, url: admin_conference_commercial_path(conference_id: @conference.short_title, id: commercial) do |f|
.form-group
- = f.label :url
- = f.url_field :url, id: "commercial_url_#{commercial.id}", required: 'required'
+ = f.label :title
+ = f.text_field :title, class: 'form-control'
+ .form-group
+ = f.label :url, 'URL'
+ %abbr{title: 'This field is required'} *
+ = f.url_field :url, class: 'form-control', id: "commercial_url_#{commercial.id}", required: 'required'
%span.help-block
Just paste the url of your video/photo provider
- = f.submit nil, { class: 'btn btn-success' }
+ = f.submit 'Update', class: 'btn btn-success'
- if can? :destroy, commercial
= link_to 'Delete', admin_conference_commercial_path(@conference.short_title, commercial.id),
method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger'
diff --git a/app/views/admin/conferences/_form_fields.html.haml b/app/views/admin/conferences/_form_fields.html.haml
index ceb72acc8..f5705f455 100644
--- a/app/views/admin/conferences/_form_fields.html.haml
+++ b/app/views/admin/conferences/_form_fields.html.haml
@@ -1,18 +1,17 @@
-%h4
- Basic Information
+%h4 Basic Information
%hr
- if f.object.new_record?
.form-group
= f.label :organization, "Organization"
= f.select :organization_id, Organization.accessible_by(current_ability, :update).pluck(:name, :id)
.form-group
- = f.label :title, "Title"
+ = f.label :title
%abbr{title: 'This field is required'} *
= f.text_field :title, required: true, class: 'form-control', placeholder: 'Title'
%span.help-block
The name of your conference as it shall appear throughout the site. Example: 'openSUSE Conference 2013'
.form-group
- = f.label :title, "Short Title"
+ = f.label :short_title
%abbr{title: 'This field is required'} *
= f.text_field :short_title, required: true, pattern: '[a-zA-Z0-9_-]+', title: 'Only letters, numbers, underscores, and dashes.', prepend: conferences_url + '/', class: 'form-control', placeholder: 'Short Title'
%span.help-block
@@ -21,9 +20,9 @@
froscon2011
- unless f.object.new_record? # We are showing more fields on the edit form
.form-group
+ = f.label :description
= f.text_area :description, rows: 5, data: { provide: 'markdown' }, class: 'form-control'
- %span.help-block
- = markdown_hint('A description of the conference.')
+ .help-block= markdown_hint('Splash page content')
.form-group
= f.color_field :color, size: 6, class: 'form-control'
%span.help-block
@@ -34,18 +33,27 @@
= image_tag f.object.picture.thumb.url
= f.file_field :picture
%span.help-block
- This will be displayed on the front page.
- = f.hidden_field :picture_cache
- = f.select :ticket_layout, Conference.ticket_layouts.keys, {}, class: 'form-control'
- %span.help-block
- Layout type for tickets of the conference.
+ This will be shown in the navigation bar and emails.
+ .form-group
+ = f.label :custom_css, "Custom CSS"
+ = f.text_area :custom_css, rows: 10, class: 'form-control', html: { style: 'font-family: monospace' }
+ %span.help-block
+ Add custon CSS to all pages within the conference. The class
+ %code .conference-#{@conference.short_title}
+ is included on the body element.
+ .form-group
+ = f.label :ticket_layout
+ = f.select :ticket_layout, Conference.ticket_layouts.keys, {}, class: 'form-control'
+ %span.help-block
+ Layout type for tickets of the conference.
-%h4
- Scheduling
+%h4 Scheduling
%hr
-= f.time_zone_select :timezone, nil, { default: 'UTC' }, { class: 'form-control' }
-%span.help-block
- Please select in what time zone your conference will take place.
+.form-group
+ = f.label :timezone
+ = f.time_zone_select :timezone, nil, { default: 'UTC' }, { class: 'form-control' }
+ %span.help-block
+ Please select in what time zone your conference will take place.
.form-group
= f.label :start_date, "Start Date"
%abbr{title: 'This field is required'} *
@@ -53,43 +61,39 @@
.form-group
= f.label :end_date, "End Date"
%abbr{title: 'This field is required'} *
- = f.text_field :end_date, id: 'conference-end-datepicker', required: true, class: 'form-control'
-- unless f.object.new_record? # We are showing more fields on the edit form
+ = f.text_field :end_date, id: 'conference-end-datepicker', required: true, class: 'form-control'
+- unless f.object.new_record?
.form-group
+ = f.label :start_hour
= f.number_field :start_hour, size: 2, min: 0, max: 23, class: 'form-control'
%span.help-block
= rescheduling_hint(@affected_event_count)
.form-group
+ = f.label :end_hour
= f.number_field :end_hour, size: 2, min: 1, max: 24, class: 'form-control'
%span.help-block
= rescheduling_hint(@affected_event_count)
- %h4
- Registrations
+ %h4 Registrations
%hr
.form-group
+ = f.label :registration_limit
= f.number_field :registration_limit, in: 0..9999, class: 'form-control'
%span.help-block
Limit the number of registrations to the conference (0 no limit). Please note that the registration limit
does not apply to speakers of confirmed events, they will still be able to register even if the limit has been reached.
- You currently have
- = pluralize(@conference.registrations.count, 'registration')
+ You currently have #{pluralize(@conference.registrations.count, 'registration')}.
%h4
Booths
%hr
.form-group
+ = f.label :booth_limit
= f.number_field :booth_limit, in: 0..9999, class: 'form-control'
%span.help-block
- = (t'booth').capitalize
- limit is the maximum number of
- = (t'booth').pluralize
- 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
- = (t'booth').pluralize
- than the conference can accommodate. You currently have
- = pluralize(@conference.booths.accepted.count, "accepted #{t'booth'}") + '.'
+ #{(t'booth').capitalize} limit is the maximum number of #{(t'booth').pluralize}
+ 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 #{(t'booth').pluralize}
+ than the conference can accommodate. You currently have #{pluralize(@conference.booths.accepted.count, "accepted #{t'booth'}")}.
%p.text-right
- if f.object.new_record?
= f.submit nil, { class: 'btn btn-success' }
- else
= f.submit nil, { class: 'btn btn-success', data: { confirm: 'Are you sure you want to proceed?' } }
-
diff --git a/app/views/admin/conferences/_todo_list.html.haml b/app/views/admin/conferences/_todo_list.html.haml
index 88bb7c367..74ddc7961 100644
--- a/app/views/admin/conferences/_todo_list.html.haml
+++ b/app/views/admin/conferences/_todo_list.html.haml
@@ -1,13 +1,14 @@
-.list-group
- %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)}" }
+%ul.list-group
+ .list-group-item
%h4
Conference progress
.progress
- .progress-bar{ 'role' => 'progressbar', 'aria-valuenow' => "#{conference_progress['process']}", 'aria-valuemin' => '0',
- 'aria-valuemax' => '100', 'style' => "width: #{conference_progress['process']}%;" }
+ .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 #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['registration'])}" }
- %span{ 'class' => icon_for_todo(conference_progress['registration']) }
+ %li.list-group-item{ class: class_for_todo(conference_progress['registration']) }
+ %span{ class: icon_for_todo(conference_progress['registration']) }
- if can? :update, @conference
- if conference.registration_period
= link_to 'Set up registration period', edit_admin_conference_registration_period_path(conference_progress['short_title'])
@@ -15,14 +16,14 @@
= 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 #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['cfp'])}" }
- %span{ 'class' => icon_for_todo(conference_progress['cfp']) }
+ %li.list-group-item{ class: 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_cfps_path(conference_progress['short_title'])
- else
Set up call for papers
- %li{'class'=>"list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['venue'])}"}
- %span{'class'=>icon_for_todo(conference_progress['venue'])}
+ %li.list-group-item{ class: class_for_todo(conference_progress['venue']) }
+ %span{ class: icon_for_todo(conference_progress['venue'])}
- if can? :update, Venue.new(conference: @conference)
- @conference.reload
- if conference.venue
@@ -32,32 +33,32 @@
- else
- @conference.reload
Add venue
- %li{ 'class' => "list-group-item #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['rooms'])}" }
- %span{ 'class' => icon_for_todo(conference_progress['rooms']) }
+ %li.list-group-item{ class: 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 #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['tracks'])}" }
- %span{ 'class' => icon_for_todo(conference_progress['tracks']) }
+ %li.list-group-item{ class: 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 #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['event_types'])}" }
- %span{ 'class' => icon_for_todo(conference_progress['event_types']) }
+ %li{ class: "list-group-item #{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 #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['difficulty_levels'])}" }
- %span{ 'class' => icon_for_todo(conference_progress['difficulty_levels']) }
+ %li{ class: "list-group-item #{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 #{hidden_if_conference_over(conference)} #{class_for_todo(conference_progress['splashpage'])}" }
- %span{ 'class' => icon_for_todo(conference_progress['splashpage']) }
+ %li.list-group-item{ class: "#{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'])
- else
diff --git a/app/views/admin/conferences/_top_submitter.html.haml b/app/views/admin/conferences/_top_submitter.html.haml
index 9f220249c..b4f714fd5 100644
--- a/app/views/admin/conferences/_top_submitter.html.haml
+++ b/app/views/admin/conferences/_top_submitter.html.haml
@@ -6,7 +6,7 @@
- @top_submitter.each do |key, value|
.row.top-submitter
.col-md-2
- = image_tag(key.gravatar_url(size: '25'), title: "Yo #{key.name}!", alt: '', 'class' => 'img-circle img-responsive text-center')
+ = image_tag(key.profile_picture(size: '25'), title: "Yo #{key.name}!", alt: '', 'class' => 'img-circle img-responsive text-center')
.col-md-10
%h4
= link_to key.name, admin_user_path(key)
diff --git a/app/views/admin/conferences/show.html.haml b/app/views/admin/conferences/show.html.haml
index 094a56e18..8d164643f 100644
--- a/app/views/admin/conferences/show.html.haml
+++ b/app/views/admin/conferences/show.html.haml
@@ -3,16 +3,16 @@
Dashboard for #{@conference.title}
%hr
.row
- .col-sm-3.col-xs-3
+ .col-sm-3.col-xs-6
= render "big_statistic",
icon: "user", subtitle: "Registration", value: @total_reg, delta: @new_reg
- .col-sm-3.col-xs-3
+ .col-sm-3.col-xs-6
= render "big_statistic",
icon: "square-check", subtitle: "Submission", value: @total_submissions, delta: @new_submissions
- .col-sm-3.col-xs-3
+ .col-sm-3.col-xs-6
= render "big_statistic",
icon: "file-lines", subtitle: "Hour", value: @program_length, delta: @new_program_length
- .col-sm-3.col-xs-3
+ .col-sm-3.col-xs-6
= render "big_statistic",
icon: "box-archive", subtitle: "Withdrawn", value: @total_withdrawn, delta: @new_withdrawn
@@ -29,8 +29,20 @@
= render 'line_chart',
title: 'Submissions per week', data: @submissions
.col-md-4
- = render 'todo_list',
- conference_progress: @conference_progress, conference: @conference
+ - unless @conference.ended?
+ = render 'todo_list',
+ conference_progress: @conference_todo_list, conference: @conference
+ - else
+ .list-group
+ .list-group-item
+ %h4
+ Conference Progress
+ .progress
+ .progress-bar{ role: 'progressbar', 'aria-valuenow': 100,
+ 'aria-valuemin': 0, 'aria-valuemax': 100,
+ style: 'width: 100%;' }
+ Conference Complete!
+
.row#tickets
.col-md-8
= render 'line_chart',
@@ -43,14 +55,15 @@
%a{ href: '#distribution_all', 'data-toggle' => 'tab' }
%span.fa-solid.fa-star
All
- %li
- %a{ href: '#distribution_confirmed', 'data-toggle' => 'tab' }
- %span.fa-solid.fa-comment
- Confirmed
- %li
- %a{ href: '#distribution_withdrawn', 'data-toggle' => 'tab' }
- %span.fa-solid.fa-box-archive
- Withdrawn
+ -# TODO-SNAPCON: Disabled for Snap!Con
+ %li
+ %a{ href: '#distribution_confirmed', 'data-toggle' => 'tab' }
+ %span.fa-solid.fa-comment
+ Confirmed
+ %li
+ %a{ href: '#distribution_withdrawn', 'data-toggle' => 'tab' }
+ %span.fa-solid.fa-box-archive
+ Withdrawn
.tab-content
.tab-pane.active#distribution_all
.row
@@ -63,28 +76,28 @@
.col-md-4
= render 'donut_chart', title: 'Tracks',
combined_data: @tracks_distribution
- .tab-pane#distribution_confirmed
+ -# .tab-pane#distribution_withdrawn
.row
.col-md-4
= render 'donut_chart', title: 'Event types',
- combined_data: @event_type_distribution_confirmed
+ combined_data: @event_type_distribution_withdrawn
.col-md-4
= render 'donut_chart', title: 'Difficulty levels',
- combined_data: @difficulty_levels_distribution_confirmed
+ combined_data: @difficulty_levels_distribution
.col-md-4
= render 'donut_chart', title: 'Tracks',
- combined_data: @tracks_distribution_confirmed
- .tab-pane#distribution_withdrawn
+ combined_data: @tracks_distribution
+ -# .tab-pane#distribution_confirmed
.row
.col-md-4
= render 'donut_chart', title: 'Event types',
- combined_data: @event_type_distribution_withdrawn
+ combined_data: @event_type_distribution_confirmed
.col-md-4
= render 'donut_chart', title: 'Difficulty levels',
- combined_data: @difficulty_levels_distribution_withdrawn
+ combined_data: @difficulty_levels_distribution_confirmed
.col-md-4
= render 'donut_chart', title: 'Tracks',
- combined_data: @tracks_distribution_withdrawn
+ combined_data: @tracks_distribution_confirmed
.row
.col-md-8
@@ -99,11 +112,11 @@
Recent Submissions
.tab-content
.tab-pane.active#recent_reg
- = render partial: 'recent_registrations', locals: {recent_registrations: @recent_registrations}
+ = render 'recent_registrations', recent_registrations: @recent_registrations
.tab-pane#recent_submissions
- = render partial: 'recent_submissions', locals: {recent_events: @recent_events}
+ = render 'recent_submissions', recent_events: @recent_events
.col-md-4
- = render partial: 'top_submitter', locals: {top_submitter: @top_submitter}
+ = render 'top_submitter', top_submitter: @top_submitter
:javascript
$('#recentTable a').click(function (e) {
diff --git a/app/views/admin/currency_conversions/_form.html.haml b/app/views/admin/currency_conversions/_form.html.haml
new file mode 100644
index 000000000..6f052dcd2
--- /dev/null
+++ b/app/views/admin/currency_conversions/_form.html.haml
@@ -0,0 +1,12 @@
+= form_for(@currency_conversion, url: (@currency_conversion.new_record? ? admin_conference_currency_conversions_path : admin_conference_currency_conversion_path(@conference.short_title, @currency_conversion))) do |f|
+ .form-group
+ = f.label :from_currency
+ = f.text_field :from_currency, autofocus: true , required: true, class: 'form-control'
+ .form-group
+ = f.label :to_currency
+ = f.text_field :to_currency, autofocus: true , required: true, class: 'form-control'
+ .form-group
+ = f.label :rate
+ = f.text_field :rate, autofocus: true, required: true, class: 'form-control', type: 'number', step: '0.01'
+ %p.text-right
+ = f.submit nil, class: 'btn btn-primary'
diff --git a/app/views/admin/currency_conversions/edit.html.haml b/app/views/admin/currency_conversions/edit.html.haml
new file mode 100644
index 000000000..69b77e91b
--- /dev/null
+++ b/app/views/admin/currency_conversions/edit.html.haml
@@ -0,0 +1,8 @@
+.row
+ .col-md-12
+ .page-header
+ %h1
+ Edit Currency Conversion
+.row
+ .col-md-8
+ = render partial: 'form'
diff --git a/app/views/admin/currency_conversions/index.html.haml b/app/views/admin/currency_conversions/index.html.haml
new file mode 100644
index 000000000..1d5ef815b
--- /dev/null
+++ b/app/views/admin/currency_conversions/index.html.haml
@@ -0,0 +1,38 @@
+.row
+ .col-md-12
+ .page-header
+ %h1 Currency Conversions
+ %p.text-muted
+ Enter the currency conversions for this conference
+ %p.alert.alert-warning
+ %strong Warning:
+ Currency conversion feature has not been implemented yet. Do not add any currency conversions.
+.row
+ .col-md-12
+ %table.table.table-hover#currency-conversions
+ %thead
+ %tr
+ %th From Curr
+ %th To Curr
+ %th Rate
+ %th Actions
+ %tbody
+ - @conference.currency_conversions.each do |currency_conversion|
+ %tr
+ %td
+ = currency_conversion.from_currency
+ %td
+ = currency_conversion.to_currency
+ %td
+ = currency_conversion.rate
+ %td
+ .btn-group{ role: 'group' }
+ = link_to 'Edit', edit_admin_conference_currency_conversion_path(@conference.short_title, currency_conversion.id),
+ method: :get, class: 'btn btn-primary'
+ = link_to 'Delete', admin_conference_currency_conversion_path(@conference.short_title, currency_conversion.id),
+ method: :delete, class: 'btn btn-danger',
+ data: { confirm: "Do you really want to delete this currency conversion?" }
+
+.row
+ .col-md-12.text-right
+ = link_to 'Add Currency Conversion', new_admin_conference_currency_conversion_path(@conference.short_title), class: 'btn btn-primary'
diff --git a/app/views/admin/currency_conversions/new.html.haml b/app/views/admin/currency_conversions/new.html.haml
new file mode 100644
index 000000000..bcc3543f2
--- /dev/null
+++ b/app/views/admin/currency_conversions/new.html.haml
@@ -0,0 +1,8 @@
+.row
+ .col-md-12
+ .page-header
+ %h1
+ Create Currency Conversion
+.row
+ .col-md-8
+ = render partial: 'form'
diff --git a/app/views/admin/emails/index.html.haml b/app/views/admin/emails/index.html.haml
index e296d824f..f60c4889f 100644
--- a/app/views/admin/emails/index.html.haml
+++ b/app/views/admin/emails/index.html.haml
@@ -33,7 +33,7 @@
'data-body-input-id' => 'email_settings_registration_body',
'data-body-text' => "Dear {name},\n\nThank you for Registering for the conference {conference}.\nPlease complete your registration by filling out your travel information.\n\nIf you are unable to attend please unregister online:\n{registrationlink}\n\nFeel free to contact us with any questions or concerns.\nWe look forward to see you there.\n\nBest wishes\n\n{conference} Team" } Load Template
%a.btn.btn-link.control_label.template_help_link{ 'data-name' => 'registration_help' } Show Help
- = render partial: 'help', locals: { id: 'registration_help', show_event_variables: false }
+ = render partial: 'shared/help', locals: { id: 'registration_help', show_event_variables: false, show_ticket_variables: false, show_ticket_variables: false }
#proposal.tab-pane{ role: 'tabpanel' }
.checkbox
%label
@@ -47,7 +47,7 @@
= f.text_area :submitted_proposal_body, input_html: { rows: 10, cols: 20 }, class: 'form-control'
%a.btn.btn-link.control_label.load_template{ 'data-subject-input-id' => 'email_settings_submitted_proposal_subject', 'data-subject-text' => 'Your proposal has been submitted successfully', 'data-body-input-id' => 'email_settings_submitted_proposal_body', 'data-body-text' => "Dear {name}\n\nThank you for submitting your proposal {eventtitle}.\n\nOur team will review it and get back to you as soon as possible.\n\nFeel free to contact us with any questions or concerns.\n\nBest wishes\n\n{conference} Team" } Load Template
%a.btn.btn-link.control_label.template_help_link{ 'data-name' => 'submitted_proposal_help' } Show Help
- = render partial: 'help', locals: { id: 'submitted_proposal_help', show_event_variables: true }
+ = render partial: 'shared/help', locals: { id: 'submitted_proposal_help', show_event_variables: true, show_ticket_variables: false }
.checkbox
%label
= f.check_box :send_on_accepted, data: { name: 'email_settings_accepted_subject' }, class: 'send_on_radio'
@@ -63,7 +63,7 @@
'data-body-input-id' => 'email_settings_accepted_body',
'data-body-text' => "Dear {name}\n\nWe are very pleased to inform you that your submission {eventtitle} has been accepted for the conference {conference}.\n\nThe public page of your submission can be found at:\n{proposalslink}\nIf you haven´t already registered for {conference}, please do as soon as possible:\n{registrationlink}\n\nFeel free to contact us with any questions or concerns.\n\nWe look 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' => 'accepted_help' } Show Help
- = render partial: 'help', locals: { id: 'accepted_help', show_event_variables: true }
+ = render partial: 'shared/help', locals: { id: 'accepted_help', show_event_variables: true, show_ticket_variables: false }
.checkbox
%label
= f.check_box :send_on_rejected, data: { name: 'email_settings_rejected_subject'}, class: 'send_on_radio'
@@ -79,7 +79,7 @@
'data-body-input-id' => 'email_settings_rejected_body',
'data-body-text' => "Dear {name},\n\nThank you for your submission {eventtitle} for the conference {conference}.\nAfter careful consideration we are sorry to inform you that your submission has been rejected.\n\n\nBest wishes\n\n{conference} Team" } Load Template
%a.btn.btn-link.control_label.template_help_link{ 'data-name' => 'rejected_help' } Show Help
- = render partial: 'help', locals: {id: 'rejected_help', show_event_variables: true}
+ = render partial: 'shared/help', locals: {id: 'rejected_help', show_event_variables: true, show_ticket_variables: false}
.checkbox
%label
= f.check_box :send_on_confirmed_without_registration, data: {name: 'email_settings_confirmed_without_registration_subject'}, class: 'send_on_radio'
@@ -95,7 +95,7 @@
'data-body-input-id' => 'email_settings_confirmed_without_registration_body',
'data-body-text' => "Dear {name},\n\nThank you for the confirmation of {eventtitle}. Unfortunately you are not registered for the conference {conference}. Please register as soon as possible:\n{registrationlink}\n\nFeel free to contact us with any questions or concerns.\n\nWe look 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' => 'confirmed_help' } Show Help
- = render partial: 'help', locals: {id: 'confirmed_help', show_event_variables: true}
+ = render partial: 'shared/help', locals: {id: 'confirmed_help', show_event_variables: true, show_ticket_variables: false}
#notifications.tab-pane{ role: 'tabpanel' }
.checkbox
%label
@@ -112,7 +112,7 @@
'data-body-input-id' => 'email_settings_conference_dates_updated_body',
'data-body-text' => "Dear {name},\n\nThe date of {conference} has changed.\n New Dates : {conference_start_date} - {conference_end_date}.\n\nFeel free to contact us with any questions or concerns.\n\nWe look 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' => 'updated_dates_help' } Show Help
- = render partial: 'help', locals: {id: 'updated_dates_help', show_event_variables: false}
+ = render partial: 'shared/help', locals: {id: 'updated_dates_help', show_event_variables: false, show_ticket_variables: false}
.checkbox
%label
= f.check_box :send_on_conference_registration_dates_updated, data: { name: 'email_settings_conference_registration_dates_updated_subject' }, class: 'send_on_radio'
@@ -128,7 +128,7 @@
'data-body-input-id' => 'email_settings_conference_registration_dates_updated_body',
'data-body-text' => "Dear {name},\n\nThe registration date of {conference} has changed.\n New Dates : {registration_start_date} - {registration_end_date}.\n\nFeel free to contact us with any questions or concerns.\n\nWe look 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' => 'updated_registrations_dates_help' } Show Help
- = render partial: 'help', locals: {id: 'updated_registrations_dates_help', show_event_variables: false}
+ = render partial: 'shared/help', locals: {id: 'updated_registrations_dates_help', show_event_variables: false, show_ticket_variables: false}
.checkbox
%label
= f.check_box :send_on_venue_updated, data: { name: 'email_settings_venue_updated_subject'}, class: 'send_on_radio'
@@ -144,7 +144,7 @@
'data-body-input-id' => 'email_settings_venue_updated_body',
'data-body-text' => "Dear {name},\n\nThe Conference venue of {conference} has changed. New location is: {venue}.\n Address: {venue_address}.\n\nFeel free to contact us with any questions or concerns.\n\nWe look 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' => 'updated_venue_help' } Show Help
- = render partial: 'help', locals: {id: 'updated_venue_help', show_event_variables: false}
+ = render partial: 'shared/help', locals: {id: 'updated_venue_help', show_event_variables: false, show_ticket_variables: false}
#cfp.tab-pane{ role: 'tabpanel' }
.checkbox
%label
@@ -161,7 +161,7 @@
'data-body-input-id' => 'email_settings_program_schedule_public_body',
'data-body-text' => "Dear {name},\n\nThe schedule for {conference} has been announced.\nLink to Schedule {schedule_link}\n\nBest wishes\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}
+ = render partial: 'shared/help', locals: {id: 'updated_cfp_help', show_event_variables: false, show_ticket_variables: false}
.checkbox
%label
= f.check_box :send_on_cfp_dates_updated
@@ -177,7 +177,7 @@
'data-body-input-id' => 'email_settings_cfp_dates_updated_body',
'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}
+ = render partial: 'shared/help', locals: {id: 'updated_cfp_help', show_event_variables: false, show_ticket_variables: false}
#booth.tab-pane{ role: 'tabpanel' }
.checkbox
%label
@@ -194,7 +194,7 @@
'data-body-input-id' => 'email_settings_booths_acceptance_body',
'data-body-text' => "Dear {name},\n\nWe are pleased to inform you that your #{t'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}
+ = render partial: 'shared/help', locals: {id: 'booth_acceptance_help', show_event_variables: false, show_ticket_variables: false}
.checkbox
%label
= f.check_box :send_on_booths_rejection
@@ -210,7 +210,7 @@
'data-body-input-id' => 'email_settings_booths_rejection_body',
'data-body-text' => "Dear {name},\n\nThank you for your #{t'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}
+ = render partial: 'shared/help', locals: {id: 'booth_rejection_help', show_event_variables: false, show_ticket_variables: false, show_ticket_variables: false}
.row
.col-md-12
diff --git a/app/views/admin/event_types/_form.html.haml b/app/views/admin/event_types/_form.html.haml
index 813474c30..d0a2739c1 100644
--- a/app/views/admin/event_types/_form.html.haml
+++ b/app/views/admin/event_types/_form.html.haml
@@ -13,7 +13,8 @@
= @event_type.program.schedule_interval
.form-group
= f.label :description
- = f.text_field :description, class: 'form-control'
+ = f.text_area :description, class: 'form-control', rows: 5, data: { provide: 'markdown' }
+ .help-block= markdown_hint
.form-group
= f.label :minimum_abstract_length
%abbr{title: 'This field is required'} *
@@ -22,6 +23,10 @@
= f.label :maximum_abstract_length
%abbr{title: 'This field is required'} *
= f.number_field :maximum_abstract_length, size: 3, required: true, class: 'form-control'
+ .form-group
+ = f.label :submission_template
+ = f.text_area :submission_template, rows: 5, data: { provide: 'markdown' }
+ .help-block= markdown_hint
.form-group
= f.label :color
= f.color_field :color, class: 'form-control'
diff --git a/app/views/admin/event_types/index.html.haml b/app/views/admin/event_types/index.html.haml
index 36c6dc78e..56411fb78 100644
--- a/app/views/admin/event_types/index.html.haml
+++ b/app/views/admin/event_types/index.html.haml
@@ -11,6 +11,7 @@
%tr
%th Title
%th Description
+ %th Instructions
%th Length
%th Abstract Length
%th Color
@@ -21,7 +22,9 @@
%td
= event_type.title
%td
- = event_type.description
+ = markdown(event_type.description)
+ %td
+ = markdown(event_type.submission_template)
%td
= event_type.length
Minutes
diff --git a/app/views/admin/events/_all_events.xlsx.axlsx b/app/views/admin/events/_all_events.xlsx.axlsx
index 686102c59..44f4deb66 100644
--- a/app/views/admin/events/_all_events.xlsx.axlsx
+++ b/app/views/admin/events/_all_events.xlsx.axlsx
@@ -2,7 +2,7 @@
wb.add_worksheet(name: 'all events') do |sheet|
bold_style = wb.styles.add_style(b: true)
- wrap_text = wb.styles.add_style alignment: {wrap_text: true}
+ wrap_text = wb.styles.add_style alignment: { wrap_text: true }
row = ['Event ID',
'Title',
'Abstract',
@@ -22,7 +22,7 @@ wb.add_worksheet(name: 'all events') do |sheet|
row << event.id
row << event.title
row << event.abstract
- row << (event.time.present? ? "#{event.time.strftime("%Y-%m-%d")} #{event.time.strftime("%I:%M%p")} " : '')
+ row << (event.time.present? ? "#{event.time.strftime('%Y-%m-%d')} #{event.time.strftime('%I:%M%p')} " : '')
row << event.submitter.name
row << event.speaker_names
row << event.speaker_emails
@@ -31,7 +31,7 @@ wb.add_worksheet(name: 'all events') do |sheet|
row << (event.difficulty_level.present? ? event.difficulty_level.title : '')
row << (event.room.present? ? event.room.name : '')
row << event.state
- sheet.add_row row , style: wrap_text
- sheet.column_widths 10,15,35,13,18,18,28,12,15,15,15,10
+ sheet.add_row row, style: wrap_text
+ sheet.column_widths 10, 15, 35, 13, 18, 18, 28, 12, 15, 15, 15, 10
end
end
diff --git a/app/views/admin/events/_all_with_comments.xlsx.axlsx b/app/views/admin/events/_all_with_comments.xlsx.axlsx
index 60e038a6b..8a877c792 100644
--- a/app/views/admin/events/_all_with_comments.xlsx.axlsx
+++ b/app/views/admin/events/_all_with_comments.xlsx.axlsx
@@ -22,13 +22,13 @@ wb.add_worksheet(name: 'events with comments') do |sheet|
@events.each do |event|
all_comments = ''
event.root_comments.each do |comment|
- all_comments << "#{comment.created_at.strftime("%Y-%m-%d")} #{comment.created_at.strftime("%I:%M%p")} #{comment.user.name}: #{comment.body}\n"
+ all_comments << "#{comment.created_at.strftime('%Y-%m-%d')} #{comment.created_at.strftime('%I:%M%p')} #{comment.user.name}: #{comment.body}\n"
end
row = []
row << event.id
row << event.title
row << event.abstract
- row << (event.time.present? ? "#{event.time.strftime("%Y-%m-%d")} #{event.time.strftime("%I:%M%p")} " : '')
+ row << (event.time.present? ? "#{event.time.strftime('%Y-%m-%d')} #{event.time.strftime('%I:%M%p')} " : '')
row << event.submitter.name
row << event.speaker_names
row << event.speaker_emails
@@ -39,6 +39,6 @@ wb.add_worksheet(name: 'events with comments') do |sheet|
row << event.state
row << all_comments.strip
sheet.add_row row, style: cell_style
- sheet.column_widths 10,15,35,13,18,18,28,12,15,15,15,10
+ sheet.column_widths 10, 15, 35, 13, 18, 18, 28, 12, 15, 15, 15, 10
end
end
diff --git a/app/views/admin/events/_confirmed_events.csv.haml b/app/views/admin/events/_confirmed_events.csv.haml
index 2a61c3526..1c0ddb937 100644
--- a/app/views/admin/events/_confirmed_events.csv.haml
+++ b/app/views/admin/events/_confirmed_events.csv.haml
@@ -17,7 +17,7 @@
event.title,
event.abstract,
(event.time.present? ? "#{event.time.strftime("%Y-%m-%d")} #{event.time.strftime("%I:%M%p")} " : ''),
- event.submitter.name,
+ (event.submitter.present? ? event.submitter.name : ''),
event.speaker_names,
event.speaker_emails,
event.event_type.title,
diff --git a/app/views/admin/events/_confirmed_events.xlsx.axlsx b/app/views/admin/events/_confirmed_events.xlsx.axlsx
index bbb7ef821..204bcb5ca 100644
--- a/app/views/admin/events/_confirmed_events.xlsx.axlsx
+++ b/app/views/admin/events/_confirmed_events.xlsx.axlsx
@@ -2,7 +2,7 @@
wb.add_worksheet(name: 'confirmed events') do |sheet|
bold_style = wb.styles.add_style(b: true)
- wrap_text = wb.styles.add_style alignment: {wrap_text: true}
+ wrap_text = wb.styles.add_style alignment: { wrap_text: true }
row = ['Event ID',
'Title',
'Abstract',
@@ -22,7 +22,7 @@ wb.add_worksheet(name: 'confirmed events') do |sheet|
row << event.id
row << event.title
row << event.abstract
- row << (event.time.present? ? "#{event.time.strftime("%Y-%m-%d")} #{event.time.strftime("%I:%M%p")} " : '')
+ row << (event.time.present? ? "#{event.time.strftime('%Y-%m-%d')} #{event.time.strftime('%I:%M%p')} " : '')
row << event.submitter.name
row << event.speaker_names
row << event.speaker_emails
@@ -31,7 +31,7 @@ wb.add_worksheet(name: 'confirmed events') do |sheet|
row << (event.difficulty_level.present? ? event.difficulty_level.title : '')
row << (event.room.present? ? event.room.name : '')
row << event.state
- sheet.add_row row , style: wrap_text
- sheet.column_widths 10,15,35,13,18,18,28,12,15,15,15,10
+ sheet.add_row row, style: wrap_text
+ sheet.column_widths 10, 15, 35, 13, 18, 18, 28, 12, 15, 15, 15, 10
end
end
diff --git a/app/views/admin/events/_datatable_row.haml b/app/views/admin/events/_datatable_row.haml
index 17f7b2645..cdf3a9036 100644
--- a/app/views/admin/events/_datatable_row.haml
+++ b/app/views/admin/events/_datatable_row.haml
@@ -1,7 +1,6 @@
- cache ['admin', conference_id, program, event, current_user] do
%tr{ id: "event-#{event.id}" }
- %td
- = event.id
+ %td= event.id
%td
= link_to event.title,
@@ -34,6 +33,10 @@
.small
No Speaker
+ %td
+ - if event.speakers.any?
+ = volunteer_links(event)
+
- if @program.languages.present?
%td
= event.language
@@ -49,16 +52,19 @@
%td.text-center{ data: { order: event.is_highlight.to_s } }
= event_switch_checkbox(event, :is_highlight, conference_id)
- %td
- = event_type_dropdown(event, event_types, conference_id)
+ - if @program.event_types.any?
+ %td{ data: { search: event.event_type.try(:title) || 'Type' } }
+ = event_type_dropdown(event, event_types, conference_id)
- %td
- = track_dropdown(event, tracks, conference_id)
+ - if @program.tracks.any?
+ %td{ data: { search: event.track.try(:name) || 'Track' } }
+ = track_dropdown(event, tracks, conference_id)
- %td
- = difficulty_dropdown(event, difficulty_levels, conference_id)
+ - if @program.difficulty_levels.any?
+ %td{ data: { search: event.difficulty_level.try(:title) || 'Difficulty Level' } }
+ = difficulty_dropdown(event, difficulty_levels, conference_id)
- %td
+ %td{ data: { search: event.state.humanize } }
= state_dropdown(event, conference_id, email_settings)
%td.text-center
diff --git a/app/views/admin/events/_datatable_row_rating.haml b/app/views/admin/events/_datatable_row_rating.haml
index 8e3e9c29d..e2183c5e3 100644
--- a/app/views/admin/events/_datatable_row_rating.haml
+++ b/app/views/admin/events/_datatable_row_rating.haml
@@ -1,12 +1,11 @@
- if show_votes
- %div{ data: { toggle: 'tooltip' }, title: rating_tooltip(event, max_rating) }<
+ %span{ data: { toggle: 'tooltip' }, title: rating_tooltip(event, max_rating) }<
= rating_stars(event.average_rating, max_rating, avgrate: true)
-.clearfix
- - if event.voted?(current_user)
- %span.label.label-success
- You voted:
- = rating_fraction(event.user_rating(current_user), max_rating)
- - else
- %span.label.label-danger
- Not rated
+- if event.voted?(current_user)
+ %span.label.label-success<>
+ You voted:
+ = rating_fraction(event.user_rating(current_user), max_rating)
+- else
+ %span.label.label-danger<>
+ Not rated
diff --git a/app/views/admin/events/_event_version.haml b/app/views/admin/events/_event_version.haml
new file mode 100644
index 000000000..fc46f08d6
--- /dev/null
+++ b/app/views/admin/events/_event_version.haml
@@ -0,0 +1,32 @@
+%tr
+ %td= version.id
+ %td
+ = link_to_user(version.whodunnit)
+
+ - if version.item_type == 'Event'
+ = event_change_description(version)
+ = "event #{event.title}"
+
+ - elsif version.item_type == 'Vote'
+ = vote_change_description(version)
+ = "event #{event.title}"
+
+ - elsif version.item_type == 'EventUser'
+ = general_change_description(version)
+ = 'event speaker or volunteer'
+
+ - else
+ = general_change_description(version)
+ = link_to 'materials',
+ edit_admin_conference_program_event_path(conference_id: conference.short_title,
+ id: event.id, anchor: 'commercials-content')
+
+ %small.text-muted
+ = distance_of_time_in_words(Time.now, version.created_at) + ' ago'
+ %br
+ = "(#{version.created_at.strftime('%B %-d, %Y %H:%M')})"
+
+ %br
+ = render 'shared/object_changes', version: version
+ %td
+ = render 'shared/changelog_actions', version: version
diff --git a/app/views/admin/events/_form.html.haml b/app/views/admin/events/_form.html.haml
index e3e75f7f2..538d842a6 100644
--- a/app/views/admin/events/_form.html.haml
+++ b/app/views/admin/events/_form.html.haml
@@ -6,38 +6,37 @@
%li.active
= link_to 'Proposal', '#proposal-content', 'data-toggle' => 'tab'
%li
- = link_to 'Commercials', '#commercials-content', 'data-toggle' => 'tab'
+ = link_to 'Materials', '#commercials-content', 'data-toggle' => 'tab'
.tab-content
.tab-pane.active#proposal-content
= form_for(@event, url: @url) do |f|
- = render partial: 'proposals/form', locals: { f: f }
- = render partial: 'shared/user_selectize'
+ = render 'proposals/form', f: f
+ = render 'shared/user_selectize'
.tab-pane#commercials-content
- %p.text-muted
- You can add commercials for your proposal. These commercials will be displayed on the
- = link_to 'public proposal page.', conference_program_proposal_path(@conference.short_title, @event)
- If you don't add a commercial, the conference commercial will be displayed!
- - if can? :create, @event.commercials.new
- .row
- .col-md-6
- #resource-content
- #resource-placeholder{ style: 'background-color:#d3d3d3; float: left; width: 400px; height: 250px; margin: 5px; border-width: 1px; border-style: solid; border-color: rgba(0,0,0,.2);' }
- .row
- .col-md-6
- = form_for(@event.commercials.new, url: conference_program_proposal_commercials_path(conference_id: @conference.short_title, proposal_id: @event)) do |f|
- = render partial: 'proposals/commercial_form_fields', locals: { f: f, commercial: @event.commercials.build }
+ - if @event.persisted?
+ %p.text-muted
+ You can add materals for your proposal. These materials will be displayed on the
+ = link_to 'public proposal page.', conference_program_proposal_path(@conference.short_title, @event)
+ If you don't add any materials, the conference materials will be displayed.
+ - if can? :create, @event.commercials.new
+ .row
+ .col-md-6
+ #resource-content
+ #resource-placeholder{ style: 'background-color:#d3d3d3; float: left; width: 400px; height: 250px; margin: 5px; border-width: 1px; border-style: solid; border-color: rgba(0,0,0,.2);' }
+ .row
+ .col-md-6
+ = form_for(@event.commercials.new, url: conference_program_proposal_commercials_path(conference_id: @conference.short_title, proposal_id: @event)) do |f|
+ = render 'proposals/commercial_form_fields', f: f, commercial: @event.commercials.build
%hr
- @event.commercials.each_slice(3) do |slice|
.row
- slice.each do |commercial|
- if commercial.persisted?
.col-md-4
- .thumbnail
- .flexvideo{ id: "resource-content-#{commercial.id}"}
+ .panel.panel-default
+ %div{ id: "resource-content-#{commercial.id}"}
= render partial: 'shared/media_item', locals: { commercial: commercial }
- .caption
+ .caption.panel-footer
- if can? :update, commercial
= form_for(commercial, url: conference_program_proposal_commercial_path(conference_id: @conference.short_title, proposal_id: @event, id: commercial)) do |f|
- = render partial: 'proposals/commercial_form_fields', locals: { f: f, commercial: commercial }
-
-
+ = render 'proposals/commercial_form_fields', f: f, commercial: commercial
diff --git a/app/views/admin/events/_proposal.html.haml b/app/views/admin/events/_proposal.html.haml
index 17ed991d0..ae50c63d2 100644
--- a/app/views/admin/events/_proposal.html.haml
+++ b/app/views/admin/events/_proposal.html.haml
@@ -7,6 +7,8 @@
%small
= @event.subtitle
.btn-group.pull-right
+ - if @event.public
+ = link_to 'Preview', conference_program_proposal_path(@conference.short_title, @event.id), class: 'btn btn-mini btn-primary'
= link_to 'Registrations', registrations_admin_conference_program_event_path(@conference.short_title, @event), class: 'btn btn-success'
= link_to 'Edit', edit_admin_conference_program_event_path(@conference.short_title, @event), class: 'btn btn-mini btn-primary'
@@ -28,6 +30,22 @@
%b State
%td
= state_dropdown(@event, @conference.short_title, @conference.email_settings)
+ %tr
+ %td
+ %b Is Parent Event?
+ %td
+ = event_switch_checkbox(@event, :superevent, @conference.short_title)
+ %tr
+ %td
+ %b Parent Event
+ %td
+ - if @event.parent_event.present?
+ = link_to(@event.parent_event.title, admin_conference_program_event_path(@conference, @event.parent_event))
+ - else
+ = '(None)'
+ %tr
+ %th Presentation Mode
+ %td= @event.presentation_mode&.humanize
%tr
%td
%b Track
@@ -69,11 +87,12 @@
%td
%b Submitter
%td
- = link_to @event.submitter.name, admin_user_path(@event.submitter)
- - if @event.submitter.email_public
- (
- = link_to @event.submitter.email, "mailto: #{@event.submitter.email}"
- )
+ - if @event.submitter
+ = link_to @event.submitter.name, admin_user_path(@event.submitter)
+ - if @event.submitter.email_public
+ (#{mail_to(@event.submitter.email)})
+ - else
+ %span.text-danger Unkown Submitter
%tr
%td
%b Speakers
@@ -82,9 +101,12 @@
%div
= link_to speaker.name, admin_user_path(speaker)
- if speaker.email_public
- (
- = link_to speaker.email, "mailto: #{speaker.email}"
- )
+ (#{mail_to(speaker.email)})
+ %tr
+ %td
+ %b Volunteers
+ %td
+ = volunteer_links(@event)
%tr
%td
%b Biographies
@@ -106,11 +128,21 @@
%td
%b Abstract
%td= markdown(@event.abstract)
+ %tr
+ %td
+ %b Submission Description
+ %td= markdown(@event.submission_text)
+
%tr
%td
%b Requirements
%td= simple_format(@event.description)
+ %tr
+ %td
+ %b Committee Feedback
+ %td= markdown(@event.committee_review)
+
- if @conference.program.rating_enabled?
= render 'voting',
event: @event,
@@ -137,4 +169,3 @@
= f.text_area :body, class: 'form-control'
.text-right
= f.submit nil, class: 'btn btn-primary'
-
diff --git a/app/views/admin/events/_voting.html.haml b/app/views/admin/events/_voting.html.haml
index e03886632..e0baa0508 100644
--- a/app/views/admin/events/_voting.html.haml
+++ b/app/views/admin/events/_voting.html.haml
@@ -22,20 +22,20 @@
- if voting_period
- max_rating.times do |counter|
- if event.user_rating(current_user) > counter
- = link_to '',
+ = link_to('',
vote_admin_conference_program_event_path(conference_id,
event, rating: counter + 1),
remote: true,
id: "label#{counter + 1}",
class: 'rating myrating bright',
- voted: true
+ voted: true)
- else
- = link_to '',
+ = link_to('',
vote_admin_conference_program_event_path(conference_id,
event, rating: counter + 1),
remote: true,
id: "label#{counter + 1}",
- class: 'rating myrating'
+ class: 'rating myrating')
- else
= rating_stars(event.user_rating(current_user), max_rating, voted: true)
(Voting period is closed)
diff --git a/app/views/admin/events/events.xlsx.axlsx b/app/views/admin/events/events.xlsx.axlsx
index 7b8b1dc49..bc90bd790 100644
--- a/app/views/admin/events/events.xlsx.axlsx
+++ b/app/views/admin/events/events.xlsx.axlsx
@@ -2,9 +2,9 @@
wb = xlsx_package.workbook
if @event_export_option == 'confirmed'
- render partial: 'confirmed_events', locals: {:wb => wb}
+ render partial: 'confirmed_events', locals: { wb: wb }
elsif @event_export_option == 'all'
- render partial: 'all_events', locals: {:wb => wb}
+ render partial: 'all_events', locals: { wb: wb }
elsif @event_export_option == 'all_with_comments'
- render partial: 'all_with_comments', locals: {:wb => wb}
+ render partial: 'all_with_comments', locals: { wb: wb }
end
diff --git a/app/views/admin/events/index.html.haml b/app/views/admin/events/index.html.haml
index bb6db7514..46429af29 100644
--- a/app/views/admin/events/index.html.haml
+++ b/app/views/admin/events/index.html.haml
@@ -6,9 +6,9 @@
= "(#{@events.length})" if @events.any?
.btn-group.pull-right
- %button.btn.btn-primary{ title: 'Mass import of commercials for events',
+ %button.btn.btn-primary{ title: 'Mass import of materials for events',
data: { toggle: 'modal', target: '#mass-commercials-modal' } }
- Add Commercials
+ Add Materials
- if can? :create, Event
= link_to 'Add Event',
@@ -29,7 +29,7 @@
.modal-content
.modal-header
%h1
- Add commercials to events
+ Add materials to events
.modal-body
= form_for('', url: mass_upload_commercials_admin_conference_program_path(@conference.short_title), method: :post) do |f|
.form-group
@@ -59,32 +59,22 @@
%table.datatable
%thead
%tr
- %th
- %b ID
- %th
- %b Title
+ %th ID
+ %th Title
- if @program.rating_enabled?
- %th
- %b Rating
- %th
- %b Submitter
- %th
- %b Speakers
+ %th Rating
+ %th Submitter
+ %th Speakers
- if @program.languages.present?
- %th
- %b Language
- %th
- %b Requires Registration
- %th
- %b Highlight
- %th
- %b Type
- %th
- %b Track
- %th
- %b Difficulty
- %th
- %b State
+ %th Language
+ %th Requires Registration
+ %th Highlight
+ %th Type
+ - if @program.tracks.any?
+ %th Track
+ - if @program.difficulty_levels.any?
+ %th Difficulty
+ %th State
%th
.fa-solid.fa-comment
%th Add Survey
diff --git a/app/views/admin/events/new.html.haml b/app/views/admin/events/new.html.haml
index edf9a1d0f..4dca33846 100644
--- a/app/views/admin/events/new.html.haml
+++ b/app/views/admin/events/new.html.haml
@@ -1,10 +1,3 @@
-.row
- .col-md-12
- .page-header
- %h1
- New Event
-.row
- .col-md-8
- = form_for(@event, url: @url) do |f|
- = render partial: 'proposals/form', locals: { f: f }
- = render partial: 'shared/user_selectize'
+= form_for(@event, url: @url) do |f|
+ = render 'proposals/form', f: f
+ = render 'shared/user_selectize'
diff --git a/app/views/admin/events/show.html.haml b/app/views/admin/events/show.html.haml
index d19d056a5..6cd270117 100644
--- a/app/views/admin/events/show.html.haml
+++ b/app/views/admin/events/show.html.haml
@@ -11,7 +11,7 @@
- progress_status = @event.progress_status
= progress_status.reject{ |_key, value| value || value.nil? }.length
%li
- = link_to 'Commercials', '#proposal-commercials', 'data-toggle' => 'tab'
+ = link_to 'Materials', '#proposal-commercials', 'data-toggle' => 'tab'
.tab-content
#proposal-content.tab-pane.active
@@ -26,42 +26,11 @@
%th Actions
%tbody
- @versions.each do |version|
- %tr
- %td
- = version.id
- %td
- %p
- = link_to_user(version.whodunnit)
-
- - if version.item_type == 'Event'
- = event_change_description(version)
- = "event #{@event.title}"
-
- - elsif version.item_type == 'Vote'
- = vote_change_description(version)
- = "event #{@event.title}"
-
- - else
- = general_change_description(version)
- = link_to 'commercial',
- edit_admin_conference_program_event_path(conference_id: @conference.short_title,
- id: @event.id, anchor: 'commercials-content')
-
- %small.text-muted
- = distance_of_time_in_words(Time.now, version.created_at) + ' ago'
- %br
- = "(#{version.created_at.strftime('%B %-d, %Y %H:%M')})"
-
- %br
- = render partial: 'shared/object_changes', locals: { version: version }
- %td
- = render partial: 'shared/changelog_actions', locals: { version: version }
-
+ = render 'event_version', version: version, event: @event, conference: @conference
#proposal-tasks.tab-pane
- progress_percentage = @event.calculate_progress
.col-md-12
- %h3
- = @event.title
+ %h3= @event.title
%br
%table.table.table-hover
%tr
@@ -83,7 +52,7 @@
%td{ 'class' => class_for_todo(progress_status['subtitle']) }
%span{ 'class' => [icon_for_todo(progress_status['subtitle']), 'fa-lg'] }
%tr
- %td= link_to 'Add a commercial', edit_admin_conference_program_event_path(@event.program.conference.short_title, @event, anchor: 'commercials-content')
+ %td= link_to 'Add materials', edit_admin_conference_program_event_path(@event.program.conference.short_title, @event, anchor: 'commercials-content')
%td{ 'class' => class_for_todo(progress_status['commercials']) }
%span{ 'class' => [icon_for_todo(progress_status['commercials']), 'fa-lg'] }
- unless progress_status['track'].nil?
@@ -95,5 +64,6 @@
%td= link_to 'Add a difficulty level', edit_admin_conference_program_event_path(@event.program.conference.short_title, @event)
%td{ 'class' => class_for_todo(progress_status['difficulty_level']) }
%span{ 'class' => [icon_for_todo(progress_status['difficulty_level']), 'fa-lg'] }
+
#proposal-commercials.tab-pane
= render partial: 'shared/media_items', locals: { commercials: @event.commercials }
diff --git a/app/views/admin/physical_tickets/_physical_ticket.html.haml b/app/views/admin/physical_tickets/_physical_ticket.html.haml
index ff4dd1850..241e88754 100644
--- a/app/views/admin/physical_tickets/_physical_ticket.html.haml
+++ b/app/views/admin/physical_tickets/_physical_ticket.html.haml
@@ -1,8 +1,17 @@
%tr
%td= physical_ticket.id
%td= physical_ticket.ticket.title
- %td= physical_ticket.user.email
+ %td= physical_ticket.ticket.registration_ticket? ? 'Yes' : 'No'
+ %td= physical_ticket.user&.email
%td= humanized_money_with_symbol physical_ticket.ticket_purchase.amount_paid
+ %td
+ - if physical_ticket.ticket_scannings.present?
+ %span Checked in:
+ %br
+ = format_all_timestamps(physical_ticket.ticket_scannings.pluck(:created_at), conference)
+ = form_for(physical_ticket, url: admin_ticket_scanning_path, method: :post) do |f|
+ = f.hidden_field 'token'
+ = f.submit "Mark Present", { class: 'btn btn-success' }
%td
.btn-group
= link_to 'Show',
@@ -13,4 +22,4 @@
conference_physical_ticket_path(conference.short_title,
physical_ticket.token,
format: :pdf),
- class: 'button btn btn-default btn-info'
+ class: 'button btn btn-info'
diff --git a/app/views/admin/physical_tickets/index.html.haml b/app/views/admin/physical_tickets/index.html.haml
index 63e8d202b..80835f901 100644
--- a/app/views/admin/physical_tickets/index.html.haml
+++ b/app/views/admin/physical_tickets/index.html.haml
@@ -21,8 +21,10 @@
%tr
%th ID
%th Type
+ %th Registration?
%th User
%th Paid
+ %th Attedance
%th Actions
%tbody
- @physical_tickets.each do |physical_ticket|
diff --git a/app/views/admin/programs/_form.html.haml b/app/views/admin/programs/_form.html.haml
index 037418bf4..c341dbbe3 100644
--- a/app/views/admin/programs/_form.html.haml
+++ b/app/views/admin/programs/_form.html.haml
@@ -26,6 +26,7 @@
.form-group
= f.label :voting_start_date
= f.text_field :voting_start_date, id: 'datetimepicker-voting_start_date', value: (f.object.voting_start_date.to_formatted_s(:db_without_seconds) unless f.object.voting_start_date.nil?), class: 'form-control'
+ .form-group
= f.label :voting_end_date
= f.text_field :voting_end_date, id: 'datetimepicker-voting_end_date', value: (f.object.voting_end_date.to_formatted_s(:db_without_seconds) unless f.object.voting_end_date.nil?), class: 'form-control'
%h4
@@ -42,7 +43,9 @@
= f.select :languages, I18nData.languages.invert, { include_blank: 'Any Language', include_hidden: false }, { multiple: true, class: 'form-control' }
%span.help-block
The languages allowed for events.
+ %hr
.form-group
+ = f.label :schedule_interval
= f.number_field :schedule_interval, autofocus: true, class: 'form-control'
%span.help-block
It is the minimal time interval of your schedule. The value should be 5, 6, 10, 12, 15, 20, 30 or 60.
diff --git a/app/views/admin/registrations/index.csv.haml b/app/views/admin/registrations/index.csv.haml
index 76f012111..1929abb10 100644
--- a/app/views/admin/registrations/index.csv.haml
+++ b/app/views/admin/registrations/index.csv.haml
@@ -2,15 +2,11 @@
'Name',
'Nickname',
'AffilΚation',
- 'Email',
- 'Arrival',
- 'Departure']
+ 'Email']
= CSV.generate_line headers
- @registrations.each do |registration|
= CSV.generate_line([registration.attended ? 'X' : '',
registration.name,
registration.nickname,
registration.affiliation,
- registration.email,
- registration.arrival.to_s,
- registration.departure.to_s])
+ registration.email])
diff --git a/app/views/admin/registrations/index.html.haml b/app/views/admin/registrations/index.html.haml
index 5dc557b0a..45f66b232 100644
--- a/app/views/admin/registrations/index.html.haml
+++ b/app/views/admin/registrations/index.html.haml
@@ -20,13 +20,14 @@
combined_data: @registration_distribution
.row
.col-md-12
- %div.margin-event-table
+ .margin-event-table
%table.datatable#registrations{ data: { source: admin_conference_registrations_path(conference_id: @conference, format: :json) } }
%thead
%tr
%th{ width: '0' } ID#
%th{ width: '25%' } Name
%th{ width: '0' } E-Mail
+ %th{ width: '0' } Ticket Type
%th{ width: '0' }
%abbr{ title: 'Code of Conduct' } CoC
%th{ width: '0' } Actions
@@ -34,7 +35,7 @@
:javascript
$(function () {
- var codeOfConductPresent = #{@code_of_conduct ? 'true' : 'false'};
+ var codeOfConductPresent = #{@code_of_conduct ? true : false};
var registrationsDataTable = $('#registrations.datatable').DataTable({
"processing": true,
"serverSide": true,
@@ -65,10 +66,13 @@
{
"data": "email"
},
+ {
+ "data": "ticket_type"
+ },
{
"data": "accepted_code_of_conduct",
"className": "code-of-conduct text-center",
- "searchable": false
+ "searchable": false,
},
{
"data": "actions",
@@ -84,5 +88,5 @@
]
});
- registrationsDataTable.columns(3).visible(codeOfConductPresent);
+ registrationsDataTable.columns(4).visible(true);
});
diff --git a/app/views/admin/registrations/index.xlsx.axlsx b/app/views/admin/registrations/index.xlsx.axlsx
index a95c163e5..d9cf24940 100644
--- a/app/views/admin/registrations/index.xlsx.axlsx
+++ b/app/views/admin/registrations/index.xlsx.axlsx
@@ -3,24 +3,24 @@
wb = xlsx_package.workbook
wb.add_worksheet(name: 'registrations') do |sheet|
bold_style = wb.styles.add_style(b: true)
- row = ['Attended', 'Name', 'Nickname', 'AffilΚation', 'Email']
+ row = %w[Attended Name Nickname AffilΚation Email]
@conference.questions.each do |question|
row << question.title
end
- sheet.add_row row, :style => bold_style
+ sheet.add_row row, style: bold_style
@registrations.each do |registration|
row = []
- row << ( registration.attended ? 'X' : '' )
+ row << (registration.attended ? 'X' : '')
row << registration.name
row << registration.nickname
row << registration.affiliation
row << registration.email
@conference.questions.each do |question|
qa = registration.qanswers.find_by(question: question)
- answer = ( qa ? qa.answer.title : '' )
+ answer = (qa ? qa.answer.title : '')
row << answer
end
diff --git a/app/views/admin/reports/_all_events.html.haml b/app/views/admin/reports/_all_events.html.haml
index 7f53357be..d3e86d1ed 100644
--- a/app/views/admin/reports/_all_events.html.haml
+++ b/app/views/admin/reports/_all_events.html.haml
@@ -15,7 +15,7 @@
%th Title
%th Speakers Registered
%th Speakers Biographies
- %th Commercial
+ %th Materials
%th Subtitle
%th Difficulty Level
- if @program.tracks.any?
diff --git a/app/views/admin/reports/_events_without_commercials.html.haml b/app/views/admin/reports/_events_without_commercials.html.haml
index 36292485e..aebcd0e8e 100644
--- a/app/views/admin/reports/_events_without_commercials.html.haml
+++ b/app/views/admin/reports/_events_without_commercials.html.haml
@@ -2,10 +2,10 @@
.col-md-12
.page-header
%h1
- Events without commercials
+ Events Without Materials
= "(#{@events_missing_commercial.length})"
%p.text-muted
- All submissions that have no commercial
+ All submissions that have no materials
.col-md-12
%table.datatable
%thead
diff --git a/app/views/admin/reports/_missing_speakers.html.haml b/app/views/admin/reports/_missing_speakers.html.haml
index f3a482939..2c1fd239a 100644
--- a/app/views/admin/reports/_missing_speakers.html.haml
+++ b/app/views/admin/reports/_missing_speakers.html.haml
@@ -2,7 +2,7 @@
.col-md-12
.page-header
%h1
- Missing Speakers
+ Speaker Registration
= "(#{@missing_event_speakers.distinct(:user_id).length})"
%p.text-muted
All event speakers who haven't checked in
@@ -11,6 +11,7 @@
%thead
%tr
%th Speaker Name
+ %th E-Mail
%th Registered?
%th Event
%th Room
@@ -22,6 +23,7 @@
- event = event_user.event
%tr
%td= link_to speaker.name, admin_user_path(speaker)
+ %td= speaker.email
%td
- if @conference.user_registered?(speaker)
= link_to 'Yes', admin_conference_registrations_path(@conference.short_title)
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index 6bbb88861..6f800dcf9 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -4,7 +4,7 @@
= link_to 'All Events', '#all', 'data-toggle' => 'tab'
%li
%a{href: '#missing-commercial', 'data-toggle' => 'tab'}
- Events without Commercials
+ Events without Materials
%span.label.label-danger{style: 'border-radius: 1em;'}
= @events_missing_commercial.length
%li
@@ -15,7 +15,7 @@
%li
%a{href: '#missing-speakers', 'data-toggle' => 'tab'}
- Missing Speakers
+ Speaker Registration
%span.label.label-danger{style: 'border-radius: 1em;'}
= @missing_event_speakers.distinct(:user_id).length
diff --git a/app/views/admin/rooms/_form.html.haml b/app/views/admin/rooms/_form.html.haml
index a531548a8..7453ce0eb 100644
--- a/app/views/admin/rooms/_form.html.haml
+++ b/app/views/admin/rooms/_form.html.haml
@@ -3,7 +3,21 @@
= f.label :name
%abbr{title: 'This field is required'} *
= f.text_field :name, autofocus: true, required: true, class: 'form-control'
+ .form-group
= f.label :size, 'Capacity'
= f.number_field :size, size: 5, class: 'form-control'
+ .form-group
+ = f.label :order
+ = f.number_field :order, size: 5, class: 'form-control'
+ .help-bloc Force the order of rooms in the schedule.
+ .form-group
+ = f.label :url, 'URL'
+ = f.url_field :url, class: 'form-control'
+ .help-block The URL is only visible to registered attendees.
+ .form-group
+ = f.label :discussion_url, 'Discussion URL'
+ = f.url_field :discussion_url, class: 'form-control'
+ .help-block A link to live chat during sessions in this room.
+
%p.text-right
= f.submit nil, class: 'btn btn-primary'
diff --git a/app/views/admin/rooms/index.html.haml b/app/views/admin/rooms/index.html.haml
index c2040d5ac..95a28fa37 100644
--- a/app/views/admin/rooms/index.html.haml
+++ b/app/views/admin/rooms/index.html.haml
@@ -13,20 +13,32 @@
%tr
%th Name
%th Capacity
+ %th Order
+ %th URL
%th Actions
%tbody
- @rooms.each_with_index do |room, index|
%tr
- %td
+ %th
= room.name
%td
= room.size
%td
- = link_to 'Edit', edit_admin_conference_venue_room_path(@conference.short_title, room.id),
- method: :get, class: 'btn btn-primary'
- = link_to 'Delete', admin_conference_venue_room_path(@conference.short_title, room.id),
- method: :delete, class: 'btn btn-danger',
- data: { confirm: "Do you really want to delete #{room.name}? Attention: This room will be removed from all Events that have it set"}
+ = room.order
+ %td
+ Meeting:
+ = link_to(room.url, room.url, target: '_blank')
+ - if room.discussion_url.present?
+ %br
+ Discussion:
+ = link_to(room.discussion_url, room.discussion_url, target: '_blank')
+ %td
+ .btn-group
+ = link_to 'Edit', edit_admin_conference_venue_room_path(@conference.short_title, room.id), class: 'btn btn-primary'
+ = link_to('Delete',
+ admin_conference_venue_room_path(@conference.short_title, room.id),
+ method: :delete, class: 'btn btn-danger',
+ data: { confirm: "Do you really want to delete #{room.name}? Attention: This room will be removed from all Events that have it set"})
.row
.col-md-12.text-right
= link_to 'Add Room', new_admin_conference_venue_room_path(@conference.short_title), class: 'btn btn-primary'
diff --git a/app/views/admin/schedules/_day_tab.html.haml b/app/views/admin/schedules/_day_tab.html.haml
index 8a1dff600..844dc6ae2 100644
--- a/app/views/admin/schedules/_day_tab.html.haml
+++ b/app/views/admin/schedules/_day_tab.html.haml
@@ -1,13 +1,15 @@
- compact_grid = @program.schedule_interval < 15
- cells_per_hour = 60 / @program.schedule_interval
/ use smaller cell heights for more compact grids
-- cell_height = compact_grid ? 32 : 58
+- cell_height = compact_grid ? (@program.schedule_interval < 8 ? 16 : 32) : 58
- date_event_schedules = @event_schedules.select{ |e| e.start_time.to_date.eql? date }
+/ Dynamically set the column width, but no narrower than 2 (on a BS grid of 12)
+- col_size = [2, (12 / @rooms.count).to_i ].max
.row
- @rooms.each do |room|
- 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) }
+ .col-xs-6{ class: "#{('non_schedulable' if non_schedulable)} col-md-#{col_size}" }
.room-name
- room_date_event_schedules = date_event_schedules.select{ |e| e.room == room }
= room.name
@@ -19,10 +21,11 @@
room_id: room.id, |
hour: time, |
date: date, |
+ class: "#{'compact' if compact_grid}", |
style: "height: #{cell_height}px"}
.div
= time
- event_schedules = room_date_event_schedules.select{ |e| (e.start_time.hour.to_s + e.start_time.strftime(':%M')).eql? time }
- if event_schedules.any?
- event_schedule = event_schedules.first
- = render partial: 'event', locals: { event: event_schedule.event, event_schedule_id: event_schedule.id}
+ = render 'event', event: event_schedule.event, event_schedule_id: event_schedule.id
diff --git a/app/views/admin/schedules/_event.html.haml b/app/views/admin/schedules/_event.html.haml
index 9df56a39a..1d6c46a96 100644
--- a/app/views/admin/schedules/_event.html.haml
+++ b/app/views/admin/schedules/_event.html.haml
@@ -1,29 +1,28 @@
- cells_length = event.event_type.length / @program.schedule_interval
/ this height fits the room cells
- compact_grid = @program.schedule_interval < 15
-- single_cell_height = compact_grid ? 32 : 58
+- single_cell_height = compact_grid ? (@program.schedule_interval < 8 ? 16 : 32) : 58
- height = (cells_length * single_cell_height)
-- height -= 23 unless compact_grid
/ subtracting the padding before calculate the number of lines
-- lines = (height - 7) / 23
-- color = event.track.try(:color).present? ? event.track.try(:color) : 'FFFFFF'
+- lines = [(height - 7) / 20, 1].max
+- color = event.event_type.try(:color).present? ? event.event_type.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)}", |
+.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} #{'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
- %b
- = link_to(event.id, admin_conference_program_event_path(@conference, event))
+ .schedule-event-text{ style: "-webkit-line-clamp: #{lines}; height: #{height - 2}px;"}
+ %span.fa-solid.fa-circle-xmark.schedule-event-delete-button{ onclick: "Schedule.remove(\'event-#{event.id}\');", class: "#{'compact' if compact_grid}" }
+ %b= link_to(event.id, admin_conference_program_event_path(@conference, event))
= event.speakers.collect(&:name).to_sentence
= ':'
= event.title
- - if event.difficulty_level
- %span.label.label-default= event.difficulty_level.title
- - if event.event_type
- %span.label.label-default= event.event_type.title
- - if event.track
- %span.label.label-default= event.track.short_name
+ - unless compact_grid
+ - if event.difficulty_level
+ %span.label.label-default= event.difficulty_level.title
+ - if event.event_type
+ %span.label.label-default= event.event_type.title
+ - if event.track
+ %span.label.label-default= event.track.short_name
diff --git a/app/views/admin/schedules/_form.html.haml b/app/views/admin/schedules/_form.html.haml
index 9304b8e84..a0d585be5 100644
--- a/app/views/admin/schedules/_form.html.haml
+++ b/app/views/admin/schedules/_form.html.haml
@@ -9,4 +9,3 @@
.form-group
= f.select :track, Track.accessible_by(current_ability).where(program: @program).confirmed.pluck(:name, :id), { include_blank: false }, { class: 'form-control' }
= f.submit nil, class: 'btn btn-primary'
-
diff --git a/app/views/admin/schedules/show.html.haml b/app/views/admin/schedules/show.html.haml
index df864b88b..5777f4836 100644
--- a/app/views/admin/schedules/show.html.haml
+++ b/app/views/admin/schedules/show.html.haml
@@ -3,9 +3,10 @@
.row
.col-md-12
.page-header
- %h1 Schedule
+ %h1= "#{@conference.title} Schedule"
%p.text-muted
Create the schedules for the conference
+ = render 'schedules/event_types_key', event_types: @event_types, favourites: false
- if @rooms.present?
.row
@@ -25,17 +26,16 @@
Unscheduled events
.unscheduled-events
- @unscheduled_events.each do |e|
- = render partial: 'event', locals: { event: e, event_schedule_id: nil }
+ = render 'event', event: e, event_schedule_id: nil
.col-md-10
%ul.nav.nav-tabs
- @dates.each do |date|
- %li{ class: "#{ (@dates.first == date) ? 'active' : '' }"}
- %a{ href: "##{date}" }
- = date
+ %li{ class: ('active' if @dates.first == date) }
+ %a{ href: "##{date}" }= "#{date.strftime('%a')} #{date}"
.tab-content
- @dates.each do |date|
- .tab-pane{ class: "#{ (@dates.first == date) ? 'active' : '' }", id: "#{date}" }
- = render partial: 'day_tab', locals: { date: date }
+ .tab-pane{ class: ('active' if @dates.first == date), id: date }
+ = render 'day_tab', date: date
- else
- if @venue.try(:rooms).present?
.text-right
diff --git a/app/views/admin/splashpages/_form.html.haml b/app/views/admin/splashpages/_form.html.haml
index 644561586..338ad4a2e 100644
--- a/app/views/admin/splashpages/_form.html.haml
+++ b/app/views/admin/splashpages/_form.html.haml
@@ -1,4 +1,13 @@
= form_for(@splashpage, url: admin_conference_splashpage_path) do |f|
+ .row
+ .col-md-12
+ %label{for: "banner_photo"} Banner Photo
+ %br
+ - if @splashpage.banner_photo.present?
+ = image_tag @splashpage.banner_photo.thumb.url
+ = f.file_field :banner_photo
+ = f.hidden_field :banner_photo_cache
+
.checkbox
%label
= f.check_box :include_cfp
@@ -11,6 +20,11 @@
%label
= f.check_box :include_tracks
Include confirmed tracks in the program?
+ .checkbox
+ %label
+ = f.check_box :include_happening_now
+ Include events happening now?
+
.checkbox
%label
= f.check_box :include_booths
diff --git a/app/views/admin/splashpages/show.html.haml b/app/views/admin/splashpages/show.html.haml
index 0bf3267c1..c6fa8a70d 100644
--- a/app/views/admin/splashpages/show.html.haml
+++ b/app/views/admin/splashpages/show.html.haml
@@ -11,6 +11,14 @@
Build a splashpage with all the information for your conference
- if @splashpage
+ .row
+ .col-md-12
+ %strong Banner Image
+ - if @splashpage.banner_photo.present?
+ = image_tag @splashpage.banner_photo.thumb.url
+ - else
+ %p None Set
+ %hr
.row
.col-md-12
%ul.fa-ul
@@ -27,6 +35,9 @@
%li
%i{ class: "fa-li #{icon_for_todo @splashpage.include_booths?}" }
Include confirmed #{(t'booth').pluralize}
+ %li
+ %i{ class: "fa-li #{icon_for_todo @splashpage.include_happening_now}" }
+ Include events happening now
%li
%i{ class: "fa-li #{icon_for_todo @splashpage.include_registrations?}" }
Display the registration period
@@ -46,7 +57,7 @@
%i{ class: "fa-li #{icon_for_todo @splashpage.include_social_media?}" }
Display social media links
%li
- - if @conference.splashpage && @conference.splashpage.public
+ - if @conference.splashpage && @conference.splashpage.public?
%i{ class: "fa-li #{icon_for_todo @splashpage.public?}" }
%text-muted.publicorprivate Public
- else
diff --git a/app/views/admin/tickets/_form.html.haml b/app/views/admin/tickets/_form.html.haml
index 54f82c50f..5ad23df1f 100644
--- a/app/views/admin/tickets/_form.html.haml
+++ b/app/views/admin/tickets/_form.html.haml
@@ -4,7 +4,22 @@
%abbr{title: 'This field is required'} *
= f.text_field :title, required: true, autofocus: true, class: 'form-control'
.form-group
+ = f.label :description
= f.text_area :description, rows: 5, data: { provide: 'markdown' }, class: 'form-control'
+ .form-group
+ = f.label :email_subject
+ = f.text_field :email_subject, class: 'form-control'
+ .form-group
+ = f.label :email_body
+ = f.text_area :email_body, rows: 10, cols: 20, class: 'form-control'
+ - email_template = default_ticket_email_template
+ %a.btn.btn-link.control_label.load_template{'data-subject-input-id' => email_template[:subject_input_id],
+ 'data-subject-text' => email_template[:subject_text],
+ 'data-body-input-id' => email_template[:body_input_id],
+ 'data-body-text' => email_template[:body_text]
+ } Load Default Email
+ %a.btn.btn-link.control_label.template_help_link{ 'data-name' => 'accepted_help' } Show Help
+ = render partial: 'shared/help', locals: { id: 'accepted_help', show_event_variables: true, show_ticket_variables: true}
.form-group
= f.label :price
= f.number_field :price, class: 'form-control'
@@ -17,9 +32,16 @@
%span.help-block
Your conference tickets are in
= f.object.conference.tickets.first.price_currency
- .checkbox
- %label
- = f.check_box :registration_ticket
+ .form-group
+ = f.check_box :registration_ticket, class: 'form-check-input'
+ = f.label :registration_ticket, class: 'form-check-label'
+ .help-block
A registration ticket is with which user register for the conference.
+ .form-group
+ = f.check_box :visible, class: 'form-check-input'
+ = f.label :visible, 'Visible?', class: 'form-check-label'
+ .help-block
+ Only visible tickets are available to registrants. Non-visible tickets can only be managed by Admins.
+
%p.text-right
= f.submit nil, class: 'btn btn-primary'
diff --git a/app/views/admin/emails/_help.html.haml b/app/views/admin/tickets/_help.html.haml
similarity index 80%
rename from app/views/admin/emails/_help.html.haml
rename to app/views/admin/tickets/_help.html.haml
index 371bac7b7..0ad816dd9 100644
--- a/app/views/admin/emails/_help.html.haml
+++ b/app/views/admin/tickets/_help.html.haml
@@ -10,13 +10,6 @@
%tr
%td {conference}
%td The full conference title
- - if show_event_variables
- %tr
- %td {proposalslink}
- %td A link to the user's proposal page
- %tr
- %td {eventtitle}
- %td The title of an accepted or rejected proposal
%tr
%td {conference_start_date}
%td The start date of the conference
@@ -51,14 +44,16 @@
- if @conference.program.schedule_public
%td {schedule_link}
%td The link to complete schedule of the conference
- - if @conference.splashpage && @conference.splashpage.public
+ - if @conference.splashpage && @conference.splashpage.public?
%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
+ %tr
+ %td {ticket_quantity}
+ %td The quantity of tickets purchased
+ %tr
+ %td {ticket_title}
+ %td The ticket title
+ %tr
+ %td {ticket_purchase_id}
+ %td The id of the ticket purchase transaction
diff --git a/app/views/admin/tickets/index.html.haml b/app/views/admin/tickets/index.html.haml
index 369adbc10..fc632ac81 100644
--- a/app/views/admin/tickets/index.html.haml
+++ b/app/views/admin/tickets/index.html.haml
@@ -16,6 +16,7 @@
%th Sold
%th Turnover
%th Registration Ticket
+ %th Visible?
%th Actions
%tbody
- @conference.tickets.each do |ticket|
@@ -31,6 +32,8 @@
= humanized_money_with_symbol ticket.tickets_turnover_total(ticket.id)
%td
= ticket.registration_ticket? ? 'Yes' : 'No'
+ %td
+ = ticket.visible? ? 'Yes' : 'No'
%td
.btn-group
= link_to 'Edit', edit_admin_conference_ticket_path(@conference.short_title, ticket.id),
diff --git a/app/views/admin/tickets/show.html.haml b/app/views/admin/tickets/show.html.haml
index 6fa746392..883a7aaa2 100644
--- a/app/views/admin/tickets/show.html.haml
+++ b/app/views/admin/tickets/show.html.haml
@@ -7,7 +7,13 @@
Ticket
%small
= humanized_money_with_symbol @ticket.price
- = link_to 'Edit Ticket', edit_admin_conference_ticket_path, class: 'btn btn-primary pull-right'
+ = link_to 'Edit Ticket', edit_admin_conference_ticket_path, class: 'btn btn-primary pull-right'
+ - if can? :give, Ticket
+ .pull-right
+ = link_to 'Give a Ticket', '#',
+ data: { toggle: 'modal', target: "#modal-give-ticket-#{@ticket.id}" },
+ class: 'button btn btn-default btn-info'
+
%p.text-muted
People who bought this ticket
.row
@@ -15,24 +21,56 @@
%table.datatable
%thead
%tr
- %th #
+ %th ID
%th Name
%th Quantity
%th E-Mail
%th Affiliation
%th Paid
+ %th Date
+ %th
+ %span.sr-only Delete
+ â
%tbody
- @ticket.buyers.each_with_index do |buyer, index|
+ - purchases = buyer.ticket_purchases.where(ticket_id: @ticket.id)
%tr
%td
- = index + 1
+ = purchases.length == 1 ? purchases.first.id : purchases.map(&:id)
%td
= buyer.name
%td
- = buyer.ticket_purchases.where(ticket_id: @ticket.id).sum('quantity')
+ = purchases.sum('quantity')
%td
= buyer.email
%td
= buyer.affiliation
%td
= @ticket.tickets_paid(buyer)
+ %td
+ = purchases.first.created_at
+ %td
+ - if purchases.length == 1
+ = button_to("Delete", conference_ticket_purchase_path(@conference, purchases.first.id), method: :delete, data: {confirm: "Are you sure?"}, class: 'btn btn-danger btn-sm')
+
+- content_for :modals do
+ .modal.fade{ id: "modal-give-ticket-#{@ticket.id}" }
+ .modal-dialog
+ .modal-content
+ = form_for(@ticket.ticket_purchases.new,
+ url: give_admin_conference_ticket_path(@conference, @ticket)) do |f|
+ .modal-header
+ %button.close{ data: { dismiss: 'modal' } }
+ %i.fa.fa-close
+ %h3.modal-title
+ Give a #{@ticket.title} Ticket
+ .modal-body
+ .form-group
+ = f.label :user_id, 'Search Users'
+ = f.select :user_id, [], {}, { multiple: false, class: "select-help-toggle js-userSelector form-control", placeholder: "Search users..." }
+ %span.help-block
+ Search for a user by name, email, or username.
+ .modal-footer
+ = f.submit 'Give Ticket', class: 'btn btn-primary'
+
+= render partial: 'shared/user_selectize'
diff --git a/app/views/admin/tracks/_all_tracks.xlsx.axlsx b/app/views/admin/tracks/_all_tracks.xlsx.axlsx
index 1b9566a50..ee29fb331 100644
--- a/app/views/admin/tracks/_all_tracks.xlsx.axlsx
+++ b/app/views/admin/tracks/_all_tracks.xlsx.axlsx
@@ -2,7 +2,7 @@
wb.add_worksheet(name: 'all tracks') do |sheet|
bold_style = wb.styles.add_style(b: true)
- wrap_text = wb.styles.add_style alignment: {wrap_text: true}
+ wrap_text = wb.styles.add_style alignment: { wrap_text: true }
row = ['Track ID',
'Name',
'Description',
@@ -25,7 +25,7 @@ wb.add_worksheet(name: 'all tracks') do |sheet|
row << track.try(:submitter).try(:name)
row << (track.cfp_active? ? 'Yes' : 'No')
row << track.state
- sheet.add_row row , style: wrap_text
- sheet.column_widths 10,20,50,20,12,12,20,15,10
+ sheet.add_row row, style: wrap_text
+ sheet.column_widths 10, 20, 50, 20, 12, 12, 20, 15, 10
end
end
diff --git a/app/views/admin/tracks/_confirmed_tracks.xlsx.axlsx b/app/views/admin/tracks/_confirmed_tracks.xlsx.axlsx
index 1cb773183..6c3c84eee 100644
--- a/app/views/admin/tracks/_confirmed_tracks.xlsx.axlsx
+++ b/app/views/admin/tracks/_confirmed_tracks.xlsx.axlsx
@@ -2,7 +2,7 @@
wb.add_worksheet(name: 'all tracks') do |sheet|
bold_style = wb.styles.add_style(b: true)
- wrap_text = wb.styles.add_style alignment: {wrap_text: true}
+ wrap_text = wb.styles.add_style alignment: { wrap_text: true }
row = ['Track ID',
'Name',
'Description',
@@ -25,7 +25,7 @@ wb.add_worksheet(name: 'all tracks') do |sheet|
row << track.try(:submitter).try(:name)
row << (track.cfp_active? ? 'Yes' : 'No')
row << track.state
- sheet.add_row row , style: wrap_text
- sheet.column_widths 10,20,50,20,12,12,20,15,10
+ sheet.add_row row, style: wrap_text
+ sheet.column_widths 10, 20, 50, 20, 12, 12, 20, 15, 10
end
end
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index 51a50069a..12f44433e 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -14,9 +14,10 @@
url: "#{toggle_confirmation_admin_user_path(@user.id)}?user[to_confirm]=",
class: 'switch-checkbox',
readonly: true
- .checkbox
+ .form-group.switch
%label
- = f.check_box :is_admin
+ = f.check_box :is_admin,
+ class: 'switch-checkbox'
Is admin
%span.help-block An admin can create a new conference, manage users and make other users admins.
.form-group
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 2556f090e..03f5684f0 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -16,15 +16,17 @@
%th{ width: '0' } ID
%th{ width: '0' } Confirmed?
%th{ width: '0' } Email
- %th{ width: '50%' } Name
- %th{ width: '0' } Conferences Attended
+ %th{ width: '30%' } Name
+ %th{ width: '20%' } Conferences Attended
%th{ width: '50%' } Roles
%th{ width: '0' } Actions
+ %th{ style: 'display: none' } Confirmed?
%tbody
:javascript
$(function () {
$('#users.datatable').DataTable({
+ "buttons": { buttons: ["csv"] },
"processing": true,
"serverSide": true,
"ajax": $('#users.datatable').data('source'),
@@ -36,6 +38,7 @@
{
"data": "confirmed_at",
"render": function (data, type, row, meta) {
+ console.log(meta)
return ''+data+''
}
},
+ { "data": "email" },
+ { "data": "username" },
{ "data": "attended" },
{
"data": "roles",
@@ -71,6 +75,11 @@
'Edit'+
'';
}
+ },
+ {
+ "data": "confirmed",
+ "className": 'hidden',
+ "render": false
}
]
});
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index c9048be4e..c0a8f1fb5 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -12,8 +12,10 @@
.tab-content
#user-info-content.tab-pane{class: "#{'active' unless params[:tab] == 'submissions-content'}"}
- if can? :edit, @user
- .pull-right
+ .pull-right.btn-group
= link_to 'Edit', edit_admin_user_path(@user), class: 'btn btn-primary'
+ = link_to 'Delete', admin_user_path(@user),method: :delete, class: 'btn btn-danger',
+ data: {confirm: "Are you sure?"}
%table.table
- @show_attributes.each do |attr|
%tr
@@ -26,6 +28,9 @@
- elsif attr == 'biography'
%td
= markdown(@user.biography)
+ - elsif attr == 'profile_picture'
+ %td
+ = image_tag @user.profile_picture(size: '100'), alt: ''
- elsif attr == 'email'
%td
= @user.send(attr)
diff --git a/app/views/admin/venues/_form.html.haml b/app/views/admin/venues/_form.html.haml
index 960b01e4c..d7fca0db4 100644
--- a/app/views/admin/venues/_form.html.haml
+++ b/app/views/admin/venues/_form.html.haml
@@ -7,7 +7,7 @@
%li.active
= link_to 'Details', '#details-content', 'data-toggle' => 'tab'
%li
- = link_to 'Commercials', '#commercials-content', 'data-toggle' => 'tab'
+ = link_to 'Materials', '#commercials-content', 'data-toggle' => 'tab'
.tab-content
#details-content.tab-pane.active
@@ -26,7 +26,7 @@
%span.help-block
= markdown_hint
.form-group
- = f.label 'Logo'
+ = f.label :picture, 'Logo'
%br
- if @venue.picture?
= image_tag @venue.picture.thumb.url
@@ -38,8 +38,10 @@
= f.label :street
%abbr{title: 'This field is required'} *
= f.text_field :street, required: true, class: 'form-control'
+ .form-group
= f.label :postalcode
= f.text_field :postalcode, class: 'form-control'
+ .form-group
= f.label :city
%abbr{title: 'This field is required'} *
= f.text_field :city, required: true, class: 'form-control'
@@ -55,7 +57,7 @@
= f.submit nil, class: 'btn btn-primary'
#commercials-content.tab-pane
- - if can? :create, @venue.commercial and @venue.id
+ - if can?(:create, @venue.commercial) and @venue.id
- if @venue.commercial.nil?
.row
.col-md-6
@@ -66,31 +68,18 @@
.col-md-6
= form_for(Commercial.new,as: :commercial, url: admin_conference_venue_venue_commercial_path(conference_id: @conference.short_title)) do |f|
.form-group
+ = f.label :title
+ = f.text_field :title, class: 'form-control'
= f.label :url
= f.url_field :url, class: 'form-control', required: 'required'
%span.help-block
- Just paste the url of your video/photo provider. Currently supported: YouTube, Vimeo, SpeakerDeck, SlideShare, Instagram, Flickr.
+ Just paste the url of your video/photo provider. Anything that supports an iframe is allowed.
= f.submit nil, class: 'btn btn-primary pull-right', id: 'commercial_submit_action', disabled: true
%hr
- else
- - if @venue.commercial.persisted?
- .col-md-4
- .thumbnail
- .flexvideo{ id: "resource-content-#{@venue.commercial.id}"}
- = render partial: 'shared/media_item', locals: { commercial: @venue.commercial }
- .caption
- - if can? :update, @venue.commercial
- = form_for @venue.commercial, as: :commercial, url: admin_conference_venue_venue_commercial_path(conference_id: @conference.short_title, id: @venue.commercial.id) do |f|
- .form-group
- = f.label :url
- = f.url_field :url, id: "commercial_url_#{@venue.commercial.id}", class: 'form-control', required: 'required'
- %span.help-block
- Just paste the url of your video/photo provider. Currently supported: YouTube, Vimeo, SpeakerDeck, SlideShare, Instagram, Flickr.
- = f.submit nil, class: 'btn btn-success'
- - if can? :destroy, @venue.commercial
- = link_to 'Delete', admin_conference_venue_venue_commercial_path(conference_id: @conference.short_title, id: @venue.commercial.id),
- method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger'
- %hr
-
+ .col-md-4
+ %div{ id: "resource-content-#{@venue.commercial.id}"}
+ = render partial: 'shared/media_item', locals: { commercial: @venue.commercial }
+ = render 'admin/commercials/update_form', form_url: admin_conference_venue_venue_commercial_path(conference_id: @conference.short_title, id: @venue.commercial.id), conference: @conference, commercial: @venue.commercial
- else
- First Create Venue, then update commercial
+ First Create Venue, then update materials
diff --git a/app/views/admin/venues/show.html.haml b/app/views/admin/venues/show.html.haml
index 36906e05c..de29c4e3a 100644
--- a/app/views/admin/venues/show.html.haml
+++ b/app/views/admin/venues/show.html.haml
@@ -12,7 +12,7 @@
-else
- if @venue.commercial.nil?
.row
- %img{ "data-src" => "holder.js/500x300?text=No Commercial Set", class: 'img-responsive img-rounded' }
+ %img{ "data-src" => "holder.js/500x300?text=No Materials Set", class: 'img-responsive img-rounded' }
- else
- if @venue.commercial.persisted?
.thumbnail
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 1728b999e..6870af950 100644
--- a/app/views/admin/versions/_object_desc_and_link.html.haml
+++ b/app/views/admin/versions/_object_desc_and_link.html.haml
@@ -15,18 +15,14 @@
- role = current_or_last_object_state('Role', object.role_id)
- role_name = role.try(:name) || PaperTrail::Version.where(item_type: 'Role', item_id: object.role_id).last.changeset[:name].second
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
+ - if version.conference_id
- 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
+ - elsif version.organization_id
+ - organization = Organization.find(version.organization_id)
+ = link_if_alive version, role_name,
+ admins_admin_organization_path(organization), organization
= version.event == 'create' ? 'to' : 'from'
user
@@ -79,8 +75,9 @@
= link_to (current_or_last_object_state('Event', object.event_id).try(:title) || 'deleted'),
admin_conference_program_event_path(conference_short_title, object.event_id)
in
- = link_to "Schedule #{version.item.schedule_id}",
- admin_conference_schedule_path(conference_short_title, version.item.schedule_id)
+ - if version.item
+ = link_to "Schedule #{version.item&.schedule_id}",
+ admin_conference_schedule_path(conference_short_title, version.item&.schedule_id)
- when 'Schedule'
= link_if_alive version, "Schedule #{version.item_id}",
@@ -133,19 +130,15 @@
- when 'Role'
role
- role_name = object.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
+ - if version.conference_id
- 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
+ - elsif version.organization_id
+ - organization = Organization.find(version.organization_id)
+ = link_if_alive version, role_name,
+ admins_admin_organization_path(organization), organization
- when 'Venue'
venue
@@ -203,20 +196,16 @@
= link_to_user(version.item_id)
- 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)
- - elsif version.item_type == 'Commercial'
+ - if version.item_type == '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)
- unless commercial.commercialable_type == 'Conference'
in conference
= link_to_conference(version.conference_id)
+ - elsif version.organization_id
+ - organization = Organization.find(version.organization_id)
+ in organization
+ = link_to_organization(version.organization_id)
- else
in conference
= link_to_conference(version.conference_id)
diff --git a/app/views/application/_big_statistic.haml b/app/views/application/_big_statistic.haml
index c45a17f15..d767a0b19 100644
--- a/app/views/application/_big_statistic.haml
+++ b/app/views/application/_big_statistic.haml
@@ -1,13 +1,13 @@
- reverse ||= false
- variant = variant_from_delta(delta, reverse: reverse)
-.dashbox.text-center
- %span
- = icon('fa-solid', icon)
- = value
- %p.text-nowrap
- %small
- = subtitle.pluralize(value)
- %span.label{ class: "label-#{variant}" }
- = delta
- since you last logged in
-
+.dashbox.panel.panel-primary
+ .paenl-body.text-center
+ %span
+ = icon('fa-solid', icon)
+ = value
+ %p.text-nowrap
+ %small
+ = subtitle.pluralize(value)
+ .label{ class: "label-#{variant}" }
+ = delta || 0
+ since last log in
diff --git a/app/views/booths/index.html.haml b/app/views/booths/index.html.haml
index 85f847b44..ed5628b5a 100644
--- a/app/views/booths/index.html.haml
+++ b/app/views/booths/index.html.haml
@@ -48,5 +48,6 @@
= 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 #{(t'booth').capitalize }", new_conference_booth_path(@conference.short_title), class: 'button btn btn-primary'
diff --git a/app/views/conference_registrations/_event.html.haml b/app/views/conference_registrations/_event.html.haml
new file mode 100644
index 000000000..dd389f665
--- /dev/null
+++ b/app/views/conference_registrations/_event.html.haml
@@ -0,0 +1,39 @@
+.panel.panel-default{ class: ('panel-success' if event.registrations.include?(registration)) }
+ .panel-heading
+ %label{ for: "registration_event_ids_#{event.id}" }
+ %h3{ style: 'margin: 0 auto;' }
+ = hidden_field_tag 'registration[event_ids][]', nil
+ = check_box_tag 'registration[event_ids][]', event.id, event.registrations.include?(registration), id: "registration_event_ids_#{event.id}"
+ = event.title
+ %small
+ = event.subtitle
+ .text-muted
+ = registered_text(event)
+ - if event.scheduled?
+ (Scheduled on: #{event.time.to_date})
+
+ .panel-body
+ -#
+ %p
+ = canceled_replacement_event_label(event, event_schedule)
+ = replacement_event_notice(event_schedule)
+ %p
+ - if event.speakers.any?
+ presented by #{event.speaker_names}
+ - if event_schedule.present?
+ .h4.track
+ %span.fa.fa-clock-o
+ %span.label{ style: 'background-color: grey' }
+ = event_schedule.start_time.strftime('%A, %B %-d %H:%M')
+ \-
+ = event_schedule.end_time.strftime('%H:%M')
+ %p
+ = markdown(truncate(event.abstract, length: 250))
+ -# TODO: More informative text or aria-label.
+ = link_to 'more', conference_program_proposal_path(conference.short_title, event.id), target: '_blank'
+
+ - if event.track
+ %span.track
+ %span.fa.fa-road
+ %span.label{ style: "background-color: #{event.track.color}; color: #{contrast_color(event.track.color)}" }
+ = event.track.name
diff --git a/app/views/conference_registrations/_registration_info.html.haml b/app/views/conference_registrations/_registration_info.html.haml
index e8293d549..a952c067c 100644
--- a/app/views/conference_registrations/_registration_info.html.haml
+++ b/app/views/conference_registrations/_registration_info.html.haml
@@ -17,6 +17,11 @@
%h4
Pre-registration required for the following:
- @registration.events_ordered.each do |event|
+ %p
+ You are registered for #{pluralize(@registration.events.count, 'event')}.
+ They are at the end of this list.
+ - @registration.events_ordered.each do |event|
+ = render 'conference_registrations/event', event: event, event_schedule: event.event_schedules.first, conference: @conference, registration: @registration
%label
= hidden_field_tag "registration[event_ids][]", nil
= check_box_tag "registration[event_ids][]", event.id, event.registrations.include?(@registration)
diff --git a/app/views/conference_registrations/show.html.haml b/app/views/conference_registrations/show.html.haml
index c419d5359..9d89752ab 100644
--- a/app/views/conference_registrations/show.html.haml
+++ b/app/views/conference_registrations/show.html.haml
@@ -78,7 +78,7 @@
= link_to event.title, conference_program_proposal_path(@conference.short_title, event.id)
= '(' + registered_text(event) + ')'
- - if @conference.tickets.any?
+ - if @conference.tickets.visible.any?
.row
.col-md-12
%h4
@@ -151,7 +151,7 @@
Registered
= word_pluralize(@conference.participants.count, 'Attendee')
- @conference.participants.each do |participant|
- = link_to image_tag(participant.gravatar_url(size: '25'), title: "#{participant.name}!", class: 'img-circle'), user_path(participant)
+ = link_to image_tag(participant.profile_picture(size: '25'), title: "#{participant.name}!", class: 'img-circle'), user_path(participant)
.col-md-4.col-md-offset-2
- if @conference.program.speakers.confirmed.any?
%h4
@@ -162,4 +162,4 @@
Confirmed
= word_pluralize(@conference.program.speakers.confirmed.count, 'Speaker')
- @conference.program.speakers.confirmed.each do |speaker|
- = link_to image_tag(speaker.gravatar_url(size: '25'), title: "#{speaker.name}!", class: 'img-circle'), user_path(speaker)
+ = link_to image_tag(speaker.profile_picture(size: '25'), title: "#{speaker.name}!", class: 'img-circle'), user_path(speaker)
diff --git a/app/views/conferences/_about_and_happening_now.haml b/app/views/conferences/_about_and_happening_now.haml
new file mode 100644
index 000000000..239cbbce4
--- /dev/null
+++ b/app/views/conferences/_about_and_happening_now.haml
@@ -0,0 +1,34 @@
+= content_for :happening_now do
+ #happening-now
+ = render 'happening_now', conference: conference,
+ events_schedules: events_schedules, pagy: pagy,
+ events_schedules_length: events_schedules_length,
+ events_schedules_limit: events_schedules_limit,
+ is_happening_next: is_happening_next
+
+= content_for :about do
+ #about
+ = markdown(conference.description, false)
+
+%section#about-and-happening-now
+ .container
+ .row
+ .col-md-12
+ = yield :additional_messages
+ .row
+ -# happening now events are displayed second in md or lg view
+ - if conference.splashpage.include_happening_now && conference.splashpage.include_program?
+ - if conference.description.present?
+ .col-md-6.col-md-push-6.col-lg-4.col-lg-push-8
+ = yield :happening_now
+ - else
+ .col-md-12
+ = yield :happening_now
+ - if conference.description.present?
+ - if conference.splashpage.include_happening_now && conference.splashpage.include_program
+ .col-md-6.col-md-pull-6.col-lg-8.col-lg-pull-4
+ = yield :about
+ - else
+ .col-md-12
+ = yield :about
+ .trapezoid
diff --git a/app/views/conferences/_call_for_content.haml b/app/views/conferences/_call_for_content.haml
index 8287e9119..58e7bfef7 100644
--- a/app/views/conferences/_call_for_content.haml
+++ b/app/views/conferences/_call_for_content.haml
@@ -1,6 +1,6 @@
= content_for :splash_nav do
%li
- %a.smoothscroll{ href: '#call' } Call For Content
+ %a.smoothscroll{ href: '#call' } Proposals
%section#call
.container
@@ -20,3 +20,4 @@
call: call_for_booths
- if two_calls_open(call_for_events, call_for_tracks, call_for_booths)
.col-md-2.col-sm-2.hidden-xs
+ .trapezoid
diff --git a/app/views/conferences/_call_for_papers.haml b/app/views/conferences/_call_for_papers.haml
index 4847a9928..d0d513ff7 100644
--- a/app/views/conferences/_call_for_papers.haml
+++ b/app/views/conferences/_call_for_papers.haml
@@ -2,7 +2,7 @@
expires_in: 1.hour) do
.col-md-4.col-sm-4.text-center
%h2
- Call for Papers
+ Call for Participation
%p.lead
We are now accepting proposals for sessions!
%p
diff --git a/app/views/conferences/_conference_details.html.haml b/app/views/conferences/_conference_details.html.haml
index a79d31e14..b860d609a 100644
--- a/app/views/conferences/_conference_details.html.haml
+++ b/app/views/conferences/_conference_details.html.haml
@@ -2,11 +2,13 @@
.col-md-12
.well
.row
- .col-md-4.text-center
- = image_tag(conference.picture_url, class: 'img-responsive') if conference.picture?
- .col-md-6
+ .col-md-10
+ .row
+ .col-md-5
+ .col-md-2
+ = image_tag(conference.picture_url, class: 'img-responsive') if conference.picture?
%h3
- = conference.title
+ = conference.title.html_safe
%small
%b
= date_string(conference.start_date, conference.end_date)
@@ -18,14 +20,14 @@
%span>= conference.country_name
- unless conference.description.blank?
%p
- = markdown(conference.description)
+ = markdown(truncate(conference.description, length: 1000, separator: "\n", escape: false), escape_html=false)
.col-md-2
.btn-group-vertical
- if !@conference || @conference != conference
- if conference.splashpage && conference.splashpage.public
= link_to "View Conference", conference_path(conference.short_title), class: 'btn btn-default'
- if conference.program and conference.program.schedule_public
- = link_to "Schedule", conference_schedule_path(conference.short_title), class: 'btn btn-default'
+ = link_to "Schedule", vertical_schedule_conference_schedule_path(conference.short_title), class: 'btn btn-default'
- unless conference.code_of_conduct.blank?
= link_to "Code of Conduct",
[:code_of_conduct, conference.organization],
diff --git a/app/views/conferences/_footer.haml b/app/views/conferences/_footer.haml
index cba8f844b..1e6f1b0ed 100644
--- a/app/views/conferences/_footer.haml
+++ b/app/views/conferences/_footer.haml
@@ -3,12 +3,12 @@
%i.fa-solid.fa-2x.fa-circle-arrow-up
:javascript
- $(function(){
- $(document).on( 'scroll', function(){
- if ($(window).scrollTop() > 100) {
+ $(function() {
+ $(document).on( 'scroll', function() {
+ if ($(window).scrollTop() > 100) {
$('.scroll-top-wrapper').addClass('show');
- } else {
+ } else {
$('.scroll-top-wrapper').removeClass('show');
- }
+ }
});
});
diff --git a/app/views/conferences/_gallery.html.haml b/app/views/conferences/_gallery.html.haml
index 696354025..791f47365 100644
--- a/app/views/conferences/_gallery.html.haml
+++ b/app/views/conferences/_gallery.html.haml
@@ -1,17 +1,14 @@
-
-%div{ class: "modal fade", id: "gallery", tabindex: "-1", role: "dialog", "aria-labelledby" => "myLargeModalLabel", "aria-hidden"=> "true" }
- %div{ class: "modal-dialog modal-lg" }
- %div{ class: "modal-content" }
- %div{ class: "modal-body" }
- %div{ id: "carousel-example-generic", class: "carousel slide", "data-ride" => "carousel" }
-
- %div{ class: "carousel-inner" }
+.modeal.fade#gallery{ tabindex: "-1", role: "dialog", "aria-labelledby": "myLargeModalLabel", "aria-hidden": true }
+ .modal-dialog.modal-lg
+ .modal-content
+ .modal-body
+ .carousel.slide#carousel-example-generic{ "data-ride": "carousel" }
+ .carousel-inner
-
- %a{ class: "left carousel-control", href: "#carousel-example-generic", role: "button", "data-slide" => "prev" }
- %span.fa-solid.fa-chevron-left
- %a{ class: "right carousel-control", href: "#carousel-example-generic", role: "button", "data-slide" => "next" }
- %span.fa-solid.fa-chevron-right
+ %a.left.carousel-control{ href: "#carousel-example-generic", "data-slide": "prev" }
+ %span.fa-solid.fa-chevron-left{"aria-label": 'Previous Item'}
+ %a.right.carousel-control{ href: "#carousel-example-generic", "data-slide": "next" }
+ %span.fa-solid.fa-chevron-right{'aria-label': 'Next Item'}
:javascript
@@ -20,7 +17,7 @@
$('#gallery-btn').click(function(){
if(count == 0){
$('#gallery').modal('show');
- $('#gallery .modal-body').append('');
+ $('#gallery .modal-body').append('');
count +=1;
}
else{
diff --git a/app/views/conferences/_happening_now.haml b/app/views/conferences/_happening_now.haml
new file mode 100644
index 000000000..cf7f6f937
--- /dev/null
+++ b/app/views/conferences/_happening_now.haml
@@ -0,0 +1,20 @@
+- if events_schedules.present? && events_schedules.any?
+ .row
+ %h3.text-left{ style: 'margin-bottom:30px; padding-left:20px' }
+ - if is_happening_next
+ Upcoming Events
+ - else
+ Events Happening Now
+ - events_schedules.each do |event_schedule|
+ = render 'schedules/event_mini', conference: conference, event_schedule: event_schedule, event: event_schedule.event
+ - if events_schedules_length > events_schedules_limit
+ .container{ style: 'width:100%; text-align:center' }
+ != pagy_bootstrap_nav_js(pagy)
+- else
+ .row
+ %h3.text-center There are no upcoming events.
+
+:javascript
+ $(document).ready(function(){
+ updateFavouriteStatus({ events: #{@favourited_events || []}, loggedIn: #{current_user.present?} });
+ });
diff --git a/app/views/conferences/_header.haml b/app/views/conferences/_header.haml
index cc9ffa8fb..72d634896 100644
--- a/app/views/conferences/_header.haml
+++ b/app/views/conferences/_header.haml
@@ -1,34 +1,28 @@
- cache [conference, venue, '#splash#header'] do
- #banner
+ #banner{ style: ("background-image: url(#{splashpage.banner_photo_url})" if splashpage.banner_photo_url) }
.container
.row
- .col-md-8.col-md-offset-2#header
+ - picture_present = splashpage.banner_photo? || conference.picture?
+ .col-md-6.col-md-offset-3{ id: (picture_present ? "header-image" : "header-no-image") }
.row
- .col-md-4
- - if conference.picture?
+ - if conference.picture? && !splashpage.banner_photo?
+ .col-md-4
= image_tag(conference.picture_url,
class: 'img-responsive img-center',
id: 'splash-logo')
.col-md-8
%h1
- = conference.title
+ = conference.title.html_safe
%h3
- if conference.start_date && conference.end_date
%span.date.text-nowrap
= date_string(conference.start_date, conference.end_date)
- if conference.venue
%span.venue.text-nowrap
- - if venue.website
+ - if venue.website.present?
= sanitize link_to(venue.name, venue.website)
- else
= venue.city
- - if venue.country_name
+ - if venue.country != 'US'
•
= venue.country_name
-
- - unless conference.description.blank?
- %section#about
- .container
- .row
- .col-md-8.col-md-offset-2
- = markdown(conference.description)
diff --git a/app/views/conferences/_highlights.haml b/app/views/conferences/_highlights.haml
index 2bb5b3833..b457aebc8 100644
--- a/app/views/conferences/_highlights.haml
+++ b/app/views/conferences/_highlights.haml
@@ -12,7 +12,7 @@
event),
class: 'thumbnail') do
- if speaker
- = image_tag speaker.gravatar_url(size: 300),
+ = image_tag speaker.profile_picture(size: 300),
class: ['img-responsive', 'img-circle'],
title: speaker.name
.caption
diff --git a/app/views/conferences/_lodging.haml b/app/views/conferences/_lodging.haml
index 768f75d1a..8c48d3555 100644
--- a/app/views/conferences/_lodging.haml
+++ b/app/views/conferences/_lodging.haml
@@ -1,7 +1,3 @@
-= content_for :splash_nav do
- %li
- %a.smoothscroll{ href: '#lodging' } Lodging
-
- cache [venue, lodgings, '#splash#lodging'] do
%section#lodging
.container
@@ -17,25 +13,22 @@
.row.row-centered
- lodgings.each do |lodging|
- .col-md-4.col-sm-4.col-centered.col-top
+ .col-md-6.col-sm-4.col-centered.col-top
.thumbnail
- if lodging.picture?
- if lodging.website_link.present?
- = link_to(lodging.website_link, class: 'thumbnail') do
+ = link_to(lodging.website_link) 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
- %i.fa-solid.fa-house.fa-5x
- - else
- %i.fa-solid.fa-house.fa-5x
.caption
%h3.text-center
- = lodging.name
+ - if lodging.website_link.present?
+ = link_to(lodging.name, lodging.website_link)
+ - else
+ = lodging.name
- if lodging.description.present?
= markdown(lodging.description)
+ .trapezoid
diff --git a/app/views/conferences/_program.haml b/app/views/conferences/_program.haml
index 2ab2ef709..7d5f93d8e 100644
--- a/app/views/conferences/_program.haml
+++ b/app/views/conferences/_program.haml
@@ -1,16 +1,18 @@
= content_for :splash_nav do
%li
- %a.smoothscroll{ href: '#program' } Program
+ = link_to('Schedule', events_conference_schedule_path(conference))
+ %li
+ %a.smoothscroll{ href: '#program' } Featured
-- cache [conference, highlights, tracks, booths, '#splash#program'] do
+- cache [conference, conference.program, highlights, tracks, booths, '#splash#program'] do
%section#program
.container
.row
.col-md-12
%p.lead.text-center
%span.notranslate
- = conference.title
- has the most awesome program ever!
+ = conference.title.html_safe
+ will have an engaging program!
- unless highlights.blank?
= render 'highlights', conference_id: conference.short_title,
@@ -28,9 +30,10 @@
.row
.col-md-12
%p.cta-button.text-center
- = link_to(conference_schedule_path(conference.short_title),
+ = link_to(events_conference_schedule_path(conference.short_title),
class: 'btn btn-success btn-lg') do
- Full Schedule
+ View Full Schedule
+ .trapezoid
- unless booths.blank?
- booths.each do |booth|
diff --git a/app/views/conferences/_registration.haml b/app/views/conferences/_registration.haml
index 51258f15c..373e1c13e 100644
--- a/app/views/conferences/_registration.haml
+++ b/app/views/conferences/_registration.haml
@@ -1,7 +1,3 @@
-= content_for :splash_nav do
- %li
- %a.smoothscroll{ href: '#registration' } Registration
-
- cache [conference, registration_period, tickets, '#splash#registration'] do
%section#registration
.container
@@ -37,3 +33,4 @@
= link_to('Register Now',
new_conference_conference_registration_path(conference_id),
class: 'btn btn-lg btn-success')
+ .trapezoid
diff --git a/app/views/conferences/_social_media.haml b/app/views/conferences/_social_media.haml
index 672a19cdf..4cfd4d2e0 100644
--- a/app/views/conferences/_social_media.haml
+++ b/app/views/conferences/_social_media.haml
@@ -27,3 +27,4 @@
- if contact.email?
= mail_to "#{ contact.email }" do
%i.fa-solid.fa-envelope.fa-4x
+ .trapezoid.social-media
diff --git a/app/views/conferences/_sponsors.haml b/app/views/conferences/_sponsors.haml
index 5d7bbaa95..ab9e36385 100644
--- a/app/views/conferences/_sponsors.haml
+++ b/app/views/conferences/_sponsors.haml
@@ -27,9 +27,10 @@
.row
.col-md-12
%h3.text-center
- Want to sponsor?
+ Interested in sponsoring #{conference.title}?
= link_to(sponsorship_mailto(conference)) do
- Contact us!
+ Please, contact us!
+ .trapezoid
- sponsors.each do |sponsor|
- content_for :modals do
diff --git a/app/views/conferences/_tickets.haml b/app/views/conferences/_tickets.haml
index 325f04b83..c3036f53b 100644
--- a/app/views/conferences/_tickets.haml
+++ b/app/views/conferences/_tickets.haml
@@ -6,20 +6,22 @@
%section#tickets
.container
.row
- .col-md-12.text-center
+ .col-md-12.text-center.col-top
%h2
- Support
- = conference.title
+ Sign up for #{conference.title}!
+ .row.text-center
+ %h3
+ = link_to('Looking for the SAP Young Thinkers Learning Festival?', conference_path('ylf2021')) if conference.short_title == '2021'
.row.row-centered
- tickets.each do |ticket|
- .col-md-3.col-sm-3.col-centered.col-top
- = link_to(conference_tickets_path(conference.short_title),
- class: 'thumbnail') do
+ .col-lg-4.col-md-3.col-sm-3.col-centered.col-top
+ = link_to(conference_tickets_path(conference.short_title), class: 'thumbnail') do
.caption
%h3.text-center.word_break
= ticket.title
.word_break
- = markdown(ticket.description)
+ = short_ticket_description(ticket)
%button.btn-block.btn.btn-lg.btn-success
- %i.fa-solid.fa-ticket.fa-fw
+ %i.fa-solid.fa-ticket.fa-fw{ "aria-hidden": true }
= humanized_money_with_symbol(ticket.price)
+ .trapezoid
diff --git a/app/views/conferences/_venue.haml b/app/views/conferences/_venue.haml
index f25851c1e..d47769c41 100644
--- a/app/views/conferences/_venue.haml
+++ b/app/views/conferences/_venue.haml
@@ -1,10 +1,16 @@
= content_for :splash_nav do
%li
- %a.smoothscroll{ href: '#venue' } Venue
+ %a.smoothscroll{ href: '#venue' } Location
%section#venue
- if venue.location?
= render '/conferences/venue_map', venue: venue
+ - if venue.description.present?
+ .container
+ .row
+ .col-md-8.col-md-offset-2
+ = markdown(venue.description, escape_html=false)
+ .trapezoid
- else
- cache [venue, commercial, '#splash#venue'] do
.container
@@ -42,3 +48,4 @@
- if venue.website
%br
= sanitize link_to(h(venue.website), h(venue.website))
+ .trapezoid
diff --git a/app/views/conferences/index.html.haml b/app/views/conferences/index.html.haml
index 720e1cdfd..9049d912c 100644
--- a/app/views/conferences/index.html.haml
+++ b/app/views/conferences/index.html.haml
@@ -8,14 +8,15 @@
- if @antiquated and @antiquated.any?
.row
.col-md-12
- %p.text-right
+ .page-header
%button{ type: 'button', class: 'btn btn-link btn-sm', 'data-toggle' => 'collapse', 'data-target' => '#antiquated', 'aria-expanded' => 'true', 'aria-controls' => 'antiquated'}
- Older conferences
- %span.notranslate
- = "(#{@antiquated.count})"
- %i.fa-solid.fa-chevron-right
- %i.fa-solid.fa-chevron-down{ style: 'display: none' }
- #antiquated.collapse
+ %h2
+ Past Conferences
+ %span.notranslate
+ = "(#{@antiquated.count})"
+ %i.fa-solid.fa-chevron-right{ style: 'display: none' }
+ %i.fa-solid.fa-chevron-down
+ #antiquated
- @antiquated.each do |conference|
= render '/conferences/conference_details', conference: conference
%p
diff --git a/app/views/conferences/show.html.haml b/app/views/conferences/show.html.haml
index 85b2bac65..29c5014e2 100644
--- a/app/views/conferences/show.html.haml
+++ b/app/views/conferences/show.html.haml
@@ -1,10 +1,10 @@
- content_for :head do
%meta{ property: "og:title", content: @conference.title }
%meta{ property: "og:site_name", content: ENV.fetch('OSEM_NAME', 'OSEM') }
- %meta{ property: "og:description", content: @conference.description }
+ %meta{ property: "og:description", content: plain_text(@conference.description) }
%meta{ property: "og:url", content: conference_url(@conference.short_title) }
%meta{ property: "twitter:title", content: (@conference.title) }
- %meta{ property: "twitter:description", content: @conference.description }
+ %meta{ property: "twitter:description", content: plain_text(@conference.description) }
- if @conference.picture?
%meta{ property: "og:image", content: @image_url }
%meta{ property: "og:image:secure_url", content: @image_url }
@@ -13,72 +13,80 @@
- else
%meta{ property: "twitter:card", content: "summary" }
-= content_for :title do
- = @conference.title
+- provide :title, @conference.title
+= content_for :additional_messages do
+ - if user_signed_in? && @conference.registration_tickets.any? && current_user.ticket_purchases.by_conference(@conference).empty?
+ .alert.alert-dismissable.alert-info.text-center#no-tickets{ role: 'alert' }
+ %button.button.close{ "data-dismiss": "alert", "aria-label": "close" }
+ ×
+ You have not booked any tickets for this conference yet.
+ = link_to "Book your tickets now!", "#tickets", class: 'btn btn-info btn-lg'
+
+ - if @unpaid_tickets
+ .alert.alert-dismissable.alert-info.text-center#unpaid-tickets{ role: 'alert' }
+ %button.button.close{ "data-dismiss" => "alert", "aria-label"=>"close" }
+ ×
+ You have unpaid tickets. Please complete your purchase.
+ = link_to('Purchase Tickets', new_conference_payment_path(@conference), class: 'btn btn-success btn-lg')
+
+ - if @user_needs_to_register
+ .alert.alert-dismissable.alert-warning.text-center#no-reg{ role: 'alert' }
+ %button.button.close{ "data-dismiss" => "alert", 'aria-label': 'close' }
+ ×
+ You still need to complete your registration for #{@conference.title}.
+ = link_to('Complete Registration', new_conference_conference_registration_path(@conference), class: 'btn btn-success btn-lg')
#splash
+ -# - cache [@conference, @splashpage, @conference.program, current_user, '#splash#main'] do
- if @conference.code_of_conduct.present?
= render 'code_of_conduct', organization: @conference.organization
- -# header/description
- = render 'header', conference: @conference, venue: @conference.venue
+ -# header
+ = render 'header', conference: @conference, venue: @conference.venue, splashpage: @splashpage, cached: true
+
+ -# description / happening now
+ - if @conference.splashpage.include_happening_now || @conference.description.present?
+ = render 'about_and_happening_now', conference: @conference,
+ events_schedules: @events_schedules, pagy: @pagy,
+ events_schedules_length: @events_schedules_length,
+ events_schedules_limit: @events_schedules_limit,
+ is_happening_next: @is_happening_next
-# calls for content, or program
- - if @conference.splashpage.include_cfp
+ - if @conference.splashpage.include_cfp?
= render 'call_for_content', conference: @conference,
call_for_events: @call_for_events, call_for_tracks: @call_for_tracks,
call_for_booths: @call_for_booths,
event_types: @event_types, tracks: @track_names
- - if @conference.splashpage.include_program
+ - if @conference.splashpage.include_program?
= render 'program', conference: @conference, tracks: @tracks,
- highlights: @highlights, booths: @booths
+ highlights: @highlights, booths: @booths, cached: true
-# attendance/registration
- - if @conference.splashpage.include_registrations
+ - if @conference.splashpage.include_registrations?
- if @conference.registration_open?
= render 'registration', conference: @conference,
registration_period: @conference.registration_period,
tickets: @tickets, conference_id: @conference.short_title
- if @conference.splashpage.include_tickets && @conference.tickets.any?
- = render 'tickets', conference: @conference, tickets: @tickets
+ = render 'tickets', conference: @conference, tickets: @tickets, cached: true
-# geo
- - if @conference.splashpage.include_venue && @conference.venue
+ - if @conference.splashpage.include_venue? && @conference.venue
= render 'venue', conference: @conference, venue: @conference.venue,
commercial: @conference.venue.commercial
- if @conference.splashpage.include_lodgings && @conference.lodgings.any?
= render 'lodging', venue: @conference.venue, lodgings: @lodgings
-# sponsorship
- - if @conference.splashpage.include_sponsors
+ - if @conference.splashpage.include_sponsors?
= render 'sponsors', conference: @conference,
sponsorship_levels: @sponsorship_levels,
sponsors: @sponsors
-
-# footer
- - if @conference.splashpage.include_social_media
- - if @conference.contact.has_social_media?
- = render 'social_media', contact: @conference.contact
- = render 'footer'
-
-- content_for :script_head do
- :javascript
- var triangle_tcs = tinycolor("#{h(@conference.color)}").monochromatic();
- var triangle_colors = triangle_tcs.map(function(t) {
- return t.toHexString();
- });
- $(function () {
- $(document).ready(function() {
- var triangle_width = document.body.clientWidth;
- var triangle_height = ($( "#banner" ).height() + 200 );
- var pattern = Trianglify({ width: triangle_width,
- height: triangle_height,
- cell_size: 100,
- x_colors: triangle_colors
- });
- $('#banner').css('background-image', 'url("' + pattern.png() + '")');
- });
- });
+ - if @conference.splashpage.include_social_media? && @conference.contact.has_social_media?
+ = render 'social_media', contact: @conference.contact, cached: true
+ -# = render 'footer', cached: true
diff --git a/app/views/conferences/show.js.erb b/app/views/conferences/show.js.erb
new file mode 100644
index 000000000..61c0a3a35
--- /dev/null
+++ b/app/views/conferences/show.js.erb
@@ -0,0 +1,6 @@
+$('#happening-now').html("<%= j(render 'happening_now', conference: @conference,
+ events_schedules: @events_schedules, pagy: @pagy,
+ events_schedules_length: @events_schedules_length,
+ events_schedules_limit: @events_schedules_limit,
+ is_happening_next: @is_happening_next) %>");
+Pagy.init(document.getElementById('happening-now'));
diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml
index 1aa00e559..c00f3626e 100644
--- a/app/views/devise/confirmations/new.html.haml
+++ b/app/views/devise/confirmations/new.html.haml
@@ -4,7 +4,7 @@
.panel.panel-default
.panel-heading
%h3.panel-title
- Resend confirmation instructions
+ Resend account confirmation instructions
.panel-body
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), method: :post) do |f|
.form-group
diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb
new file mode 100644
index 000000000..55318a91f
--- /dev/null
+++ b/app/views/devise/mailer/confirmation_instructions.html.erb
@@ -0,0 +1,9 @@
+<%= render partial: "layouts/mailbot_header" %>
+
+
Welcome to Snap!Con <%= @email %>!
+
+
You can confirm your account email through the link below:
+
+
<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
+
+<%= render partial: "layouts/mailbot_footer" %>
diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb
new file mode 100644
index 000000000..32f4ba803
--- /dev/null
+++ b/app/views/devise/mailer/email_changed.html.erb
@@ -0,0 +1,7 @@
+Hello <%= @email %>!
+
+<% if @resource.try(:unconfirmed_email?) %>
+ We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.
+<% else %>
+ We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
+<% end %>
diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb
new file mode 100644
index 000000000..b41daf476
--- /dev/null
+++ b/app/views/devise/mailer/password_change.html.erb
@@ -0,0 +1,3 @@
+Hello <%= @resource.email %>!
+
+We're contacting you to notify you that your password has been changed.
diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb
new file mode 100644
index 000000000..f667dc12f
--- /dev/null
+++ b/app/views/devise/mailer/reset_password_instructions.html.erb
@@ -0,0 +1,8 @@
+Hello <%= @resource.email %>!
+
+Someone has requested a link to change your password. You can do this through the link below.
+
+<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
+
+If you didn't request this, please ignore this email.
+Your password won't change until you access the link above and create a new one.
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb
new file mode 100644
index 000000000..41e148bf2
--- /dev/null
+++ b/app/views/devise/mailer/unlock_instructions.html.erb
@@ -0,0 +1,7 @@
+Hello <%= @resource.email %>!
+
+Your account has been locked due to an excessive number of unsuccessful sign in attempts.
+
+Click the link below to unlock your account:
+
+<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
diff --git a/app/views/devise/sessions/_form_fields.html.haml b/app/views/devise/sessions/_form_fields.html.haml
index 8f29c0fac..088b40c79 100644
--- a/app/views/devise/sessions/_form_fields.html.haml
+++ b/app/views/devise/sessions/_form_fields.html.haml
@@ -8,7 +8,7 @@
= f.password_field :password, required: true, class: 'form-control', placeholder: 'Password'
- if devise_mapping.rememberable?
%p.text-right.small
- = f.label 'Remember me'
+ = f.label :remember_me, 'Remember me'
= f.check_box :remember_me
%p.text-right
%button{type: 'submit', class: 'btn btn-success'}
diff --git a/app/views/devise/shared/_help.html.haml b/app/views/devise/shared/_help.html.haml
index 22a7ce21d..99eff9f1d 100644
--- a/app/views/devise/shared/_help.html.haml
+++ b/app/views/devise/shared/_help.html.haml
@@ -1,5 +1,5 @@
%p.text-right
%a.small.btn.btn-default.btn-xs{"data-toggle" => "collapse", "data-target" => "#devise-help"}
- Need Help?
+ Can't login?
#devise-help.collapse
= render 'devise/shared/links'
diff --git a/app/views/devise/shared/_openid_links.html.haml b/app/views/devise/shared/_openid_links.html.haml
index 2b3ba8dc0..842b355db 100644
--- a/app/views/devise/shared/_openid_links.html.haml
+++ b/app/views/devise/shared/_openid_links.html.haml
@@ -1,8 +1,23 @@
.text-center
.btn-group.btn-group-lg#openid-btn-grp
- omniauth_configured.each do |provider|
- = link_to "user_#{provider}_omniauth_authorize".to_sym, class: "btn btn-success btn-lg",
+ - if provider != :discourse
+ = link_to "user_#{provider}_omniauth_authorize".to_sym, class: "btn btn-success btn-lg",
id: "omniauth-#{provider}",
title: "Your #{provider} login",
method: :post do
- %i{class: "fa-brands fa-#{provider}"}
+ %i{class: "fa-brands fa-#{provider}"}
+ - else
+ = link_to("user_#{provider}_omniauth_authorize".to_sym,
+ class: "btn btn-success btn-lg",
+ id: "omniauth-#{provider}",
+ title: "Your #{provider} login", method: :post) do
+ %span
+ Snap
+ %em> !
+%br
+.text-center
+ %em If you are not currently logged into Snap! or the Snap! Forums, you will need to log in to
+ Snap!Con twice when using your Snap! account. We're working on fixing this. Thanks!
+
+%br
diff --git a/app/views/devise/shared/_sign_up_form_embedded.html.haml b/app/views/devise/shared/_sign_up_form_embedded.html.haml
new file mode 100644
index 000000000..94e249c4c
--- /dev/null
+++ b/app/views/devise/shared/_sign_up_form_embedded.html.haml
@@ -0,0 +1,15 @@
+- unless current_user
+ %p
+ %strong Create an account directly for Snap!Con, or sign in using Google.
+ %p
+ You may sign in via Snap!, but the first time you try, you
+ %em might not
+ be redirected back to Snap!Con. If that happens, try a second time and the login will work.
+ We're trying to fix this issue. Thanks!
+ = semantic_fields_for @user do |u|
+ = u.input :username, input_html: { required: 'required', autocomplete: 'off' }
+ = u.input :email, input_html: { required: 'required', autocomplete: 'off' }
+ = u.input :password, input_html: { required: 'required', autocomplete: 'off', id: 'password_inline' }
+ = u.input :password_confirmation, input_html: { required: 'required', autocomplete: 'off' }
+ = render partial: 'devise/shared/openid'
+ = render partial: 'devise/shared/help'
diff --git a/app/views/layouts/_admin.html.haml b/app/views/layouts/_admin.html.haml
index a9add1acf..994d25e22 100644
--- a/app/views/layouts/_admin.html.haml
+++ b/app/views/layouts/_admin.html.haml
@@ -6,9 +6,10 @@
= render 'layouts/admin_sidebar'
-else
-# Index admin sidebar
- = render 'layouts/admin_sidebar_index'
+ - cache ['#admin-sidebar', @conference, current_user] do
+ = render 'layouts/admin_sidebar_index'
.col-md-10
#messages
=render 'layouts/messages'
#content
- = yield
\ No newline at end of file
+ = yield
diff --git a/app/views/layouts/_admin_sidebar.html.haml b/app/views/layouts/_admin_sidebar.html.haml
index f4c200060..3cf84385d 100644
--- a/app/views/layouts/_admin_sidebar.html.haml
+++ b/app/views/layouts/_admin_sidebar.html.haml
@@ -43,7 +43,7 @@
= link_to 'Contact', edit_admin_conference_contact_path(@conference.short_title)
- if can? :index, @conference.commercials.build
%li{class: "#{active_nav_li(admin_conference_commercials_path(@conference.short_title))}"}
- = link_to 'Commercials', admin_conference_commercials_path(@conference.short_title)
+ = link_to 'Materials', admin_conference_commercials_path(@conference.short_title)
- if can? :update, @conference
%li{class: active_nav_li(edit_admin_conference_splashpage_path(@conference.short_title))}
= link_to 'Splashpage', admin_conference_splashpage_path(@conference.short_title)
@@ -116,6 +116,13 @@
- 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? :update, @conference.currency_conversions.build
+ %li
+ = link_to 'Currency', admin_conference_currency_conversions_path(@conference.short_title)
+ - if can? :update, @conference.tickets.build
+ %li{class: active_nav_li(admin_conference_physical_tickets_path(@conference.short_title)) }
+ = link_to 'Ticket Purchases', admin_conference_physical_tickets_path(@conference.short_title)
+
- if can? :manage, @conference.booths.build
%li
= link_to admin_conference_booths_path(@conference.short_title) do
diff --git a/app/views/layouts/_mailbot_footer.html.erb b/app/views/layouts/_mailbot_footer.html.erb
new file mode 100644
index 000000000..4cc06c7fb
--- /dev/null
+++ b/app/views/layouts/_mailbot_footer.html.erb
@@ -0,0 +1,7 @@
+ <% if @conference.present? %>
+
+ <% else %>
+
+ <% end %>
+