├── .envrc
├── .gemrc
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── new_user_onboarding.md
├── dependabot.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .rspec
├── .rspec_parallel
├── .rubocop.yml
├── .rubocop_todo.yml
├── .ruby-version
├── CONTRIBUTING.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.md
├── Procfile
├── README.md
├── Rakefile
├── app
├── assets
│ ├── config
│ │ └── manifest.js
│ ├── fonts
│ │ ├── Amble-Bold-webfont.woff
│ │ ├── Amble-Light-webfont.woff
│ │ ├── Amble-Regular-webfont.woff
│ │ └── freehand.woff
│ ├── images
│ │ ├── background.png
│ │ ├── dropdown_arrows.svg
│ │ ├── logo.svg
│ │ ├── troll.svg
│ │ └── troll_plus_logo.svg
│ ├── javascripts
│ │ ├── application.js
│ │ ├── bridgetroll.js
│ │ ├── checkins.js.coffee
│ │ ├── enums
│ │ │ └── role.js.erb
│ │ ├── event_email_addresser.js.coffee
│ │ ├── events.js.coffee
│ │ ├── font_analytics.js.erb
│ │ ├── forms.js.coffee
│ │ ├── helpers
│ │ │ └── capitalize.js
│ │ ├── imported_event.js
│ │ ├── rsvps.js.coffee
│ │ ├── section_organizer.js
│ │ ├── section_organizer
│ │ │ ├── collections
│ │ │ │ ├── attendee_collection.js
│ │ │ │ ├── section_collection.js
│ │ │ │ └── session_collection.js
│ │ │ ├── dialogs
│ │ │ │ ├── attendee_detail.js
│ │ │ │ ├── auto_arrange_choices.js
│ │ │ │ ├── base_dialog.js
│ │ │ │ ├── edit_section.js
│ │ │ │ └── help_dialog.js
│ │ │ ├── models
│ │ │ │ ├── attendee.js
│ │ │ │ ├── section.js
│ │ │ │ └── session.js
│ │ │ └── views
│ │ │ │ ├── base_view.js
│ │ │ │ ├── section_organizer_view.js
│ │ │ │ └── section_view.js
│ │ ├── services
│ │ │ ├── footer.js
│ │ │ ├── matchHeights.js
│ │ │ └── poller.js
│ │ ├── sign_in.js.coffee
│ │ ├── tables.js.coffee
│ │ ├── toggling.js.coffee
│ │ └── views
│ │ │ └── events_filter_view.js.coffee
│ ├── stylesheets
│ │ ├── _alerts.scss
│ │ ├── _base.scss
│ │ ├── _checkins.scss
│ │ ├── _constants.scss
│ │ ├── _external_pages.scss
│ │ ├── _font_mixins.scss
│ │ ├── _fonts.scss
│ │ ├── _footer.scss
│ │ ├── _form.scss.erb
│ │ ├── _gravatars.scss
│ │ ├── _header.scss
│ │ ├── _levels.scss
│ │ ├── _mixins.scss
│ │ ├── _profile.scss
│ │ ├── _section_organizer.scss
│ │ ├── _style_guide.scss.erb
│ │ ├── _tables.scss
│ │ ├── application.css
│ │ ├── bootstrap_with_overrides.scss
│ │ ├── devise.scss
│ │ ├── events
│ │ │ ├── _email.scss
│ │ │ ├── _form.scss
│ │ │ ├── _index.scss
│ │ │ ├── _organize.scss
│ │ │ └── _show.scss
│ │ ├── font_awesome_with_extensions.scss
│ │ └── utilities.scss
│ └── templates
│ │ ├── class_levels_popover.hbs
│ │ ├── email_attendees_popover.hbs
│ │ ├── imported_event_popover.hbs
│ │ └── section_organizer
│ │ ├── attendee_detail.hbs
│ │ ├── auto_arrange_choices.hbs
│ │ ├── edit_section.hbs
│ │ ├── help_dialog.hbs
│ │ ├── section.hbs
│ │ └── section_organizer.hbs
├── controllers
│ ├── admin_pages_controller.rb
│ ├── application_controller.rb
│ ├── chapters
│ │ └── leaders_controller.rb
│ ├── chapters_controller.rb
│ ├── checkiners_controller.rb
│ ├── checkins_controller.rb
│ ├── courses_controller.rb
│ ├── devise_overrides
│ │ ├── omniauth_callbacks_controller.rb
│ │ └── registrations_controller.rb
│ ├── event_sessions_controller.rb
│ ├── events
│ │ ├── attendee_names_controller.rb
│ │ ├── attendees_controller.rb
│ │ ├── emails_controller.rb
│ │ ├── organizer_tools_controller.rb
│ │ ├── students_controller.rb
│ │ ├── surveys_controller.rb
│ │ └── unpublished_events_controller.rb
│ ├── events_controller.rb
│ ├── external_events_controller.rb
│ ├── locations_controller.rb
│ ├── meetup_users_controller.rb
│ ├── organizations_controller.rb
│ ├── organizers_controller.rb
│ ├── profiles_controller.rb
│ ├── regions
│ │ └── leaders_controller.rb
│ ├── regions_controller.rb
│ ├── rsvps_controller.rb
│ ├── sections_controller.rb
│ ├── static_pages_controller.rb
│ ├── surveys_controller.rb
│ ├── users
│ │ └── events_controller.rb
│ ├── users_controller.rb
│ └── volunteers_controller.rb
├── helpers
│ ├── application_helper.rb
│ ├── events_helper.rb
│ ├── locations_helper.rb
│ ├── navigation_helper.rb
│ ├── profiles_helper.rb
│ └── responsive_helper.rb
├── jobs
│ ├── reminder_sender.rb
│ └── survey_sender.rb
├── mailers
│ ├── admin_mailer.rb
│ ├── application_mailer.rb
│ ├── event_mailer.rb
│ ├── rsvp_mailer.rb
│ └── survey_mailer.rb
├── models
│ ├── application_record.rb
│ ├── authentication.rb
│ ├── chapter.rb
│ ├── chapter_leadership.rb
│ ├── concerns
│ │ └── presence_tracking_boolean.rb
│ ├── course.rb
│ ├── dietary_restriction.rb
│ ├── event.rb
│ ├── event_details
│ │ ├── default_details.html
│ │ ├── default_student_details.html
│ │ ├── default_survey_greeting.html
│ │ └── default_volunteer_details.html
│ ├── event_email.rb
│ ├── event_email_recipient.rb
│ ├── event_session.rb
│ ├── external_event.rb
│ ├── level.rb
│ ├── location.rb
│ ├── meetup_user.rb
│ ├── operating_system.rb
│ ├── organization.rb
│ ├── organization_leadership.rb
│ ├── organization_subscription.rb
│ ├── profile.rb
│ ├── region.rb
│ ├── region_leadership.rb
│ ├── regions_user.rb
│ ├── role.rb
│ ├── rsvp.rb
│ ├── rsvp_session.rb
│ ├── section.rb
│ ├── survey.rb
│ ├── user.rb
│ ├── volunteer_assignment.rb
│ └── volunteer_preference.rb
├── policies
│ ├── application_policy.rb
│ ├── chapter_policy.rb
│ ├── course_policy.rb
│ ├── event_policy.rb
│ ├── event_session_policy.rb
│ ├── external_event_policy.rb
│ ├── level_policy.rb
│ ├── location_policy.rb
│ ├── organization_policy.rb
│ ├── profile_policy.rb
│ ├── region_policy.rb
│ ├── rsvp_policy.rb
│ ├── section_policy.rb
│ ├── survey_policy.rb
│ └── user_policy.rb
├── presenters
│ ├── event_email_presenter.rb
│ └── past_event_emails_presenter.rb
├── services
│ ├── account_merger.rb
│ ├── database_anonymizer.rb
│ ├── event_csv_reporter.rb
│ ├── event_editor.rb
│ ├── event_list.rb
│ ├── external_event_importer.rb
│ ├── ics_generator.rb
│ ├── meetup_importer.rb
│ ├── omniauth_providers.rb
│ ├── rsvp_sorter.rb
│ ├── section_arranger.rb
│ ├── user_list.rb
│ ├── user_searcher.rb
│ └── waitlist_manager.rb
├── validators
│ └── array_of_ids_validator.rb
└── views
│ ├── admin_pages
│ └── admin_dashboard.html.erb
│ ├── chapters
│ ├── _form.html.erb
│ ├── _table.html.erb
│ ├── edit.html.erb
│ ├── index.html.erb
│ ├── leaders
│ │ └── index.html.erb
│ ├── new.html.erb
│ └── show.html.erb
│ ├── checkiners
│ └── index.html.erb
│ ├── checkins
│ └── index.html.erb
│ ├── courses
│ ├── _form.html.erb
│ ├── edit.html.erb
│ ├── index.html.erb
│ └── new.html.erb
│ ├── devise
│ ├── confirmations
│ │ └── new.html.erb
│ ├── mailer
│ │ ├── confirmation_instructions.html.erb
│ │ └── reset_password_instructions.html.erb
│ ├── passwords
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ ├── registrations
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ └── sessions
│ │ └── new.html.erb
│ ├── event_sessions
│ └── index.html.erb
│ ├── events
│ ├── _attendee_list.html.erb
│ ├── _form.html.erb
│ ├── _guidelines.html.erb
│ ├── _location_modal.html.erb
│ ├── _organizer_preworkshop_buttons.html.erb
│ ├── _upcoming_event.html.erb
│ ├── attendees
│ │ └── index.html.erb
│ ├── edit.html.erb
│ ├── emails
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── feed.atom.builder
│ ├── feed.rss.builder
│ ├── index.html.erb
│ ├── levels.html.erb
│ ├── new.html.erb
│ ├── organizer_tools
│ │ ├── _checkin_counts.html.erb
│ │ ├── _childcare_requests.html.erb
│ │ ├── _operating_system_breakdown.erb
│ │ ├── diets.html.erb
│ │ ├── index.html.erb
│ │ └── organize_sections.html.erb
│ ├── past_events.html.erb
│ ├── show.html.erb
│ ├── students
│ │ └── index.html.erb
│ ├── surveys
│ │ └── edit.html.erb
│ └── unpublished_events
│ │ ├── _mark_as_spam_button.html.erb
│ │ ├── _publish_event_button.html.erb
│ │ ├── _unpublished_event.html.erb
│ │ └── index.html.erb
│ ├── external_events
│ ├── _form.html.erb
│ ├── edit.html.erb
│ ├── index.html.erb
│ └── new.html.erb
│ ├── layouts
│ ├── application.html.erb
│ └── mailer.html.erb
│ ├── locations
│ ├── _form.html.erb
│ ├── create.js.erb
│ ├── create_failed.js.erb
│ ├── edit.html.erb
│ ├── index.html.erb
│ ├── new.html.erb
│ └── show.html.erb
│ ├── mailers
│ ├── event_mailer
│ │ ├── _new_event_detail.html.erb
│ │ ├── _session_location_detail.html.erb
│ │ ├── event_has_been_approved.html.erb
│ │ ├── event_pending_approval.html.erb
│ │ ├── from_organizer.html.erb
│ │ ├── new_event.html.erb
│ │ ├── new_organizer_alert.erb
│ │ └── unpublished_event.html.erb
│ ├── rsvp_mailer
│ │ ├── _event_details.html.erb
│ │ ├── childcare_notification.html.erb
│ │ ├── email.html.erb
│ │ └── off_waitlist.html.erb
│ └── survey_mailer
│ │ └── notification.html.erb
│ ├── meetup_users
│ └── show.html.erb
│ ├── organizations
│ ├── _chapter_infowindow.html.erb
│ ├── index.html.erb
│ ├── new.html.erb
│ └── show.html.erb
│ ├── organizers
│ └── index.html.erb
│ ├── profiles
│ └── show.html.erb
│ ├── regions
│ ├── _form.html.erb
│ ├── edit.html.erb
│ ├── index.html.erb
│ ├── leaders
│ │ └── index.html.erb
│ ├── new.html.erb
│ └── show.html.erb
│ ├── rsvps
│ ├── _class_levels.html.erb
│ ├── _dietary_restrictions.html.erb
│ ├── _form.html.erb
│ ├── edit.html.erb
│ ├── new.html.erb
│ └── quick_destroy_confirm.html.erb
│ ├── shared
│ ├── _actions.html.erb
│ ├── _chapter_select.html.erb
│ ├── _checkin_event_sessions.html.erb
│ ├── _devise_error_messages.html.erb
│ ├── _events_table.html.erb
│ ├── _google_analytics.html.erb
│ ├── _locations_table.html.erb
│ ├── _map.html.erb
│ ├── _model_error_messages.html.erb
│ ├── _navbar.html.erb
│ ├── _organizer_action.erb
│ ├── _organizer_breadcrumb.erb
│ ├── _past_events_table.erb
│ ├── _plus_one_disclaimer.html.erb
│ ├── _profile_gravatars.html.erb
│ ├── _region_select.html.erb
│ ├── _rsvp_actions.html.erb
│ ├── _rsvp_actions_unauthenticated.html.erb
│ ├── _rsvps.html.erb
│ ├── _sign_in.html.erb
│ ├── _sign_in_dialog.html.erb
│ ├── _user_chooser.html.erb
│ └── _welcome.html.erb
│ ├── static_pages
│ ├── about.html.erb
│ └── style_guide.html.erb
│ ├── surveys
│ ├── _results.html.erb
│ ├── index.html.erb
│ └── new.html.erb
│ ├── users
│ └── index.html.erb
│ └── volunteers
│ └── index.html.erb
├── bin
├── bundle
├── dev
├── find_spec_or_impl
├── rails
├── rake
├── rerun_failures
├── rspec
├── rubocop
├── setup
├── update
└── yarn
├── config.ru
├── config
├── application.rb
├── boot.rb
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ ├── staging.rb
│ └── test.rb
├── initializers
│ ├── application_controller_renderer.rb
│ ├── assets.rb
│ ├── backtrace_silencers.rb
│ ├── constants.rb
│ ├── content_security_policy.rb
│ ├── cookies_serializer.rb
│ ├── cors.rb
│ ├── devise.rb
│ ├── filter_parameter_logging.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ ├── per_form_csrf_tokens.rb
│ ├── permissions_policy.rb
│ ├── rack_profiler.rb
│ ├── request_forgery_protection.rb
│ ├── sentry.rb
│ ├── session_store.rb
│ ├── simple_form.rb
│ ├── simple_form_bootstrap.rb
│ └── wrap_parameters.rb
├── locales
│ ├── devise.en.yml
│ ├── en.yml
│ └── simple_form.en.yml
├── newrelic.yml
├── puma.rb
├── routes.rb
├── spring.rb
└── storage.yml
├── db
├── migrate
│ ├── 20151210035820_init_schema.rb
│ ├── 20160110000811_add_location_to_event_sessions.rb
│ ├── 20160211061631_add_chapter_leaderships_again.rb
│ ├── 20160312224628_create_organization_leaderships.rb
│ ├── 20160620044512_add_unaccent_extension_for_postgres.rb
│ ├── 20160710221707_create_organization_subscriptions.rb
│ ├── 20160807055624_add_twitter_username_to_profiles.rb
│ ├── 20161002052810_add_external_event_editor_to_users.rb
│ ├── 20161204040052_rename_external_event_data_to_imported_event_data.rb
│ ├── 20161206055208_regenerate_canonical_sfruby_meetup_urls.rb
│ ├── 20161228162402_create_courses.rb
│ ├── 20161228162440_create_levels.rb
│ ├── 20170110062210_set_id_sequence_for_new_courses.rb
│ ├── 20170202035358_add_food_provided_to_events.rb
│ ├── 20170319191233_add_custom_question_to_events.rb
│ ├── 20170319192836_add_custom_question_answer_to_rsvps.rb
│ ├── 20170707161519_add_appropriate_class_for_skill_to_survey.rb
│ ├── 20200102135517_add_primary_key_to_regions_users.rb
│ └── 20200329093106_sprinkle_indeces_where_we_do_uniqueness_checks.rb
├── schema.rb
├── seeds.rb
└── seeds
│ ├── admin_user.rb
│ ├── course_populator.rb
│ ├── courses.yaml
│ ├── seed_chapter.rb
│ ├── seed_courses.rb
│ ├── seed_event.rb
│ └── seed_region.rb
├── devenv.lock
├── devenv.nix
├── devenv.yaml
├── doc
├── admin.md
└── deploy.md
├── lib
├── assets
│ └── .gitkeep
└── tasks
│ ├── db.rake
│ ├── external_event_import.rake
│ ├── jasmine.rake
│ ├── populate_courses.rake
│ ├── rspec_with_retries.rake
│ ├── scheduler.rake
│ └── support
│ ├── failure_file_parser.rb
│ └── rspec_rerunner.rb
├── package.json
├── public
├── 400.html
├── 404.html
├── 406-unsupported-browser.html
├── 422.html
├── 500.html
├── email_images
│ └── confirm_account.gif
├── favicon.ico
├── robots.txt
└── troll_icon.png
├── script
└── rubymine_open_spec_or_impl.sh
├── spec
├── controllers
│ ├── admin_pages_controller_spec.rb
│ ├── chapters
│ │ └── leaders_controller_spec.rb
│ ├── chapters_controller_spec.rb
│ ├── checkiners_controller_spec.rb
│ ├── checkins_controller_spec.rb
│ ├── courses_controller_spec.rb
│ ├── devise_overrides
│ │ └── registrations_controller_spec.rb
│ ├── event_sessions_controller_spec.rb
│ ├── events
│ │ ├── attendees_controller_spec.rb
│ │ ├── emails_controller_spec.rb
│ │ ├── organizer_tools_controller_spec.rb
│ │ ├── students_controller_spec.rb
│ │ └── unpublished_events_controller_spec.rb
│ ├── events_controller_spec.rb
│ ├── external_events_controller_spec.rb
│ ├── locations_controller_spec.rb
│ ├── organizations_controller_spec.rb
│ ├── organizers_controller_spec.rb
│ ├── profiles_controller_spec.rb
│ ├── regions
│ │ └── leaders_controller_spec.rb
│ ├── regions_controller_spec.rb
│ ├── rsvps_controller_spec.rb
│ ├── sections_controller_spec.rb
│ ├── static_pages_controller_spec.rb
│ ├── surveys_controller_spec.rb
│ ├── users
│ │ └── events_controller_spec.rb
│ ├── users_controller_spec.rb
│ └── volunteers_controller_spec.rb
├── factories.rb
├── features
│ ├── admin
│ │ ├── admin_dashboard_request_spec.rb
│ │ ├── course_request_spec.rb
│ │ └── publish_event_request_spec.rb
│ ├── chapters
│ │ └── chapter_request_spec.rb
│ ├── events
│ │ ├── edit_event_request_spec.rb
│ │ ├── event_checkiner_request_spec.rb
│ │ ├── event_detail_request_spec.rb
│ │ ├── event_listing_request_spec.rb
│ │ ├── event_rsvp_request_spec.rb
│ │ ├── event_survey_request_spec.rb
│ │ ├── new_event_request_spec.rb
│ │ ├── organizer
│ │ │ ├── announcing_an_event_request_spec.rb
│ │ │ ├── attendee_details_request_spec.rb
│ │ │ ├── event_email_request_spec.rb
│ │ │ ├── event_organizer_dashboard_request_spec.rb
│ │ │ ├── event_organizers_request_spec.rb
│ │ │ └── organizer_closes_rsvps_request_spec.rb
│ │ └── rsvp
│ │ │ └── event_rsvp_coc_request_spec.rb
│ ├── external_events
│ │ └── external_event_request_spec.rb
│ ├── homepage_request_spec.rb
│ ├── locations_request_spec.rb
│ ├── organizations
│ │ └── organizations_request_spec.rb
│ ├── section_organizer
│ │ ├── section_arranger_request_spec.rb
│ │ └── section_organizer_request_spec.rb
│ ├── style_guide_request_spec.rb
│ └── users
│ │ ├── edit_account_details_request_spec.rb
│ │ ├── omniauth_request_spec.rb
│ │ ├── profiles_request_spec.rb
│ │ ├── sign_in_menu_request_spec.rb
│ │ └── users_request_spec.rb
├── helpers
│ └── events_helper_spec.rb
├── javascripts
│ ├── helpers
│ │ ├── ModelFactories.js
│ │ ├── SpecHelper.js
│ │ └── whenReady.js
│ └── views
│ │ ├── section_organizer_view_spec.js
│ │ └── section_view_spec.js
├── jobs
│ ├── reminder_sender_spec.rb
│ └── survey_sender_spec.rb
├── mailers
│ ├── devise
│ │ └── mailer_previews_spec.rb
│ ├── event_mailer_spec.rb
│ ├── previews
│ │ ├── admin_preview.rb
│ │ ├── devise
│ │ │ └── mailer_preview.rb
│ │ ├── event_preview.rb
│ │ ├── rsvp_preview.rb
│ │ └── survey_preview.rb
│ ├── rsvp_mailer_spec.rb
│ └── survey_mailer_spec.rb
├── models
│ ├── chapter_leadership_spec.rb
│ ├── chapter_spec.rb
│ ├── course_spec.rb
│ ├── event_email_spec.rb
│ ├── event_session_spec.rb
│ ├── event_spec.rb
│ ├── factory_spec.rb
│ ├── level_spec.rb
│ ├── location_spec.rb
│ ├── meetup_user_spec.rb
│ ├── profile_spec.rb
│ ├── region_leadership_spec.rb
│ ├── region_spec.rb
│ ├── rsvp_session_spec.rb
│ ├── rsvp_spec.rb
│ └── user_spec.rb
├── policies
│ ├── course_policy_spec.rb
│ ├── event_policy_spec.rb
│ ├── location_policy_spec.rb
│ └── organization_policy_spec.rb
├── presenters
│ ├── event_email_presenter_spec.rb
│ └── past_event_emails_presenter_spec.rb
├── rails_helper.rb
├── seeds
│ └── seeder_spec.rb
├── services
│ ├── account_merger_spec.rb
│ ├── database_anonymizer_spec.rb
│ ├── event_list_spec.rb
│ ├── ics_generator_spec.rb
│ ├── meetup_importer_spec.rb
│ ├── omniauth_responses.rb
│ ├── rsvp_sorter_spec.rb
│ ├── section_arranger_spec.rb
│ ├── user_searcher_spec.rb
│ └── waitlist_manager_spec.rb
├── spec_helper.rb
└── support
│ ├── authorization_shared_behaviors.rb
│ ├── event_form_helper.rb
│ ├── form_helper.rb
│ ├── jasmine-browser.mjs
│ ├── mailers.rb
│ ├── row_counting.rb
│ ├── sign_in_helper.rb
│ └── waiting_helper.rb
├── vendor
└── assets
│ ├── javascripts
│ ├── backbone-super.js
│ ├── jquery.event.drag.js
│ └── jquery.event.drop.js
│ └── stylesheets
│ └── .gitkeep
└── yarn.lock
/.envrc:
--------------------------------------------------------------------------------
1 | #!/bin/env bash
2 |
3 | # if you're wondering what this file is:
4 | # Bridge Troll uses devenv to setup local dependencies (ruby, postgres, etc.)
5 | # with devenv alone, when you want to use your dev environment, you have to type `devenv shell`
6 | #
7 | # https://direnv.net/ makes it better! this file is the entrypoint for direnv
8 | # with devenv + direnv, whenever you enter the bridge troll directory, direnv will automatically drop you into your dev environment
9 |
10 | export DIRENV_WARN_TIMEOUT=20s
11 |
12 | eval "$(devenv direnvrc)"
13 |
14 | # --impure only necessary because of old ruby
15 | use devenv
16 |
--------------------------------------------------------------------------------
/.gemrc:
--------------------------------------------------------------------------------
1 | gem: --no-rdoc --no-ri
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: bridgefoundry
4 | open_collective: bridge-foundry-inc
5 | custom: ["https://paypal.me/bridgefoundry"]
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Expected Behavior
4 |
5 |
6 | ## Current Behavior
7 |
8 |
9 | ## Possible Solution
10 |
11 |
12 | ## Steps to Reproduce
13 |
14 |
15 | 1.
16 | 2.
17 | 3.
18 | 4.
19 |
20 | ## Context (Environment)
21 |
22 |
23 |
24 |
25 |
26 | ## Detailed Description
27 |
28 |
29 | ## Possible Implementation
30 |
31 |
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/new_user_onboarding.md:
--------------------------------------------------------------------------------
1 | Welcome to the Bridge Troll project! We're excited to have your help. In order to get you started, we've made a helpful checklist of how to get started contributing to the project.
2 |
3 | - [ ] :eyes: Read [Contributing](https://github.com/railsbridge/bridge_troll/blob/master/CONTRIBUTING.md) guidelines
4 | - [ ] :fork_and_knife: Fork the repo and get a local development environment working per the [Quickstart Guide](https://github.com/nerual/bridge_troll#quickstart)
5 | - [ ] Assign yourself a [Beginner Friendly](https://github.com/railsbridge/bridge_troll/labels/Beginner%20Friendly) issue
6 | - [ ] :tada: Create your first Pull Request
7 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: bundler
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "11:00"
8 | open-pull-requests-limit: 10
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile ~/.gitignore_global
6 |
7 | # Ignore zeus stuff
8 | zeus.json
9 | custom_plan.rb
10 |
11 | # Ignore bundler config
12 | /.bundle
13 |
14 | # Ignore the default SQLite database.
15 | /db/*.sqlite3*
16 |
17 | # Ignore database dumps
18 | *.dump
19 |
20 | # Ignore all logfiles and tempfiles.
21 | /log/*.log
22 | /tmp
23 |
24 | # Ignore Rubymine created folder
25 | .idea
26 |
27 | # Ignore Redcar directory
28 | .redcar/*
29 |
30 | # Development environment variable files
31 | .env
32 |
33 | # OSX
34 | .DS_Store
35 |
36 | # Vim swap files
37 | **/*.swp
38 |
39 | # Byebug
40 | .byebug_history
41 |
42 | # yarn
43 | node_modules
44 |
45 | coverage
46 | db/*sqlite3-journal
47 | .devenv
48 |
49 | # Devenv
50 | .devenv*
51 | devenv.local.nix
52 |
53 | # direnv
54 | .direnv
55 |
56 | # pre-commit
57 | .pre-commit-config.yaml
58 | public/assets
59 | log/
60 |
61 | /config/master.key
62 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 |
--------------------------------------------------------------------------------
/.rspec_parallel:
--------------------------------------------------------------------------------
1 | --format progress
2 | --format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
3 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.4.4
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -C config/puma.rb
2 | release: rails db:migrate
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 | # frozen_string_literal: true
3 |
4 | # Add your own tasks in files placed in lib/tasks ending in .rake,
5 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
6 |
7 | require File.expand_path('config/application', __dir__)
8 |
9 | Rails.application.load_tasks
10 |
11 | if Rails.env.local?
12 | require 'rubocop/rake_task'
13 | RuboCop::RakeTask.new.tap do |task|
14 | task.options = %w[--parallel]
15 | end
16 |
17 | Rake::Task['default'].clear
18 | task default: [:rubocop, :rspec_with_retries, 'jasmine:ci']
19 | end
20 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link section_organizer.js
2 | //= link markerclustererplus/dist/markerclusterer.min.js
3 | //= link application.js
4 | //= link application.css
5 | //= link logo.svg
6 | //= link troll.svg
7 | //= link troll_plus_logo.svg
--------------------------------------------------------------------------------
/app/assets/fonts/Amble-Bold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/app/assets/fonts/Amble-Bold-webfont.woff
--------------------------------------------------------------------------------
/app/assets/fonts/Amble-Light-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/app/assets/fonts/Amble-Light-webfont.woff
--------------------------------------------------------------------------------
/app/assets/fonts/Amble-Regular-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/app/assets/fonts/Amble-Regular-webfont.woff
--------------------------------------------------------------------------------
/app/assets/fonts/freehand.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/app/assets/fonts/freehand.woff
--------------------------------------------------------------------------------
/app/assets/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/app/assets/images/background.png
--------------------------------------------------------------------------------
/app/assets/images/dropdown_arrows.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into including all the files listed below.
2 | // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3 | // be included in the compiled file accessible from http://example.com/assets/application.js
4 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5 | // the compiled file.
6 | //
7 | //= require jquery2
8 | //= require jquery_ujs
9 | //= require jquery-ui/widgets/datepicker
10 | //= require bootstrap/collapse
11 | //= require bootstrap/transition
12 | //= require bootstrap/modal
13 | //= require bootstrap/tooltip
14 | //= require bootstrap/popover
15 | //= require select2/dist/js/select2.js
16 | //= require handlebars.runtime
17 | //= require underscore
18 | //= require gmaps/google
19 | //= require backbone
20 | //= require backbone-super
21 | //= require bridgetroll
22 | //= require_tree ../templates
23 | //= require_tree ./enums
24 | //= require datatables.net/js/jquery.dataTables
25 | //= require datatables.net-bs/js/dataTables.bootstrap
26 | //= stub section_organizer
27 | //= require imported_event
28 | //= require_tree .
29 | //= require jquery_nested_form
30 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bridgetroll.js:
--------------------------------------------------------------------------------
1 | Bridgetroll = {
2 | Models: {},
3 | Collections: {},
4 | Views: {},
5 | Dialogs: {},
6 | Enums: {},
7 | Services: {},
8 | modalContainerSelector: 'body'
9 | };
10 |
--------------------------------------------------------------------------------
/app/assets/javascripts/enums/role.js.erb:
--------------------------------------------------------------------------------
1 | Bridgetroll.Enums.Role = {
2 | STUDENT: <%= Role::STUDENT.id %>,
3 | VOLUNTEER: <%= Role::VOLUNTEER.id %>,
4 | ORGANIZER: <%= Role::ORGANIZER.id %>
5 | };
--------------------------------------------------------------------------------
/app/assets/javascripts/font_analytics.js.erb:
--------------------------------------------------------------------------------
1 | <% if Rails.env.production? %>
2 | window.whenReady(function () {
3 | var fontTracking = _.extend(document.createElement('link'), {
4 | type: 'text/css',
5 | rel: 'stylesheet',
6 | href: '//hello.myfonts.net/count/262127'
7 | });
8 | document.getElementsByTagName('head')[0].appendChild(fontTracking);
9 | });
10 | <% end %>
--------------------------------------------------------------------------------
/app/assets/javascripts/forms.js.coffee:
--------------------------------------------------------------------------------
1 | jQuery ->
2 | $('input[type=submit][data-custom-action]').click (e) ->
3 | $input = $(e.target)
4 | $input.closest('form').attr('action', $input.data('custom-action'))
5 |
--------------------------------------------------------------------------------
/app/assets/javascripts/helpers/capitalize.js:
--------------------------------------------------------------------------------
1 | Handlebars.registerHelper('capitalize', function(str) {
2 | if (str) {
3 | return str.charAt(0).toUpperCase() + str.slice(1);
4 | } else {
5 | return str;
6 | }
7 | });
--------------------------------------------------------------------------------
/app/assets/javascripts/rsvps.js.coffee:
--------------------------------------------------------------------------------
1 | toggleVolunteerPreferenceVisibility = ->
2 | if $(".volunteer_preference:checked").length > 0
3 | $(".volunteer_preference_panel").show();
4 | else
5 | $(".volunteer_preference_panel").hide();
6 |
7 | $ ->
8 | toggleVolunteerPreferenceVisibility()
9 |
10 | $(document).on 'click', '.volunteer_preference', toggleVolunteerPreferenceVisibility
11 |
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer.js:
--------------------------------------------------------------------------------
1 | //= require masonry-layout/dist/masonry.pkgd.js
2 | //= require jquery.event.drag
3 | //= require jquery.event.drop
4 | //= require ./section_organizer/views/base_view
5 | //= require ./section_organizer/dialogs/base_dialog
6 | //= require_tree ./section_organizer/dialogs
7 | //= require_tree ./section_organizer/models
8 | //= require_tree ./section_organizer/collections
9 | //= require_tree ./section_organizer/views
10 |
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer/collections/attendee_collection.js:
--------------------------------------------------------------------------------
1 | Bridgetroll.Collections.Attendee = Backbone.Collection.extend({
2 | model: Bridgetroll.Models.Attendee
3 | });
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer/collections/section_collection.js:
--------------------------------------------------------------------------------
1 | Bridgetroll.Collections.Section = Backbone.Collection.extend({
2 | model: Bridgetroll.Models.Section
3 | });
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer/collections/session_collection.js:
--------------------------------------------------------------------------------
1 | Bridgetroll.Collections.Session = Backbone.Collection.extend({
2 | model: Bridgetroll.Models.Session
3 | });
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer/dialogs/attendee_detail.js:
--------------------------------------------------------------------------------
1 | Bridgetroll.Views.AttendeeDetail = (function () {
2 | return Bridgetroll.Dialogs.Base.extend({
3 | className: function () {
4 | return this._super('className', arguments) + ' bridgetroll-attendee-detail';
5 | },
6 | template: 'section_organizer/attendee_detail',
7 |
8 | context: function () {
9 | return _.extend({
10 | student: this.model.get('role_id') == Bridgetroll.Enums.Role.STUDENT,
11 | volunteer: this.model.get('role_id') == Bridgetroll.Enums.Role.VOLUNTEER,
12 | organizer: this.model.get('role_id') == Bridgetroll.Enums.Role.ORGANIZER
13 | }, this.model.attributes);
14 | }
15 | });
16 | })();
17 |
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer/dialogs/base_dialog.js:
--------------------------------------------------------------------------------
1 | Bridgetroll.Dialogs.Base = (function () {
2 | return Bridgetroll.Views.Base.extend({
3 | className: function () {
4 | if (Bridgetroll.railsEnv === 'test') {
5 | return 'modal';
6 | } else {
7 | return 'modal fade';
8 | }
9 | },
10 | showModally: function () {
11 | this.render();
12 | $(Bridgetroll.modalContainerSelector).append(this.el);
13 | $(this.el).modal();
14 |
15 | this.$el.on('modal.bs.shown', function () {
16 | $(this).find('.btn').focus();
17 | $(this).find('[autofocus]').focus();
18 | });
19 |
20 | $(this.el).on('hidden', _.bind(function () {
21 | this.destroy();
22 | }, this));
23 | }
24 | });
25 | })();
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer/dialogs/help_dialog.js:
--------------------------------------------------------------------------------
1 | Bridgetroll.Views.HelpDialog = (function () {
2 | return Bridgetroll.Dialogs.Base.extend({
3 | template: 'section_organizer/help_dialog'
4 | });
5 | })();
6 |
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer/models/section.js:
--------------------------------------------------------------------------------
1 | Bridgetroll.Models.Section = Backbone.Model.extend({
2 | urlRoot: function () {
3 | return '/events/' + this.get('event_id') + '/sections';
4 | },
5 |
6 | toJSON: function () {
7 | return {
8 | section: this.attributes
9 | };
10 | },
11 |
12 | classLevel: function () {
13 | return parseInt(this.get('class_level'), 10);
14 | },
15 |
16 | isUnassigned: function () {
17 | return this.get('id') === null;
18 | }
19 | });
--------------------------------------------------------------------------------
/app/assets/javascripts/section_organizer/models/session.js:
--------------------------------------------------------------------------------
1 | Bridgetroll.Models.Session = Backbone.Model.extend({
2 | toJSON: function () {
3 | return this.attributes;
4 | },
5 |
6 | checkedInVolunteers: function (volunteers) {
7 | return volunteers.filter(_.bind(function (a) {
8 | return a.checkedInTo(this.get('id'));
9 | }, this)).length;
10 | },
11 |
12 | checkedInStudents: function (students) {
13 | return students.filter(_.bind(function (a) {
14 | return a.checkedInTo(this.get('id'));
15 | }, this)).length;
16 | }
17 | });
--------------------------------------------------------------------------------
/app/assets/javascripts/services/footer.js:
--------------------------------------------------------------------------------
1 | window.whenReady(function () {
2 | if ($(window).height() < $('html').height()) {
3 | $('footer').removeClass('hide');
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/app/assets/javascripts/services/matchHeights.js:
--------------------------------------------------------------------------------
1 | window.resizeHeightMatchingItems = function ($element) {
2 | var items = $element.find('.js-match-height-item:not(.hide)');
3 | items.css('min-height', 0);
4 | var $items = items.map(function (ix, item) { return $(item); });
5 |
6 | var itemsByTop = {};
7 | _.each($items, function($item, ix) {
8 | var top = $item.offset().top;
9 | itemsByTop[top] = itemsByTop[top] || [];
10 | itemsByTop[top].push($item);
11 | });
12 |
13 | _.each(itemsByTop, function (items, top) {
14 | var maxHeight = Math.max.apply(Math, _.map(items, function($item) {
15 | return $item.outerHeight();
16 | }));
17 | _.each(items, function($item, ix) {
18 | $item.css('min-height', maxHeight);
19 | });
20 | });
21 | };
22 |
23 | window.whenReady(function () {
24 | function setupHeightMatching ($element) {
25 | $(window).resize(_.debounce(function() {
26 | if ($(document).width() > 768) {
27 | resizeHeightMatchingItems($element);
28 | }
29 | }, 100));
30 |
31 | setTimeout(function () {
32 | resizeHeightMatchingItems($element);
33 | });
34 | }
35 |
36 | setupHeightMatching($('body'));
37 | });
38 |
--------------------------------------------------------------------------------
/app/assets/javascripts/sign_in.js.coffee:
--------------------------------------------------------------------------------
1 | $ ->
2 | $("#sign_in_dialog").modal('hide')
3 |
4 | $(document).on 'click', '.sign-in-button', (e) ->
5 | $link = $(e.target)
6 |
7 | $modal = $($link.attr('href'))
8 | $modal.on 'shown.bs.modal', () ->
9 | $(this).find('#user_email').focus()
10 |
11 | url = document.createElement('a');
12 | url.href = $modal.find('form').attr('action');
13 |
14 | buildReturnParam = (key, href) ->
15 | key + '=' + encodeURIComponent(href)
16 |
17 | return_to_link = $link.data('returnTo')
18 | url.search = if return_to_link then buildReturnParam('return_to', return_to_link) else null
19 |
20 | $modal.find('.external-auth a').each (ix, el) ->
21 | el.search = buildReturnParam('origin', return_to_link || '/')
22 |
23 | sign_up_return_to_link = $link.data('signUpReturnTo')
24 | if sign_up_return_to_link
25 | sign_up_link = $modal.find('.sign_up_link').attr('href')
26 | $modal.find('.sign_up_link').attr('href', sign_up_link + '?' + buildReturnParam('return_to', sign_up_return_to_link))
27 |
28 | $modal.find('form').attr('action', url.pathname + url.search)
29 | $modal.modal();
30 | false
31 |
--------------------------------------------------------------------------------
/app/assets/javascripts/toggling.js.coffee:
--------------------------------------------------------------------------------
1 | setUpToggles = ->
2 | getSelectedValue = ($input) ->
3 | if $input.attr('type') == 'radio'
4 | $("input[type=radio][name='#{$input.attr('name')}']:checked").val()
5 | else if $input.attr('type') == 'checkbox'
6 | $input.filter(':checked').val()
7 |
8 | inputChanged = ->
9 | $input = $(this)
10 | selector = $input.data('toggle-target')
11 | $togglables = $(selector)
12 |
13 | if $input.data('toggle-show-when')
14 | visibleValue = $input.data('toggle-show-when').toString()
15 | checked = getSelectedValue($input) == visibleValue
16 |
17 | $togglables.toggle(checked)
18 | $togglables.find('input, select').prop('disabled', !checked)
19 |
20 | if $input.data('toggle-enable-when-checked')
21 | $togglables.prop('disabled', !$input.prop('checked'))
22 |
23 | $('[data-toggle-target]').on('change', inputChanged)
24 | $('[data-toggle-target]').each (ix, el) ->
25 | inputChanged.call(el)
26 |
27 | jQuery ->
28 | setUpToggles()
29 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_alerts.scss:
--------------------------------------------------------------------------------
1 | @import 'constants';
2 |
3 | .alert.alert-danger {
4 | h2 {
5 | margin: 0;
6 | }
7 | }
8 |
9 | .alert-action {
10 | border: 1px dashed $red-color;
11 | border-radius: 5px;
12 | padding: 15px;
13 | margin: 10px 0;
14 | }
15 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_checkins.scss:
--------------------------------------------------------------------------------
1 | .popover-content {
2 | ul {
3 | margin: 0 0 10px 10px;
4 | list-style-type: circle;
5 | }
6 |
7 | li {
8 | margin-bottom: 6px;
9 | }
10 |
11 | li:last-child {
12 | margin-bottom: 0;
13 | }
14 | }
15 |
16 | .checkins-action {
17 | &.saving {
18 | .toggle_rsvp_session {
19 | display: none !important;
20 | }
21 | }
22 |
23 | .destroy {
24 | display: none;
25 | }
26 |
27 | &.checked-in {
28 | .create {
29 | display: none;
30 | }
31 | .destroy {
32 | display: block;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/_constants.scss:
--------------------------------------------------------------------------------
1 | $level-blue: #2F96B4;
2 | $level-green: #51A351;
3 | $level-gold: #fcaa2f;
4 | $level-purple: #843178;
5 | $level-orange: #e34d0b;
6 | $level-pink: #ff0490;
7 |
8 | $red-color: #e34d0b;
9 | $orange-color: #ffcc66;
10 | $yellow-color: #fff17f;
11 | $green-color: #a5e587;
12 | $blue-color: #4290cb;
13 | $darkblue-color: #23507a;
14 | $black-color: #333;
15 | $darkgray-color: #777;
16 |
17 | $content-background-color: white;
18 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_font_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin font-amble-light() {
2 | font-family: 'AmbleLight', sans-serif;
3 | }
4 |
5 | @mixin font-amble-regular() {
6 | font-family: 'AmbleRegular', sans-serif;
7 | }
8 |
9 | @mixin font-amble-bold() {
10 | font-family: 'AmbleBold', sans-serif;
11 | }
12 |
13 | @mixin font-freehand() {
14 | font-family: 'Freehand521BT-RegularC', sans-serif;
15 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/_fonts.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Freehand521BT-RegularC';
3 | src: font_url('freehand.woff') format('woff');
4 | }
5 |
6 | @font-face {
7 | font-family: 'AmbleLight';
8 | src: font_url('Amble-Light-webfont.woff') format('woff');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 |
13 | @font-face {
14 | font-family: 'AmbleRegular';
15 | src: font_url('Amble-Regular-webfont.woff') format('woff');
16 | font-weight: normal;
17 | font-style: normal;
18 | }
19 |
20 | @font-face {
21 | font-family: 'AmbleBold';
22 | src: font_url('Amble-Bold-webfont.woff') format('woff');
23 | font-weight: normal;
24 | font-style: normal;
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_footer.scss:
--------------------------------------------------------------------------------
1 | .form-date {
2 | label {
3 | display: block;
4 | }
5 |
6 | select.form-control {
7 | width: 33%;
8 | display: inline-block;
9 | }
10 | }
11 |
12 | footer {
13 |
14 | .footerlinks {
15 | margin: 0 auto;
16 | padding: 1em 0;
17 | background-color: #f32b00;
18 | width: 100%;
19 | text-align: center;
20 | }
21 |
22 | .footerlinks a {
23 | margin: 0 0.6em;
24 | color: #fff;
25 | }
26 |
27 | .trollholder {
28 | height: 113px;
29 | width: 224px;
30 | margin: 0 auto;
31 | position: relative;
32 | overflow: hidden;
33 | }
34 |
35 | img {
36 | box-sizing: border-box;
37 | position: absolute;
38 | height: 133px;
39 | width: 224px;
40 | bottom: -19px;
41 | }
42 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/_gravatars.scss:
--------------------------------------------------------------------------------
1 | #gravatar {
2 | width: 115px;
3 | padding: 15px 0 15px 0;
4 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin column-count($count) {
2 | -moz-column-count: $count;
3 | -webkit-column-count: $count;
4 | column-count: $count;
5 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/_profile.scss:
--------------------------------------------------------------------------------
1 | .user-profile {
2 | section {
3 | margin-bottom: 15px;
4 | }
5 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/_style_guide.scss.erb:
--------------------------------------------------------------------------------
1 | @import "constants";
2 |
3 | .style-guide {
4 | ul.swatches {
5 | margin: 10px 0;
6 |
7 | li {
8 | display: inline-block;
9 | margin-right: 20px;
10 | }
11 |
12 | .swatch {
13 | width: 60px;
14 | height: 60px;
15 |
16 | <% %w(red orange yellow green blue black).each do |color| %>
17 | &.<%= color %>-color {
18 | background-color: $<%= color %>-color;
19 | }
20 | <% end %>
21 | }
22 | }
23 |
24 | .background-swatch {
25 | width: 200px;
26 | height: 200px;
27 | display: inline-block;
28 | background-image: image_url('background.png');
29 | }
30 |
31 | .paragraph-sample {
32 | margin-top: 30px;
33 | }
34 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | *= require jquery-ui/datepicker
3 | *= require bootstrap_with_overrides
4 | *= require datatables.net-bs/css/dataTables.bootstrap
5 | *= require select2/dist/css/select2
6 | *= require font_awesome_with_extensions
7 | *= require _alerts
8 | *= require _fonts
9 | *= require _style_guide
10 | *= require _base
11 | *= require _header
12 | *= require _footer
13 | *= require _tables
14 | *= require _section_organizer
15 | *= require _checkins
16 | *= require events/_index
17 | *= require events/_show
18 | *= require events/_organize
19 | *= require events/_email
20 | *= require events/_form
21 |
22 | *= require _gravatars
23 |
24 | *= require devise
25 | *= require _levels
26 | *= require _form
27 | *= require _profile
28 |
29 | *= require _external_pages
30 | *= require utilities
31 | */
32 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/bootstrap_with_overrides.scss:
--------------------------------------------------------------------------------
1 | $tableBorder: #BFBFBF;
2 |
3 | @import "bootstrap";
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/events/_email.scss:
--------------------------------------------------------------------------------
1 | .event_email {
2 | .mail-count {
3 | margin: 20px 0;
4 | }
5 |
6 | textarea {
7 | height: 275px;
8 | }
9 |
10 | label {
11 | line-height: 20px;
12 | margin: 0;
13 | }
14 |
15 | input[type=text], .select2-container {
16 | margin-bottom: 20px;
17 | }
18 |
19 | input[type=radio], input[type=checkbox] {
20 | height: 20px;
21 | margin: 5px 5px 5px 0;
22 | }
23 |
24 | .recipients-selection {
25 | label {
26 | display: block;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font_awesome_with_extensions.scss:
--------------------------------------------------------------------------------
1 | @import "font-awesome";
2 |
3 | .fa-before:before {
4 | @extend .fa;
5 | }
6 |
7 | .btn.fa-before:before {
8 | margin-right: 10px;
9 | }
10 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/utilities.scss:
--------------------------------------------------------------------------------
1 | .mb20 {
2 | margin-bottom: 20px;
3 | }
4 |
5 | .mt20 {
6 | margin-top: 20px;
7 | }
8 |
9 | .mt10 {
10 | margin-top: 10px;
11 | }
12 |
13 | .p20 {
14 | padding: 20px;
15 | }
16 |
17 | .inline-block {
18 | display: inline-block;
19 | }
20 |
21 | .whitespace-nowrap {
22 | white-space: nowrap;
23 | }
24 |
--------------------------------------------------------------------------------
/app/assets/templates/class_levels_popover.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#each description}}
3 | {{this}}
4 | {{/each}}
5 |
--------------------------------------------------------------------------------
/app/assets/templates/email_attendees_popover.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#each recipients}}
4 | {{this.full_name}} <{{this.email}}>
5 | {{/each}}
6 |
7 |
--------------------------------------------------------------------------------
/app/assets/templates/imported_event_popover.hbs:
--------------------------------------------------------------------------------
1 |
2 | This event originally took RSVPs on {{type}}
3 |
4 |
7 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/assets/templates/section_organizer/edit_section.hbs:
--------------------------------------------------------------------------------
1 |
31 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationController < ActionController::Base
4 | protect_from_forgery
5 |
6 | include Pundit::Authorization
7 | rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
8 | after_action :verify_authorized, unless: :devise_controller?
9 |
10 | before_action :configure_permitted_parameters, if: :devise_controller?
11 |
12 | before_action do
13 | Rack::MiniProfiler.authorize_request if current_user.try(:admin?)
14 | end
15 |
16 | rescue_from(ActionView::MissingTemplate) do |_e|
17 | raise if request.format == :html
18 |
19 | head(:not_acceptable)
20 | end
21 |
22 | def after_sign_in_path_for(resource)
23 | params[:return_to] || super
24 | end
25 |
26 | protected
27 |
28 | def configure_permitted_parameters
29 | devise_parameter_sanitizer.permit(:sign_up) do |u|
30 | u.permit(policy(User).permitted_attributes + [{ region_ids: [] }])
31 | end
32 | end
33 |
34 | def user_not_authorized
35 | flash[:error] = 'You are not authorized to perform this action.'
36 | redirect_to(request.referer || root_path)
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/controllers/event_sessions_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class EventSessionsController < ApplicationController
4 | before_action :authenticate_user!, only: %i[index destroy]
5 | before_action :find_event
6 |
7 | def index
8 | authorize @event, :checkin?
9 | @checkin_counts = @event.checkin_counts
10 | end
11 |
12 | def show
13 | skip_authorization
14 | event_session = @event.event_sessions.find(params[:id])
15 | ics = IcsGenerator.new.event_session_ics(event_session)
16 |
17 | respond_to do |format|
18 | format.ics { render body: ics, layout: false }
19 | format.all { head :not_found }
20 | end
21 | end
22 |
23 | def destroy
24 | authorize @event, :edit?
25 | event_session = @event.event_sessions.find(params[:id])
26 | if @event.event_sessions.count > 1 && !event_session.any_rsvps?
27 | event_session.destroy
28 | flash[:notice] = "Session #{event_session.name} deleted!"
29 | else
30 | flash[:notice] = "Can't delete that session!"
31 | end
32 | redirect_to edit_event_path(@event)
33 | end
34 |
35 | private
36 |
37 | def find_event
38 | @event = Event.find(params[:event_id])
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/app/controllers/events/attendee_names_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'csv'
4 |
5 | module Events
6 | class AttendeeNamesController < ApplicationController
7 | before_action :authenticate_user!
8 | before_action :find_event
9 |
10 | def index
11 | authorize @event, :edit?
12 |
13 | rsvps = @event.rsvps.where(role_id: Role.attendee_role_ids_with_organizers)
14 |
15 | respond_to do |format|
16 | format.csv do
17 | send_data(attendee_csv_data(rsvps), type: :csv)
18 | end
19 | end
20 | end
21 |
22 | private
23 |
24 | def find_event
25 | @event = Event.find_by(id: params[:event_id])
26 | end
27 |
28 | def attendee_csv_data(rsvps)
29 | CSV.generate do |csv|
30 | csv << ['Last Name', 'First Name']
31 |
32 | rsvps.includes(:user).joins(:bridgetroll_user).order('users.last_name ASC, users.first_name ASC').each do |rsvp|
33 | csv << [rsvp.user.last_name, rsvp.user.first_name]
34 | end
35 | end
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/controllers/events/students_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'csv'
4 |
5 | module Events
6 | class StudentsController < ApplicationController
7 | before_action :authenticate_user!
8 | before_action :find_event
9 |
10 | def index
11 | authorize @event, :edit?
12 | @event = Event.find(params[:event_id])
13 | @students = @event.student_rsvps
14 | respond_to do |format|
15 | format.csv { send_data student_csv_data(@students), type: :csv }
16 | format.html
17 | end
18 | end
19 |
20 | private
21 |
22 | def find_event
23 | @event = Event.find_by(id: params[:event_id])
24 | end
25 |
26 | def student_csv_data(rsvps)
27 | CSV.generate do |csv|
28 | csv << [
29 | 'Student Name',
30 | 'Class Level',
31 | 'Operating System',
32 | 'Occupation',
33 | 'Gender'
34 | ]
35 |
36 | rsvps.each do |rsvp|
37 | csv << [
38 | rsvp.user.full_name,
39 | rsvp.class_level,
40 | rsvp.operating_system.name,
41 | rsvp.job_details,
42 | rsvp.user.gender
43 | ]
44 | end
45 | end
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/app/controllers/events/surveys_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Events
4 | class SurveysController < ApplicationController
5 | before_action :authenticate_user!
6 | before_action :find_event
7 |
8 | def edit
9 | authorize @event, :edit?
10 | end
11 |
12 | private
13 |
14 | def find_event
15 | @event = Event.find(params[:event_id])
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/controllers/meetup_users_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class MeetupUsersController < ApplicationController
4 | before_action :authenticate_user!
5 |
6 | def show
7 | skip_authorization
8 | @user = MeetupUser.find(params[:id])
9 | @rsvps = @user.rsvps.includes(:event)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/controllers/profiles_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ProfilesController < ApplicationController
4 | before_action :authenticate_user!
5 | before_action :load_user_and_profile
6 |
7 | def show
8 | skip_authorization
9 | end
10 |
11 | protected
12 |
13 | def load_user_and_profile
14 | @user = User.includes(:profile).references(:profile).find(params[:user_id])
15 | @profile = @user.profile
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/controllers/sections_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class SectionsController < ApplicationController
4 | before_action :authenticate_user!
5 | before_action :find_event
6 |
7 | def create
8 | authorize @event, :edit?
9 | section = @event.sections.create!(name: 'New Section')
10 | render json: section
11 | end
12 |
13 | def update
14 | authorize @event, :edit?
15 | section = @event.sections.find(params[:id])
16 | if section.update(section_params)
17 | render json: section
18 | else
19 | render json: {}, status: :unprocessable_entity
20 | end
21 | end
22 |
23 | def destroy
24 | authorize @event, :edit?
25 | section = @event.sections.find(params[:id])
26 | section.destroy
27 | render json: {}
28 | end
29 |
30 | def arrange
31 | authorize @event, :edit?
32 | SectionArranger.new(@event).arrange(params[:checked_in_to])
33 | redirect_to event_organize_sections_path(@event)
34 | end
35 |
36 | private
37 |
38 | def find_event
39 | @event = Event.find_by(id: params[:event_id])
40 | end
41 |
42 | def section_params
43 | permitted_attributes(Section)
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/app/controllers/static_pages_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class StaticPagesController < ApplicationController
4 | before_action :skip_authorization
5 |
6 | def style_guide; end
7 |
8 | def about; end
9 | end
10 |
--------------------------------------------------------------------------------
/app/controllers/users/events_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Users
4 | class EventsController < ApplicationController
5 | # Provides a simple, public, mini-api to allow third parties to check to
6 | # see if a user has attended any classes
7 | def index
8 | skip_authorization
9 | @user = User.find(params[:user_id])
10 | @event_count = @user.rsvps.where('checkins_count > 0').count
11 | render json: { event_count: @event_count }, status: :ok
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class UsersController < ApplicationController
4 | before_action :authenticate_user!
5 |
6 | def index
7 | skip_authorization
8 | respond_to do |format|
9 | format.html
10 | format.json do
11 | render json: UserList.new(params)
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/controllers/volunteers_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class VolunteersController < ApplicationController
4 | before_action :authenticate_user!
5 | before_action :find_event
6 |
7 | def index
8 | authorize @event, :edit?
9 | @volunteer_rsvps = @event.volunteer_rsvps
10 | end
11 |
12 | private
13 |
14 | def find_event
15 | @event = Event.find(params[:event_id])
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationHelper
4 | def gravatar_image_tag(email, size:, **)
5 | hash = Digest::MD5.hexdigest(email)
6 | tag.img(**, src: "https://secure.gravatar.com/avatar/#{hash}.png?s=#{size}")
7 | end
8 |
9 | def resource_name
10 | :user
11 | end
12 |
13 | def resource
14 | User.new
15 | end
16 |
17 | def devise_mapping
18 | Devise.mappings[:user]
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/helpers/locations_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module LocationsHelper
4 | def pretty_print_address(location)
5 | safe_join(location_array(location).map { |line| tag.div(line) }, '')
6 | end
7 |
8 | def location_map_link(location)
9 | "http://maps.google.com/?q=#{Rack::Utils.escape(location.full_address)}"
10 | end
11 |
12 | private
13 |
14 | def location_array(location)
15 | [
16 | location.name,
17 | location.address_1,
18 | location.address_2.presence,
19 | "#{location.city}, #{location.state} #{location.zip}"
20 | ].compact
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/helpers/profiles_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ProfilesHelper
4 | def skills
5 | [
6 | [:childcaring, 'Childcare'],
7 | [:writing, 'Writer'],
8 | [:designing, 'Designer'],
9 | [:mentoring, 'Mentor'],
10 | [:outreach],
11 | [:macosx, 'Mac OS X'],
12 | [:windows],
13 | [:linux]
14 | ]
15 | end
16 |
17 | def skills?(user)
18 | skills.any? { |(skill_symbol, _)| user.profile[skill_symbol] }
19 | end
20 |
21 | def skill_title(skill)
22 | skill[1].presence || skill[0].to_s.titlecase
23 | end
24 |
25 | def skill_symbol(skill)
26 | skill[0]
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/helpers/responsive_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ResponsiveHelper
4 | def content_tag_maybe_hidden(tag, content, params = {})
5 | params[:class] = "#{params[:class]} hide-on-phone" if content.blank?
6 | content_tag tag, params do
7 | content
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/jobs/survey_sender.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class SurveySender
4 | FIRST_AUTO_SEND_DATE = Date.parse('2015-11-15')
5 |
6 | def self.send_all_surveys
7 | Event.where('ends_at > ?', FIRST_AUTO_SEND_DATE)
8 | .where(ends_at: ...1.day.ago)
9 | .where(survey_sent_at: nil).each do |event|
10 | send_surveys(event)
11 | end
12 | end
13 |
14 | def self.send_surveys(event)
15 | return if event.survey_sent_at.present?
16 |
17 | attendee_rsvps = event.attendee_rsvps.where('checkins_count > 0')
18 | attendee_rsvps.each do |rsvp|
19 | SurveyMailer.notification(rsvp).deliver_now
20 | end
21 | event.update_attribute(:survey_sent_at, DateTime.now)
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/mailers/admin_mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AdminMailer < ApplicationMailer
4 | def test_group_mail(options)
5 | set_recipients([options[:to]])
6 |
7 | mail(
8 | from: 'events@bridgefoundry.org',
9 | subject: '[Bridge Troll] Group Email Test'
10 | ) do |format|
11 | format.html { render html: mail_content('group') }
12 | end
13 | end
14 |
15 | def test_individual_mail(options)
16 | mail(
17 | to: options[:to],
18 | from: 'events@bridgefoundry.org',
19 | subject: '[Bridge Troll] Individual Email Test'
20 | ) do |format|
21 | format.html { render html: mail_content('individual') }
22 | end
23 | end
24 |
25 | private
26 |
27 | def mail_content(type)
28 | <<~MAIL_CONTENT
29 | Hey there!
30 |
31 | This is a test message from events.bridgefoundry.org!
32 |
33 | If you received it, it means that #{type} emails can probably be sent successfully from #{Rails.configuration.action_mailer.default_url_options[:host]}
34 | MAIL_CONTENT
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationMailer < ActionMailer::Base
4 | layout 'mailer'
5 | append_view_path Rails.root.join('app/views/mailers')
6 |
7 | private
8 |
9 | def set_recipients(recipients, cc = nil)
10 | # Sendgrid API allows a single SMTP request to send multiple
11 | # email messages. Change this to something else if we move
12 | # away from Sendgrid.
13 | # http://sendgrid.com/docs/API_Reference/SMTP_API/
14 |
15 | recipients += cc unless cc.nil?
16 |
17 | headers['X-SMTPAPI'] = {
18 | to: recipients
19 | }.to_json
20 | headers[:to] = 'events@bridgefoundry.org' # supposedly required even with X-SMTPAPI
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/mailers/survey_mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class SurveyMailer < ApplicationMailer
4 | helper(EventsHelper)
5 | def notification(rsvp)
6 | @rsvp = rsvp
7 | mail(
8 | to: rsvp.user.email,
9 | subject: "How was #{rsvp.event.title}?"
10 | )
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationRecord < ActiveRecord::Base
4 | self.abstract_class = true
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/authentication.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Authentication < ApplicationRecord
4 | validates :provider, uniqueness: { scope: [:uid] }
5 |
6 | belongs_to :user, inverse_of: :authentications, counter_cache: true
7 |
8 | after_commit :authentication_created, on: :create
9 |
10 | def authentication_created
11 | OmniauthProviders.finish_auth_for(self)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/models/chapter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Chapter < ApplicationRecord
4 | belongs_to :organization, inverse_of: :chapters
5 | has_many :events, dependent: :nullify
6 | has_many :external_events, dependent: :nullify
7 | has_many :chapter_leaderships, dependent: :destroy
8 | has_many :leaders, through: :chapter_leaderships, source: :user
9 |
10 | validates :name, presence: true
11 | validates :name, uniqueness: true
12 |
13 | def leader?(user)
14 | return false unless user
15 |
16 | user.admin? || user.chapter_leaderships.map(&:chapter_id).include?(id)
17 | end
18 |
19 | def destroyable?
20 | (events_count + external_events_count).zero?
21 | end
22 |
23 | def code_of_conduct_url
24 | organization.code_of_conduct_url || Event::DEFAULT_CODE_OF_CONDUCT_URL
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/models/chapter_leadership.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ChapterLeadership < ApplicationRecord
4 | belongs_to :chapter
5 | belongs_to :user, inverse_of: :chapter_leaderships
6 |
7 | validates :user, uniqueness: { scope: :chapter }
8 | end
9 |
--------------------------------------------------------------------------------
/app/models/course.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Course < ApplicationRecord
4 | has_many :levels, dependent: :destroy
5 | has_many :events, dependent: :nullify
6 | validates :name, presence: true
7 | validates :title, presence: true
8 | validates :description, presence: true
9 |
10 | accepts_nested_attributes_for :levels, allow_destroy: true
11 | validates :levels, length: { maximum: 5 }
12 | validate :unique_level_positions
13 | validate :unique_level_colors
14 |
15 | def unique_level_positions
16 | unique_level_values(:num, 'position must be unique')
17 | end
18 |
19 | def unique_level_colors
20 | unique_level_values(:color, 'color must be unique')
21 | end
22 |
23 | private
24 |
25 | def unique_level_values(field, message)
26 | return if levels.map(&field).length == levels.map(&field).uniq.length
27 |
28 | errors.add(:level, message)
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/models/dietary_restriction.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class DietaryRestriction < ApplicationRecord
4 | belongs_to :rsvp
5 |
6 | DIETS = %w[vegetarian vegan gluten-free dairy-free].freeze
7 |
8 | validates :restriction, uniqueness: { scope: :rsvp_id }
9 |
10 | validates :restriction, inclusion: { in: DIETS }
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/event_details/default_details.html:
--------------------------------------------------------------------------------
1 | Workshop Description
2 |
3 | Sponsors
4 |
5 | Transportation and Parking
6 |
7 | Food and Drinks
8 |
9 | Childcare
10 |
11 | Afterparty
12 |
--------------------------------------------------------------------------------
/app/models/event_details/default_student_details.html:
--------------------------------------------------------------------------------
1 | All students need to bring their own laptop and powercord.
2 |
3 | Since bandwidth is usually at a premium at the Installfest, please download RailsInstaller (for PCs and most Mac installations) or XCode (if you're going that route).
4 |
5 | You can find more information on what to download by getting started with the Installfest instructions: http://docs.railsbridge.org/installfest
6 |
--------------------------------------------------------------------------------
/app/models/event_details/default_survey_greeting.html:
--------------------------------------------------------------------------------
1 | Thanks so much for attending!
2 |
3 | We love feedback, so let us know how things went for you and anything you think we could make better in the future!
--------------------------------------------------------------------------------
/app/models/event_details/default_volunteer_details.html:
--------------------------------------------------------------------------------
1 | Be sure to review the curriculum before the workshop. We have several curricula available at http://docs.railsbridge.org .
2 |
--------------------------------------------------------------------------------
/app/models/event_email.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class EventEmail < ApplicationRecord
4 | attr_accessor :attendee_group, :include_waitlisted, :only_checked_in, :cc_organizers
5 |
6 | belongs_to :event
7 | belongs_to :sender, class_name: 'User'
8 |
9 | has_many :event_email_recipients, dependent: :destroy
10 | has_many :recipient_rsvps, through: :event_email_recipients
11 | has_many :recipients, through: :recipient_rsvps, source: :user, source_type: 'User'
12 |
13 | validates :subject, :body, presence: true
14 | end
15 |
--------------------------------------------------------------------------------
/app/models/event_email_recipient.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class EventEmailRecipient < ApplicationRecord
4 | belongs_to :event_email
5 | belongs_to :recipient_rsvp, class_name: 'Rsvp', inverse_of: :event_email_recipients
6 | end
7 |
--------------------------------------------------------------------------------
/app/models/level.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Level < ApplicationRecord
4 | COLORS = %w[blue green gold purple orange pink].freeze
5 |
6 | default_scope { order(:num) }
7 |
8 | belongs_to :course, optional: true
9 | validates :num, presence: true,
10 | inclusion: {
11 | in: (1..5),
12 | message: 'Must be between 1 and 5'
13 | }
14 | validates :color, presence: true,
15 | inclusion: {
16 | in: COLORS,
17 | message: "Must be one of: #{COLORS.sort.join(', ')}"
18 | }
19 | validates :title, presence: true
20 | validates :level_description, presence: true
21 | serialize :level_description, type: Array, coder: YAML
22 | alias_attribute :description, :level_description
23 |
24 | def level_description_bullets
25 | level_description.map { |line| "* #{line}" }.join("\n")
26 | end
27 |
28 | def level_description_bullets=(value)
29 | self.level_description = value.split("\n").map { |line| line.gsub(/^\s*\*\s*/, '').strip }
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/app/models/meetup_user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class MeetupUser < ApplicationRecord
4 | has_many :rsvps, -> { where user_type: 'MeetupUser' }, foreign_key: 'user_id', inverse_of: :meetup_user
5 | has_many :events, through: :rsvps
6 |
7 | def email
8 | '(email not available)'
9 | end
10 |
11 | def profile
12 | @profile ||= Profile.new.tap do |p|
13 | Profile.attribute_names.each { |attrib| p[attrib] = false }
14 | p.readonly!
15 | end
16 | end
17 |
18 | def profile_path
19 | "/meetup_users/#{id}"
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/models/organization.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'csv'
4 |
5 | class Organization < ApplicationRecord
6 | has_many :chapters, dependent: :destroy, inverse_of: :organization
7 | has_many :organization_leaderships, dependent: :destroy
8 | has_many :leaders, through: :organization_leaderships, source: :user
9 | has_many :organization_subscriptions, dependent: :destroy
10 | has_many :users, through: :organization_subscriptions
11 |
12 | def leader?(user)
13 | return false unless user
14 |
15 | user.admin? || user.organization_leaderships.map(&:organization_id).include?(id)
16 | end
17 |
18 | def subscription_csv
19 | CSV.generate do |csv|
20 | csv << %w[email first_name last_name]
21 | users.each do |user|
22 | csv << [user.email, user.first_name, user.last_name]
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/models/organization_leadership.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class OrganizationLeadership < ApplicationRecord
4 | belongs_to :organization
5 | belongs_to :user, inverse_of: :organization_leaderships
6 |
7 | validates :user, uniqueness: { scope: :organization }
8 | end
9 |
--------------------------------------------------------------------------------
/app/models/organization_subscription.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class OrganizationSubscription < ApplicationRecord
4 | belongs_to :user
5 | belongs_to :subscribed_organization, class_name: 'Organization', foreign_key: :organization_id,
6 | inverse_of: :organization_subscriptions
7 | end
8 |
--------------------------------------------------------------------------------
/app/models/profile.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Profile < ApplicationRecord
4 | belongs_to :user, inverse_of: :profile
5 |
6 | after_initialize :remove_at_from_twitter_username
7 |
8 | validates :github_username, format: { with: /\A([a-z0-9-]+-)*[a-z0-9]+\Z/i, allow_blank: true }
9 | validates :twitter_username, format: { with: /\A@?\w{1,15}\Z/i, allow_blank: true }
10 |
11 | validates :user_id, uniqueness: true
12 |
13 | def remove_at_from_twitter_username
14 | self.twitter_username = twitter_username.try(:gsub, /\A@*/, '')
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/models/region.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Region < ApplicationRecord
4 | has_many :locations, dependent: :nullify
5 | has_many :events, through: :locations
6 | has_many :external_events, dependent: :nullify
7 | has_many :regions_users, dependent: :destroy
8 | has_many :users, through: :regions_users
9 | has_many :region_leaderships, dependent: :destroy
10 | has_many :leaders, through: :region_leaderships, source: :user
11 |
12 | validates :name, presence: true
13 | validates :name, uniqueness: true
14 |
15 | def leader?(user)
16 | return false unless user
17 |
18 | user.admin? || user.region_leaderships.map(&:region_id).include?(id)
19 | end
20 |
21 | def destroyable?
22 | (locations_count + external_events_count).zero?
23 | end
24 |
25 | def as_json(_options = {})
26 | {
27 | name: name,
28 | users_subscribed_to_email_count: users.where(allow_event_email: true).count
29 | }
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/app/models/region_leadership.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RegionLeadership < ApplicationRecord
4 | belongs_to :region, inverse_of: :region_leaderships
5 | belongs_to :user, inverse_of: :region_leaderships
6 |
7 | validates :user, uniqueness: { scope: :region }
8 | end
9 |
--------------------------------------------------------------------------------
/app/models/regions_user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RegionsUser < ApplicationRecord
4 | belongs_to :user, inverse_of: :regions_users
5 | belongs_to :region, inverse_of: :regions_users
6 | end
7 |
--------------------------------------------------------------------------------
/app/models/role.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Role < ActiveHash::Base
4 | include ActiveHash::Enum
5 | self.data = [
6 | { id: 1, name: 'STUDENT', title: 'Student' },
7 | { id: 2, name: 'VOLUNTEER', title: 'Volunteer' },
8 | { id: 3, name: 'ORGANIZER', title: 'Organizer' }
9 | ]
10 | enum_accessor :name
11 |
12 | def self.attendee_role_ids
13 | [Role::VOLUNTEER.id, Role::STUDENT.id]
14 | end
15 |
16 | def self.attendee_role_ids_with_organizers
17 | [Role::VOLUNTEER.id, Role::STUDENT.id, Role::ORGANIZER.id]
18 | end
19 |
20 | def self.empty_attendance
21 | Role.all.each_with_object({}) do |role, hsh|
22 | hsh[role.id] = 0
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/models/rsvp_session.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RsvpSession < ApplicationRecord
4 | belongs_to :rsvp
5 | belongs_to :event_session
6 |
7 | validates :rsvp_id, uniqueness: { scope: :event_session_id }
8 |
9 | after_destroy :update_counter_cache
10 | after_save :update_counter_cache
11 |
12 | def user_full_name
13 | rsvp.user.full_name
14 | end
15 |
16 | def update_counter_cache
17 | rsvp.checkins_count = rsvp.rsvp_sessions.where('rsvp_sessions.checked_in' => true).count
18 | rsvp.save
19 | end
20 |
21 | def as_json(options = {})
22 | super.merge(role_id: rsvp.role_id)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/models/section.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Section < ApplicationRecord
4 | belongs_to :event
5 | has_many :rsvps, dependent: :nullify
6 |
7 | def student_rsvps
8 | rsvps.where(role_id: Role::STUDENT.id)
9 | end
10 |
11 | def volunteer_rsvps
12 | rsvps.where(role_id: Role::VOLUNTEER.id)
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/models/survey.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Survey < ApplicationRecord
4 | belongs_to :rsvp
5 |
6 | validates :rsvp_id, uniqueness: { message: 'Only one survey response allowed per user.' }
7 | validates :recommendation_likelihood, allow_blank: true,
8 | numericality: { only_integer: true, greater_than: 0, less_than: 11 }
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/volunteer_assignment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class VolunteerAssignment < ActiveHash::Base
4 | include ActiveHash::Enum
5 | self.data = [
6 | { id: 1, name: 'UNASSIGNED', title: 'Unassigned' },
7 | { id: 2, name: 'TEACHER', title: 'Teacher' },
8 | { id: 3, name: 'TA', title: 'TA' }
9 | ]
10 | enum_accessor :name
11 | end
12 |
--------------------------------------------------------------------------------
/app/models/volunteer_preference.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class VolunteerPreference < ActiveHash::Base
4 | include ActiveHash::Enum
5 | self.data = [
6 | { id: 1, name: 'NEITHER', title: 'Non-teaching volunteer' },
7 | { id: 2, name: 'TEACHER', title: 'Teacher' },
8 | { id: 3, name: 'TA', title: 'TA' },
9 | { id: 4, name: 'BOTH', title: 'No Preference' }
10 | ]
11 | enum_accessor :name
12 | end
13 |
--------------------------------------------------------------------------------
/app/policies/application_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationPolicy
4 | class Scope
5 | attr_reader :user, :scope
6 |
7 | def initialize(user, scope)
8 | @user = user
9 | @scope = scope
10 | end
11 | end
12 |
13 | attr_reader :user, :record
14 |
15 | def initialize(user, record)
16 | @user = user
17 | @record = record
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/policies/chapter_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ChapterPolicy < ApplicationPolicy
4 | def new?
5 | user && (user.admin? || user.organization_leaderships.present?)
6 | end
7 |
8 | def update?
9 | record.leader?(user) || record.organization.leader?(user)
10 | end
11 |
12 | def create?
13 | update?
14 | end
15 |
16 | def destroy?
17 | user&.admin?
18 | end
19 |
20 | def modify_leadership?
21 | update?
22 | end
23 |
24 | def permitted_attributes
25 | %i[
26 | name
27 | organization_id
28 | ]
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/policies/course_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CoursePolicy < ApplicationPolicy
4 | def index?
5 | user.admin?
6 | end
7 |
8 | def destroy?
9 | record.events.empty? && user.admin?
10 | end
11 |
12 | def edit?
13 | update?
14 | end
15 |
16 | def new?
17 | create?
18 | end
19 |
20 | def update?
21 | user.admin?
22 | end
23 |
24 | def create?
25 | user.admin?
26 | end
27 |
28 | def permitted_attributes
29 | return [] unless user&.admin?
30 |
31 | [
32 | :name,
33 | :title,
34 | :description,
35 | {
36 | levels_attributes: LevelPolicy.new(user, Level).permitted_attributes + [:id]
37 | }
38 | ]
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/app/policies/event_session_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class EventSessionPolicy < ApplicationPolicy
4 | def permitted_attributes
5 | %i[
6 | starts_at
7 | ends_at
8 | name
9 | required_for_students
10 | volunteers_only
11 | location_overridden
12 | location_id
13 | ]
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/policies/external_event_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ExternalEventPolicy < ApplicationPolicy
4 | def edit?
5 | user.admin? || user.external_event_editor?
6 | end
7 |
8 | def permitted_attributes
9 | %i[
10 | city
11 | ends_at
12 | location
13 | name
14 | organizers
15 | starts_at
16 | url
17 | region_id
18 | chapter_id
19 | ]
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/policies/level_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class LevelPolicy < ApplicationPolicy
4 | def permitted_attributes
5 | return [] unless user&.admin?
6 |
7 | %i[
8 | num
9 | color
10 | title
11 | level_description_bullets
12 | _destroy
13 | ]
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/policies/location_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class LocationPolicy < ApplicationPolicy
4 | def destroy?
5 | record.events.empty?
6 | end
7 |
8 | def archive?
9 | return false unless record.persisted?
10 | return false if record.archived?
11 |
12 | update? || edit_additional_details?
13 | end
14 |
15 | def update?
16 | record.events_count.zero? || user.admin? ||
17 | record.notable_events.map(&:organizers).flatten.map(&:id).include?(user.id)
18 | end
19 |
20 | def edit_additional_details?
21 | record.region&.leader?(user)
22 | end
23 |
24 | def permitted_attributes
25 | attributes = %i[
26 | name
27 | address_1
28 | address_2
29 | city
30 | state
31 | zip
32 | region_id
33 | ]
34 | if edit_additional_details?
35 | attributes += %i[
36 | contact_info
37 | notes
38 | ]
39 | end
40 | attributes
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/app/policies/organization_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class OrganizationPolicy < ApplicationPolicy
4 | class Scope < Scope
5 | def resolve
6 | if user.admin?
7 | scope.all
8 | else
9 | scope.where(id: user.organization_leaderships.map(&:organization_id))
10 | end
11 | end
12 | end
13 |
14 | def create?
15 | user&.admin?
16 | end
17 |
18 | def manage_organization?
19 | user && (user.admin? || record.leader?(user))
20 | end
21 |
22 | def permitted_attributes
23 | [
24 | :name
25 | ]
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/policies/profile_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ProfilePolicy < ApplicationPolicy
4 | def permitted_attributes
5 | %i[
6 | childcaring
7 | designing
8 | outreach
9 | linux
10 | macosx
11 | mentoring
12 | other
13 | windows
14 | writing
15 | bio
16 | github_username
17 | twitter_username
18 | ]
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/policies/region_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RegionPolicy < ApplicationPolicy
4 | def update?
5 | record.leader?(user)
6 | end
7 |
8 | def modify_leadership?
9 | record.leader?(user)
10 | end
11 |
12 | def permitted_attributes
13 | [
14 | :name
15 | ]
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/policies/rsvp_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RsvpPolicy < ApplicationPolicy
4 | def survey?
5 | record.user == user
6 | end
7 |
8 | def permitted_attributes
9 | [
10 | :subject_experience,
11 | :teaching,
12 | :taing,
13 | :teaching_experience,
14 | :teaching_experience,
15 | :childcare_info,
16 | :operating_system_id,
17 | :job_details,
18 | :class_level,
19 | :custom_question_answer,
20 | :dietary_info,
21 | :needs_childcare,
22 | :plus_one_host,
23 | {
24 | event_session_ids: [],
25 | dietary_restriction_diets: []
26 | }
27 | ]
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/app/policies/section_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class SectionPolicy < ApplicationPolicy
4 | def permitted_attributes
5 | %i[
6 | name
7 | class_level
8 | ]
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/policies/survey_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class SurveyPolicy < ApplicationPolicy
4 | def permitted_attributes
5 | %i[
6 | good_things
7 | bad_things
8 | appropriate_for_skill
9 | other_comments
10 | rsvp_id
11 | recommendation_likelihood
12 | ]
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/policies/user_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class UserPolicy < ApplicationPolicy
4 | def permitted_attributes
5 | [
6 | :first_name,
7 | :last_name,
8 | :email,
9 | :password,
10 | :password_confirmation,
11 | :remember_me,
12 | :time_zone,
13 | :gender,
14 | :allow_event_email,
15 | :current_password,
16 | {
17 | region_ids: [],
18 | subscribed_organization_ids: [],
19 | profile_attributes: ProfilePolicy.new(user, Profile).permitted_attributes
20 | }
21 | ]
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/presenters/event_email_presenter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class EventEmailPresenter < SimpleDelegator
4 | def rsvps
5 | @rsvps ||= event.rsvps.where(role_id: Role.attendee_role_ids).includes(:user)
6 | end
7 |
8 | def volunteers_rsvps
9 | rsvps.where(role_id: Role::VOLUNTEER.id)
10 | end
11 |
12 | def students_accepted_rsvps
13 | rsvps.where(role_id: Role::STUDENT.id).confirmed
14 | end
15 |
16 | def students_waitlisted_rsvps
17 | rsvps.where(role_id: Role::STUDENT.id).where.not('waitlist_position IS NULL')
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/presenters/past_event_emails_presenter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class PastEventEmailsPresenter
4 | attr_reader :event
5 |
6 | def initialize(event)
7 | @event = event
8 | end
9 |
10 | def emails
11 | event.event_emails.order(:created_at)
12 | end
13 |
14 | def recipient_counts
15 | counts = {}
16 |
17 | emails.each do |email|
18 | counts[email.id] = {
19 | total: email.recipient_rsvps.length,
20 | volunteers: 0,
21 | students: 0
22 | }
23 |
24 | email.recipient_rsvps.each do |rsvp|
25 | counts[email.id][:volunteers] += 1 if rsvp.role_volunteer?
26 | counts[email.id][:students] += 1 if rsvp.role_student?
27 | end
28 | end
29 |
30 | counts
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/services/account_merger.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AccountMerger
4 | def initialize(user_to_keep, user_to_merge)
5 | @user_to_keep = user_to_keep
6 | @user_to_merge = user_to_merge
7 | end
8 |
9 | def merge!
10 | raise "Can't merge the same user onto itself!" if @user_to_merge.id == @user_to_keep.id
11 |
12 | to_destroy = Rsvp.where(event_id: (
13 | @user_to_merge.rsvps.pluck('event_id') & @user_to_keep.rsvps.pluck('event_id')
14 | ), user_id: @user_to_merge.id, user_type: 'User')
15 |
16 | Rails.logger.info(<<~USER_PROMPT)
17 |
18 | Ready to merge #{user_desc(@user_to_merge)}'s data onto #{user_desc(@user_to_keep)}!
19 |
20 | #{to_destroy.count} RSVP(s) of #{user_desc(@user_to_merge)} will be DESTROYED
21 | #{@user_to_merge.rsvps.count - to_destroy.count} RSVP(s) will be adopted by #{user_desc(@user_to_keep)}
22 |
23 | Is this cool? (y/n)
24 | USER_PROMPT
25 |
26 | return unless $stdin.gets.chomp.casecmp('y')
27 |
28 | to_destroy.destroy_all
29 |
30 | @user_to_merge.rsvps.update_all(user_id: @user_to_keep.id)
31 | @user_to_merge.destroy
32 |
33 | true
34 | end
35 |
36 | private
37 |
38 | def user_desc(user)
39 | "#{user.full_name} (#{user.id})"
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/app/services/external_event_importer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'net/http'
4 | require 'csv'
5 |
6 | class ExternalEventImporter
7 | def import(url)
8 | return unless url
9 |
10 | uri = URI.parse(url)
11 | http = Net::HTTP.new(uri.host, uri.port)
12 | resp = http.get(uri.request_uri)
13 |
14 | return unless resp.code == '200'
15 |
16 | CSV.parse(resp.body.force_encoding('UTF-8'), headers: :first_line) do |line|
17 | date_template = '%m/%d/%Y'
18 | start_date = Date.strptime(line['START DATE'], date_template)
19 | ExternalEvent.create!(
20 | name: line['TITLE'] || 'Ruby on Rails Outreach Workshop for Women',
21 | starts_at: start_date,
22 | ends_at: line['END DATE'] ? Date.strptime(line['END DATE'], date_template) : start_date,
23 | url: line['URL'],
24 | city: line['CITY'],
25 | location: line['LOCATION'],
26 | organizers: line['ORGANIZERS']
27 | )
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/services/ics_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Generates downloadable calendar event files to support 'Export to iCal' feature
4 | class IcsGenerator
5 | require 'icalendar'
6 | require 'date'
7 |
8 | include Icalendar
9 |
10 | def event_session_ics(event_session)
11 | cal = Calendar.new
12 | cal.event do |e|
13 | e.dtstart = event_session.starts_at.to_datetime
14 | e.dtend = event_session.ends_at.to_datetime
15 | e.summary = "#{event_session.event.title}: #{event_session.name}"
16 | e.location = event_session.true_location.try(:name)
17 | end
18 | cal.to_ical
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/services/meetup_importer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class MeetupImporter
4 | def associate_user(bridgetroll_user, meetup_id)
5 | authentication_meetup_id = bridgetroll_user.reload.meetup_id
6 | if authentication_meetup_id.to_s != meetup_id.to_s
7 | raise "User has no registered authentication with meetup UID #{meetup_id}"
8 | end
9 |
10 | meetup_user = MeetupUser.where(meetup_id: meetup_id).first
11 | return if meetup_user.blank?
12 |
13 | Rsvp.where(user_type: 'MeetupUser', user_id: meetup_user.id).find_each do |rsvp|
14 | rsvp.user = bridgetroll_user
15 | rsvp.save!
16 | end
17 | end
18 |
19 | def disassociate_user(bridgetroll_user)
20 | raise 'User is not associated with a meetup account!' if bridgetroll_user.meetup_id.blank?
21 |
22 | meetup_user = MeetupUser.where(meetup_id: bridgetroll_user.meetup_id).first
23 | Rsvp.where(user_type: 'User', user_id: bridgetroll_user.id).find_each do |rsvp|
24 | rsvp.user = meetup_user
25 | rsvp.save!
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/services/rsvp_sorter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RsvpSorter
4 | def initialize(event, rsvps)
5 | @event = event
6 | @rsvps = rsvps
7 | end
8 |
9 | def ordered
10 | if @event.historical?
11 | modern_rsvps + historical_rsvps
12 | else
13 | modern_rsvps
14 | end
15 | end
16 |
17 | private
18 |
19 | def modern_rsvps_order_clause
20 | name_clause = 'lower(users.first_name) ASC, lower(users.last_name) ASC'
21 | Arel.sql(
22 | if @event.past?
23 | "checkins_count > 0 DESC, #{name_clause}"
24 | else
25 | name_clause
26 | end
27 | )
28 | end
29 |
30 | def modern_rsvps
31 | @rsvps
32 | .where(user_type: 'User')
33 | .includes(:bridgetroll_user)
34 | .order(modern_rsvps_order_clause)
35 | .references(:bridgetroll_users)
36 | end
37 |
38 | def historical_rsvps
39 | @rsvps
40 | .where(user_type: 'MeetupUser')
41 | .includes(:meetup_user)
42 | .order(Arel.sql('lower(meetup_users.full_name) ASC'))
43 | .references(:meetup_users)
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/app/services/user_searcher.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class UserSearcher
4 | def initialize(relation, query)
5 | @relation = relation
6 | @query = query
7 | end
8 |
9 | def as_json(_options = {})
10 | args = 'lower(first_name)', "' '", 'lower(last_name)'
11 | search_field = if Rails.application.using_postgres?
12 | "CONCAT(#{args * ', '})"
13 | else
14 | args * ' || '
15 | end
16 |
17 | @relation
18 | .select(:id, :first_name, :last_name)
19 | .where("#{search_field} like ?", "%#{@query.downcase}%")
20 | .map { |u| { id: u.id, text: u.full_name } }
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/validators/array_of_ids_validator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ArrayOfIdsValidator < ActiveModel::EachValidator
4 | def validate_each(record, attribute, value)
5 | return if value.nil?
6 |
7 | unless value.respond_to?(:each)
8 | record.errors.add(attribute, 'must be an array')
9 | return
10 | end
11 |
12 | if value.empty?
13 | record.errors.add(attribute, 'must have at least one value')
14 | return
15 | end
16 |
17 | value.each do |id|
18 | record.errors.add(attribute, 'must be in the list') unless options[:in].include?(id)
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/views/chapters/_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= simple_form_for(@chapter) do |f| %>
4 | <%= render 'shared/model_error_messages', model: @chapter %>
5 |
6 |
7 | <%= f.label :organization_id %>
8 | <%= f.collection_select(:organization_id, policy_scope(Organization).order(:name), :id, :name, {prompt: true}, {class: 'form-control'}) %>
9 |
10 |
11 |
12 | <%= f.input :name, placeholder: "ex: BridgeName CityName", hint: "We suggest naming your chapter after the organization and city or region." %>
13 |
14 |
15 | <%= f.submit class: 'btn btn-submit', data: {disable_with: 'Please wait...'} %>
16 |
17 | <% end %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/views/chapters/_table.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Name
5 | Events
6 | <% if current_user.try(:admin?) %>
7 |
8 | <% end %>
9 |
10 |
11 |
12 |
13 | <% chapters.each do |chapter| %>
14 |
15 | <%= link_to chapter.name, chapter %>
16 | <%= chapter.events_count + chapter.external_events_count %>
17 |
18 | <% if policy(chapter).update? %>
19 | <%= link_to 'Edit', edit_chapter_path(chapter), class: 'btn fa-before fa-edit' %>
20 | <% end %>
21 | <% if current_user.try(:admin?) && chapter.destroyable? %>
22 | <%= link_to 'Destroy', chapter, data: {confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-danger' %>
23 | <% end %>
24 |
25 |
26 | <% end %>
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/views/chapters/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Editing chapter") %>
2 |
3 | <%= render 'form' %>
4 |
5 | <%= render 'shared/actions', links: [
6 | ['Show', @chapter]
7 | ] %>
8 |
9 |
--------------------------------------------------------------------------------
/app/views/chapters/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, 'Listing chapters') %>
2 |
3 | <%= render 'table', chapters: @chapters %>
4 |
5 | <%= render 'shared/actions', links: crud_object_nav_links(:chapter, policy(Chapter).new? ? ['New Chapter', new_chapter_path] : nil) %>
6 |
--------------------------------------------------------------------------------
/app/views/chapters/leaders/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Chapter Leaders for #{@chapter.name}") %>
2 |
3 | Potential Leaders
4 | <%= render 'shared/user_chooser',
5 | action: chapter_leaders_path(@chapter),
6 | select_id: 'chapter_leader',
7 | foreign_key: 'id',
8 | url: potential_chapter_leaders_path(@chapter),
9 | choices: []
10 | %>
11 |
12 |
13 |
14 |
15 |
16 | Name
17 | Email
18 |
19 |
20 |
21 |
22 | <% @chapter.leaders.each do |leader| %>
23 |
24 | <%= user_gravatar(leader) %>
25 | <%= leader.full_name %>
26 | <%= leader.email %>
27 |
28 | <% if leader.id != current_user.id %>
29 | <%= button_to 'Remove', chapter_leader_path(@chapter, leader), method: :delete,
30 | form: {style: "margin-bottom : 0;"}, class: "btn btn-mini" %>
31 | <% end %>
32 |
33 |
34 | <% end %>
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/views/chapters/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, 'New chapter') %>
2 |
3 | <%= render 'form' %>
4 |
--------------------------------------------------------------------------------
/app/views/chapters/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @chapter.name) %>
2 |
3 | <% if @chapter.leaders.present? %>
4 | Chapter Leaders
5 |
6 | <% @chapter.leaders.each do |leader| %>
7 | <%= link_to leader.full_name, user_profile_path(leader) %>
8 | <% end %>
9 |
10 | <% end %>
11 |
12 | <% if @chapter.leader?(current_user) %>
13 | <%= link_to 'Edit Chapter Leaders', chapter_leaders_path(@chapter), class: "btn mb20" %>
14 |
15 | Organizers
16 |
17 |
18 |
19 | Name
20 | # Events
21 |
22 |
23 |
24 | <% @organizer_rsvps.each do |rsvp| %>
25 |
26 |
27 | <%= link_to rsvp.user.full_name, user_profile_path(rsvp.user) %>
28 |
29 | <%= rsvp.events_count %>
30 |
31 | <% end %>
32 |
33 |
34 | <% end %>
35 |
36 | Events
37 | <%= render 'shared/events_table', events: @chapter_events %>
38 |
39 | <%= render 'shared/actions', links: [
40 | current_user.try(:admin?) ? ['Edit', edit_chapter_path(@chapter)] : nil,
41 | ] %>
42 |
--------------------------------------------------------------------------------
/app/views/courses/edit.html.erb:
--------------------------------------------------------------------------------
1 | Editing Course
2 |
3 | <%= render 'form' %>
4 |
--------------------------------------------------------------------------------
/app/views/courses/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, 'Listing courses') %>
2 |
3 |
4 |
5 |
6 | Name
7 | Title
8 | Description
9 |
10 |
11 |
12 |
13 |
14 | <% @courses.each do |course| %>
15 |
16 | <%= link_to course.name, course %>
17 | <%= course.title %>
18 | <%= simple_format_with_html(course.description) %>
19 |
20 | <%= link_to 'Edit', edit_course_path(course),
21 | class: 'btn fa-before fa-edit' %>
22 |
23 |
24 | <%= link_to 'Remove', course, method: :delete,
25 | class: 'btn btn-danger',
26 | data: {
27 | confirm: "Are you sure you want to remove " + course.title + " and its' " + pluralize(@course_level_counts[course.id], 'level') + "?"
28 | } %>
29 |
30 |
31 | <% end %>
32 |
33 |
34 |
35 | <%= link_to 'New Course', new_course_path, class: 'btn' %>
36 |
--------------------------------------------------------------------------------
/app/views/courses/new.html.erb:
--------------------------------------------------------------------------------
1 | New Course
2 |
3 | <%= render 'form' %>
4 |
--------------------------------------------------------------------------------
/app/views/devise/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend confirmation instructions
2 |
3 |
4 |
5 | <%= simple_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
6 | <%= render "shared/devise_error_messages" if resource.errors.any? %>
7 |
8 | <%= f.input :email %>
9 |
10 |
<%= f.submit "Resend confirmation instructions", class: 'btn btn-submit', data: {disable_with: 'Please wait...'} %>
11 | <% end %>
12 |
13 |
14 |
15 | <%= devise_links(:sign_up, :forgot_password) %>
16 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/confirmation_instructions.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
Welcome to Bridge Troll, <%= @resource.first_name %>!
3 |
4 |
Click the link below to begin a magical journey to an amazing, welcoming technology workshop.
5 |
6 |
7 | <%= link_to confirmation_url(@resource, confirmation_token: @token) do %>
8 |
9 | <% end %>
10 |
11 |
12 |
13 | Should the image above not load for any reason, click here:
14 | <%= link_to "Sign up to Bridge Troll", confirmation_url(@resource, confirmation_token: @token) %>
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/reset_password_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Someone has requested a link to change your password, and you can do this through the link below.
4 |
5 | <%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
6 |
7 | If you didn't request this, please ignore this email.
8 | Your password won't change until you access the link above and create a new one.
9 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 | Change your password
2 |
3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
4 | <%= render "shared/devise_error_messages" if resource.errors.any? %>
5 |
6 | <%= f.hidden_field :reset_password_token %>
7 |
8 |
9 | <%= f.label :password, "New password" %>
10 | <%= f.password_field :password %>
11 |
12 |
13 |
14 | <%= f.label :password_confirmation, "Confirm new password" %>
15 | <%= f.password_field :password_confirmation %>
16 |
17 |
18 | <%= f.submit "Change my password", class: 'btn btn-submit', data: {disable_with: 'Please wait...'} %>
19 | <% end %>
20 |
21 | <%= devise_links(:sign_up, :confirmation_instructions) %>
22 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/new.html.erb:
--------------------------------------------------------------------------------
1 | Forgot your password?
2 |
3 |
4 |
5 | <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
6 | <%= render "shared/devise_error_messages" if resource.errors.any? %>
7 |
8 | <%= f.input :email %>
9 |
10 |
<%= f.submit "Send me reset password instructions", class: 'btn btn-submit', data: {disable_with: 'Please wait...'} %>
11 | <% end %>
12 |
13 |
14 |
15 | <%= devise_links(:sign_up, :confirmation_instructions) %>
16 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Edit #{resource_name.to_s.humanize}") %>
2 |
3 |
4 | <%= render 'shared/profile_gravatars'%>
5 |
6 | <%= link_to 'Change Gravatar', 'https://en.gravatar.com/emails', target: "_blank", class: 'btn fa-before fa-upload' %>
7 |
8 | <%= link_to 'View Public Profile', user_profile_path(resource), class: 'btn fa-before fa-user' %>
9 |
10 |
11 | <%= render 'form', button_text: 'Update', is_registered: true %>
12 |
13 | <%= link_to "Delete my account", registration_path(resource_name), data: { confirm: "This will delete your Bridgetroll account forever. Are you sure?" }, method: :delete, class: 'btn btn-danger' %>
14 |
--------------------------------------------------------------------------------
/app/views/devise/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Sign in") %>
2 |
3 |
4 | <%= render partial: "shared/sign_in" %>
5 | <%= devise_links(:forgot_password, :confirmation_instructions) %>
6 |
--------------------------------------------------------------------------------
/app/views/event_sessions/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 | <%= content_for(:header_link, event_path(@event)) %>
3 |
4 | <%= render partial: 'shared/checkin_event_sessions' %>
5 |
--------------------------------------------------------------------------------
/app/views/events/_attendee_list.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% attendees.each do |rsvp| %>
3 |
4 | <%= user_gravatar(rsvp.loaded_user) %><%= link_to rsvp.loaded_user.full_name, rsvp.loaded_user.profile_path, class: rsvp_class(rsvp) %>
5 | <% if rsvp.waitlist_position.present? %>
6 | #<%= rsvp.waitlist_position %>
7 | <% end %>
8 |
9 | <% end %>
10 |
11 |
--------------------------------------------------------------------------------
/app/views/events/_location_modal.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 | <%# Render the new location form (passing modal => true to enable remote => true) %>
10 | <%= render 'locations/form', modal: true %>
11 |
12 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/views/events/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 | <%= content_for(:header_link, event_path(@event)) %>
3 |
4 |
5 |
6 |
Editing event
7 | <%= render 'form' %>
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/views/events/emails/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 | <%= content_for(:header_link, event_organizer_tools_path(@event)) %>
3 |
4 | <%= render partial: 'shared/organizer_breadcrumb', locals: {current_page_title: 'Review Sent Email'} %>
5 |
6 | From: <%= link_to @email.sender.full_name, user_profile_path(@email.sender) %>
7 |
8 |
9 |
10 | Subject: <%= @email.subject %>
11 |
12 |
13 |
14 | Body:
15 |
16 | <%= @email.body %>
17 |
--------------------------------------------------------------------------------
/app/views/events/feed.atom.builder:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | atom_feed do |feed|
4 | feed.title('Bridge Troll Events')
5 | feed.link root_url
6 | feed.updated @events.last.try(:updated_at)
7 | feed.id root_url
8 |
9 | @events.each do |event|
10 | feed.entry(event) do |entry|
11 | entry.title(event.title)
12 | entry.summary(event.details, type: 'html')
13 | entry.link event_path(event)
14 | entry.id event_path(event)
15 | entry.updated event.updated_at
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/views/events/feed.rss.builder:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | xml.instruct! :xml, version: '1.0'
4 | xml.rss version: '2.0' do
5 | xml.channel do
6 | xml.title 'Bridge Troll Events'
7 | xml.link root_url
8 | xml.language 'en'
9 |
10 | @events.each do |event|
11 | xml.item do
12 | xml.title event.title
13 | xml.pubDate event.created_at.to_fs(:rfc822)
14 | xml.link event_path(event)
15 | xml.guid event_path(event)
16 | xml.description event.details
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/views/events/levels.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 | <%= content_for(:header_link, event_path(@event)) %>
3 |
4 | Class Levels for <%= (@event.course || Course.find_by_name("RAILS")).title %>
5 |
6 | <%= render partial: 'rsvps/class_levels', locals: {
7 | levels: (@event.course || Course.find_by_name("RAILS")).levels,
8 | show_no_preference: false
9 | } %>
10 |
--------------------------------------------------------------------------------
/app/views/events/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, 'Organize an Event') %>
2 |
3 |
4 |
5 | <%= render 'guidelines' %>
6 | <%= render 'form' %>
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/views/events/organizer_tools/_checkin_counts.html.erb:
--------------------------------------------------------------------------------
1 | Current Checkin Counts
2 | <% @event.event_sessions.each do |session| %>
3 |
4 | <%= session.name %>:
5 |
6 | <% [Role::VOLUNTEER, Role::STUDENT].each do |role| %>
7 | <% checked_in = @checkin_counts[role.id][:checkin][session.id] %>
8 | <% total = @checkin_counts[role.id][:rsvp][session.id] %>
9 |
10 | <%= role.title.pluralize %>:
11 |
12 | <%= checked_in %>/<%= total %>
13 | <% if total > 0 %>
14 | (<%= (100 * checked_in / total.to_f).round(0) %>%)
15 | <% end %>
16 |
17 | checked in
18 |
19 | <% end %>
20 |
21 |
22 | <% end %>
23 |
--------------------------------------------------------------------------------
/app/views/events/organizer_tools/_childcare_requests.html.erb:
--------------------------------------------------------------------------------
1 | Childcare Requests
2 | <% if !@event.has_childcare %>
3 | Childcare sign ups are currently disabled.
4 | <% end %>
5 | <% if @childcare_requests.length > 0 %>
6 |
7 |
8 |
9 | Requester Name
10 | Email
11 | Children Info
12 |
13 |
14 |
15 | <% @childcare_requests.each do |request| %>
16 |
17 |
18 | <%= request.user.full_name %>
19 |
20 |
21 | <%= request.user.email %>
22 |
23 | <%= request.childcare_info %>
24 |
25 | <% end %>
26 |
27 |
28 | <% else %>
29 | No one has requested childcare for this event yet.
30 | <% end %>
31 |
--------------------------------------------------------------------------------
/app/views/events/organizer_tools/_operating_system_breakdown.erb:
--------------------------------------------------------------------------------
1 | Operating System Breakdown
2 | <% total = @os_counts.values.reduce(&:+) %>
3 | <% if total.present? %>
4 |
5 |
6 |
7 | OS
8 | Count
9 |
10 |
11 |
12 | <% OperatingSystem.all.each do |os| %>
13 | <% count = @os_counts.fetch(os.id, 0) %>
14 | <% next unless count > 0 %>
15 |
16 |
17 | <%= os.title %>
18 |
19 |
20 | <%= count %> (<%= '%.2f' % ((count.to_f / total) * 100) %>%)
21 |
22 |
23 | <% end %>
24 |
25 |
26 | <% else %>
27 | No student RSVPs yet...
28 | <% end %>
--------------------------------------------------------------------------------
/app/views/events/past_events.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
Past events
3 |
Hundreds of workshops in dozens of cities around the world have been
4 | organized and taught by volunteers, based on our open source curricula.
5 | <%= render 'shared/past_events_table' %>
6 |
7 |
--------------------------------------------------------------------------------
/app/views/events/students/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 | <%= content_for(:header_link, event_organizer_tools_path(@event)) %>
3 |
4 | Students
5 |
6 |
7 |
8 |
9 | Name
10 | Class level
11 | Operating System
12 | Waitlisted
13 | Job Details
14 | <% @students.each do |rsvp| %>
15 |
16 |
17 | <%= user_gravatar(rsvp.user) %>
18 | <%= rsvp.user.full_name %>
19 |
20 | <%= rsvp.class_level %>
21 | <%= rsvp.operating_system.title %>
22 | <%= if rsvp.waitlist_position then "yes" else "no" end %>
23 | <%= rsvp.job_details %>
24 |
25 | <% end %>
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/views/events/surveys/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 |
3 | <%= simple_form_for @event do |f| %>
4 | <%= render 'shared/model_error_messages', model: @event %>
5 |
6 | <%= render partial: 'shared/organizer_breadcrumb', locals: {current_page_title: 'Edit Email Body'} %>
7 |
8 |
9 |
10 | Use the field below to edit the email body that will be sent to RSVP'd participants. A link to the survey will be included at the bottom of your message.
11 |
12 | <%= f.input :survey_greeting, label: "Email Body:", input_html: {rows: 4} %>
13 |
14 |
15 |
16 | <%= f.submit "Update", class: "btn" %>
17 |
18 | <% end %>
19 |
--------------------------------------------------------------------------------
/app/views/events/unpublished_events/_mark_as_spam_button.html.erb:
--------------------------------------------------------------------------------
1 | <%= button_to 'Flag as Spam', unpublished_event_flag_path(event), class: 'btn btn-danger', method: :post,
2 | form_class: 'button_to inline-block',
3 | data: {
4 | confirm: "Are you sure? This will remove this event from the approval page, and flag #{event.organizers.first.full_name} as a spammer so that all subsequent events they create will immediately be flagged as spam."
5 | } %>
--------------------------------------------------------------------------------
/app/views/events/unpublished_events/_publish_event_button.html.erb:
--------------------------------------------------------------------------------
1 | <% if event.location %>
2 | <%= button_to 'Publish', unpublished_event_publish_path(event), class: 'btn', id: "publish-event-#{event.id}", method: :post, form_class: 'button_to inline-block' %>
3 |
22 | <% else %>
23 | No Location - Can't Publish!
24 | <% end %>
--------------------------------------------------------------------------------
/app/views/events/unpublished_events/_unpublished_event.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to event.title, event %>
4 |
5 |
6 | <% event.event_sessions.each_with_index do |session, index| %>
7 | <%= formatted_session_fancy_date(session) %><% if index < event.event_sessions.length - 1 %> <% end %>
8 | <% end %>
9 |
10 |
11 | <% if event.location %>
12 | <%= event.location.name %>,
13 | <%= [event.location.city, event.location.state].select(&:present?).join(', ') %>
14 | <% end %>
15 |
16 |
17 | <%= render 'publish_event_button', event: event %>
18 | <% if current_user.admin? || current_user.publisher? %>
19 | <%= render 'mark_as_spam_button', event: event %>
20 | <% end %>
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/views/external_events/_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= simple_form_for(@external_event) do |f| %>
4 | <%= render 'shared/model_error_messages', model: @external_event %>
5 |
6 | <%= content_tag :div, class: field_classes(@external_event, :region) do %>
7 | <%= render 'shared/region_select', f: f %>
8 | <% end %>
9 |
10 | <%= content_tag :div, class: field_classes(@external_event, :chapter) do %>
11 |
12 | <%= render 'shared/chapter_select', f: f %>
13 |
14 | <% end %>
15 |
16 | <%= f.input :name %>
17 |
18 | <%= f.input :url, label: 'URL' %>
19 |
20 |
21 | <%= f.input :starts_at, start_year: 2009 %>
22 |
23 |
24 |
25 | <%= f.input :ends_at, start_year: 2009 %>
26 |
27 |
28 | <%= f.input :city %>
29 |
30 | <%= f.input :location %>
31 |
32 | <%= f.input :organizers %>
33 |
34 |
35 | <%= f.submit class: :btn, data: {disable_with: 'Please wait...'} %>
36 |
37 | <% end %>
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/views/external_events/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Editing External Event") %>
2 |
3 | <%= render 'form' %>
4 |
--------------------------------------------------------------------------------
/app/views/external_events/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "New External Event") %>
2 |
3 | <%= render 'form' %>
4 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= yield %>
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/views/locations/create.js.erb:
--------------------------------------------------------------------------------
1 | $('#new-location-modal').modal('hide');
2 | $('#new-location-modal form')[0].reset();
3 |
4 | id = '<%= @location.id %>'
5 |
6 | $location_select = $('#event_location_id')
7 | $location_select.append("<%= @location.name_with_region %> ")
8 | $location_select.val(id).trigger('change')
9 |
--------------------------------------------------------------------------------
/app/views/locations/create_failed.js.erb:
--------------------------------------------------------------------------------
1 | $('#new-location-modal-body').html('<%= j render "form", modal: true %>')
2 |
--------------------------------------------------------------------------------
/app/views/locations/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Editing location") %>
2 |
3 | <% if @location.archived? %>
4 |
5 | This location is archived, and can no longer be used for events.
6 |
7 | <% end %>
8 |
9 | <%= render 'form' %>
10 |
11 | <%= render 'shared/actions', links: [
12 | ['Show', @location]
13 | ] %>
14 |
15 |
--------------------------------------------------------------------------------
/app/views/locations/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, 'Listing locations') %>
2 |
3 | <%= render 'shared/locations_table', locations: @locations %>
4 |
5 | <%= render 'shared/actions', links: crud_object_nav_links(:location, user_signed_in? ? ['New Location', new_location_path] : nil)
6 | %>
7 |
--------------------------------------------------------------------------------
/app/views/locations/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, 'New location') %>
2 |
3 | <%= render 'form' %>
4 |
--------------------------------------------------------------------------------
/app/views/mailers/event_mailer/_new_event_detail.html.erb:
--------------------------------------------------------------------------------
1 | <% if @event.location && !@event.multiple_locations? %>
2 | <%= @event.title %> will be held at:
3 | <%= pretty_print_address(@event.location) %>
4 |
5 | <% end %>
6 |
7 | There will be <%= pluralize(@event.event_sessions.count, 'session') %>:
8 |
9 | <% @event.event_sessions.each do |s| %>
10 | <%= pretty_print_session(s) %>
11 | <% if @event.multiple_locations? %>
12 | <%= render 'session_location_detail', location: s.true_location %>
13 | <% end %>
14 | <% end %>
15 |
16 |
17 | Visit the <%= link_to 'event page', event_url(@event) %> for more information about the event.
18 |
--------------------------------------------------------------------------------
/app/views/mailers/event_mailer/_session_location_detail.html.erb:
--------------------------------------------------------------------------------
1 |
2 | Location:
3 | <%= pretty_print_address(location) %>
4 |
5 |
--------------------------------------------------------------------------------
/app/views/mailers/event_mailer/event_has_been_approved.html.erb:
--------------------------------------------------------------------------------
1 | Dear <%= @event.organizers.first.first_name %>,
2 |
3 |
4 | Congratulations! Your event <%= @event.title %> has been approved. Our organization is entirely grass-roots and volunteer-run, so your contribution is really important. Thank you so much for organizing!
5 |
6 |
7 | Next Steps:
8 | What's next? You've just posted your event to Bridgetroll, so you're probably about three weeks out. You can Check out the workshop timeline to see what other organizers generally do around this time. You can find more details about using Bridgetroll here .
9 |
10 |
11 | You're also always welcome to email the organizers' mailing list with questions, requests, panic, or jokes. We're all here to help.
12 |
13 |
14 |
15 | Sincerely,
16 | The Troll
17 |
--------------------------------------------------------------------------------
/app/views/mailers/event_mailer/event_pending_approval.html.erb:
--------------------------------------------------------------------------------
1 | Dear <%= @event.organizers.first.first_name %>,
2 |
3 |
4 | We're so excited that you're organizing <%= @event.title %>! For spam-prevention and quality assurance, your event needs to be approved by a Bridge Troll admin before it is announced to your region and visible on the site.
5 |
6 |
7 | Admins are looking particularly to make sure that your event specifically states what underrepresented populations it is reaching out to, and that the workshop is free. (And they might tell you about any typos they find, or things that don't make sense, if they find either.)
8 |
9 |
10 | If you have any questions, or if it's been a day or two and you haven't heard anyone, please email bridgetroll@railsbridge.org.
11 |
12 | Sincerely,
13 | The Troll
14 |
--------------------------------------------------------------------------------
/app/views/mailers/event_mailer/from_organizer.html.erb:
--------------------------------------------------------------------------------
1 | <%= @sender.full_name %> has something to say about <%= @event.upcoming? ? 'an upcoming event' : 'a past event' %>:
2 |
3 | <%= sanitize(@body).gsub(/(\r)?\n/, " ").html_safe %>
4 |
5 | <%= link_to 'Check out this event on Bridge Troll by clicking this link!', event_url(@event) %>
6 |
--------------------------------------------------------------------------------
/app/views/mailers/event_mailer/new_event.html.erb:
--------------------------------------------------------------------------------
1 | Howdy!
2 |
3 | A new event, "<%= @event.title %>", has been posted to Bridge Troll!
4 |
5 | <%= render 'new_event_detail' %>
6 |
7 | You received this email because Bridge Troll thinks you're interested in events for the "<%= @region.name %> " region. If you want to change that preference, opt-out of emails entirely, or delete your account in a fit of rage, <%= link_to 'visit your preference page on Bridge Troll', edit_user_registration_url %>.
8 |
--------------------------------------------------------------------------------
/app/views/mailers/event_mailer/new_organizer_alert.erb:
--------------------------------------------------------------------------------
1 | Howdy <%= @user.full_name %>!
2 |
3 | You've been added as an organizer to "<%= @event.title %>".
4 |
5 | If you think you shouldn't be an organizer, <%= link_to 'you can remove yourself on the Organizers page', event_organizers_url(@event) %>
6 |
7 | The Troll
8 |
--------------------------------------------------------------------------------
/app/views/mailers/event_mailer/unpublished_event.html.erb:
--------------------------------------------------------------------------------
1 | Howdy, Bridge Troll administrator!
2 |
3 | A new event, "<%= link_to @event.title, event_url(@event) %>", has been posted to Bridge Troll by <%= link_to @event.organizers.first.full_name, user_profile_url(@event.organizers.first) %> !
4 |
5 | <%= render 'new_event_detail' %>
6 |
7 | <% if @event.published? %>
8 | <%= link_to 'This event is already published, so you should be able to find it on the list of upcoming events!', events_url %>
9 | <% else %>
10 | <%= link_to 'Go see the list of unpublished events to see if this is worth approving!', unpublished_events_url %>
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/app/views/mailers/rsvp_mailer/childcare_notification.html.erb:
--------------------------------------------------------------------------------
1 | Hey Organizers,
2 |
3 | There is a new childcare request for <%= @rsvp.event.title %>!
4 |
5 | The request was made by <%= @rsvp.user.full_name %>. The information about the request can be seen directly below:
6 | <%= @rsvp.childcare_info %>
7 |
8 | Visit the <%= link_to 'event page', event_url(@rsvp.event) %> for more information about the event.
9 |
10 | - The Troll
--------------------------------------------------------------------------------
/app/views/mailers/rsvp_mailer/email.html.erb:
--------------------------------------------------------------------------------
1 | Hey <%= @rsvp.user.full_name %>,
2 |
3 | Thanks for signing up as a <%= @rsvp.role.name.downcase %> for <%= @rsvp.event.title %>!
4 |
5 | <% if @rsvp.waitlisted? %>
6 | You're at position <%= @rsvp.waitlist_position %> on the waitlist. You'll get an email if a slot opens up for you.
7 | <% end %>
8 |
9 | <%= render partial: 'event_details' %>
10 |
11 | - The Troll
12 |
--------------------------------------------------------------------------------
/app/views/mailers/rsvp_mailer/off_waitlist.html.erb:
--------------------------------------------------------------------------------
1 | Hey <%= @rsvp.user.full_name %>,
2 |
3 | A spot opened up! You are now confirmed as a <%= @rsvp.role.name.downcase %> for <%= @rsvp.event.title %>!
4 |
5 | <%= render partial: 'event_details' %>
6 |
7 | - The Troll
8 |
--------------------------------------------------------------------------------
/app/views/mailers/survey_mailer/notification.html.erb:
--------------------------------------------------------------------------------
1 | <%= simple_format_with_html(@rsvp.event.survey_greeting) %>
2 |
3 | <%= link_to "Click right here to take the survey!", new_event_rsvp_survey_url(@rsvp.event, @rsvp)%>
4 |
5 | - The Troll
6 |
--------------------------------------------------------------------------------
/app/views/meetup_users/show.html.erb:
--------------------------------------------------------------------------------
1 | Events attended by <%= @user.full_name %>
2 |
3 | <%= render 'shared/rsvps', rsvps: @rsvps %>
4 |
5 | <%= render 'shared/actions', links: [
6 | ['User Index', users_path]
7 | ] %>
8 |
--------------------------------------------------------------------------------
/app/views/organizations/_chapter_infowindow.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to location.chapter.name, location.chapter %> (<%= pluralize(location.chapter.events_count, 'event') %>)
3 |
4 |
5 | Last Event:
6 | <%= link_to location.event.title, location.event %> @
7 | <%= link_to location.name, location %>
8 |
--------------------------------------------------------------------------------
/app/views/organizations/index.html.erb:
--------------------------------------------------------------------------------
1 | Organizations
2 |
3 | <% @organizations.sort_by(&:name).each do |org| %>
4 | <% if org.chapters.count > 0 %>
5 | <%= link_to org.name, organization_path(org) %>
6 | <% org.chapters.count %><%= pluralize(org.chapters.count, 'chapter') %>
7 | <% if org.leaders.length > 0 %>
8 | Leaders:
9 |
10 | <% org.leaders.each do |leader| %>
11 | <%= link_to leader.full_name, user_profile_path(leader) %>
12 | <% end %>
13 |
14 | <% end %>
15 | <% end %>
16 | <% end %>
17 |
18 | Chapters
19 | <%= render "shared/map",
20 | locations: @chapter_locations,
21 | map_class: 'gmaps4rails_bigger_map',
22 | infowindow_template: 'chapter_infowindow' %>
23 |
24 | <% if user_signed_in? %>
25 | <% links = [] %>
26 | <% if policy(Organization).create? %>
27 | <% links << ['Add Organization', new_organization_path] %>
28 | <% end %>
29 | <%= render 'shared/actions', links: links, additional_class: 'no-margin' %>
30 | <% end %>
31 |
--------------------------------------------------------------------------------
/app/views/organizations/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, 'New organization') %>
2 |
3 |
4 |
5 | <%= simple_form_for(@organization) do |f| %>
6 | <%= render 'shared/model_error_messages', model: @organization %>
7 |
8 | <%= f.input :name, placeholder: "ex: SandwichBridge" %>
9 |
10 |
11 | <%= f.submit class: 'btn btn-submit', data: {disable_with: 'Please wait...'} %>
12 |
13 | <% end %>
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/views/organizations/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= @organization.name %>
2 |
3 | <% if @organization.leaders.present? %>
4 | Organization Leaders
5 | These are people who can approve Bridge Troll postings for any <%= @organization.name %> event.
6 |
7 | <% @organization.leaders.each do |leader| %>
8 | <%= link_to leader.full_name, user_profile_path(leader) %>
9 | <% end %>
10 |
11 | <% end %>
12 |
13 | <% if policy(@organization).manage_organization? %>
14 |
21 | <% end %>
22 |
23 | Chapters
24 |
25 | <%= render 'chapters/table', chapters: @organization.chapters %>
26 |
--------------------------------------------------------------------------------
/app/views/regions/_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= simple_form_for(@region) do |f| %>
4 | <%= render 'shared/model_error_messages', model: @region %>
5 |
6 | <%= f.input :name %>
7 |
8 |
9 | <%= f.submit class: 'btn btn-submit', data: {disable_with: 'Please wait...'} %>
10 |
11 | <% end %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/regions/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Editing region") %>
2 |
3 | <%= render 'form' %>
4 |
5 | <%= render 'shared/actions', links: [
6 | ['Show', @region]
7 | ] %>
8 |
9 |
--------------------------------------------------------------------------------
/app/views/regions/leaders/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Region Leaders for #{@region.name}") %>
2 |
3 | Potential Leaders
4 | <%= render 'shared/user_chooser',
5 | action: region_leaders_path(@region),
6 | select_id: 'region_leader',
7 | foreign_key: 'id',
8 | url: potential_region_leaders_path(@region),
9 | choices: []
10 | %>
11 |
12 |
13 |
14 |
15 |
16 | Name
17 | Email
18 |
19 |
20 |
21 |
22 | <% @region.leaders.each do |leader| %>
23 |
24 | <%= user_gravatar(leader) %>
25 | <%= leader.full_name %>
26 | <%= leader.email %>
27 |
28 | <% if leader.id != current_user.id %>
29 | <%= button_to 'Remove', region_leaders_path(@region, leader), method: :delete,
30 | form: {style: "margin-bottom : 0;"}, class: "btn btn-mini" %>
31 | <% end %>
32 |
33 |
34 | <% end %>
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/views/regions/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, 'New region') %>
2 |
3 | <%= render 'form' %>
4 |
--------------------------------------------------------------------------------
/app/views/rsvps/_class_levels.html.erb:
--------------------------------------------------------------------------------
1 |
30 |
--------------------------------------------------------------------------------
/app/views/rsvps/_dietary_restrictions.html.erb:
--------------------------------------------------------------------------------
1 | <%= f.collection_check_boxes(:dietary_restriction_diets,
2 | DietaryRestriction::DIETS,
3 | :to_s,
4 | :capitalize,
5 | {
6 | item_wrapper_class: 'checkbox_row',
7 | item_wrapper_tag: 'div',
8 | include_hidden: false
9 | }
10 | ) %>
11 | <%= f.input :dietary_info, label: "Other" %>
--------------------------------------------------------------------------------
/app/views/rsvps/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 |
3 |
4 |
Edit RSVP
5 |
6 | <%= render 'form' %>
7 |
8 |
--------------------------------------------------------------------------------
/app/views/rsvps/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 |
3 |
4 |
RSVP
5 |
6 | <% if @show_new_region_warning %>
7 |
8 |
9 | Reminder: this event is in
10 |
11 | <%= @event.location ? @event.location_city_and_state : @event.region.name %> .
12 | Since you've never attended an event in that location, please double check you can make it.
13 |
14 |
15 | <% end %>
16 |
17 |
AWESOME - you're almost signed up!
18 |
19 | <%= render 'rsvps/form' %>
20 |
21 |
--------------------------------------------------------------------------------
/app/views/rsvps/quick_destroy_confirm.html.erb:
--------------------------------------------------------------------------------
1 |
2 | Are you sure you want to cancel?
3 |
4 |
5 |
6 |
7 | <%= link_to 'Yes', event_rsvp_path(@event, @rsvp, token: @rsvp.token), class: 'btn btn-submit', method: :delete %>
8 |
9 |
10 | <%= link_to 'No', root_path, class: 'btn' %>
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/views/shared/_actions.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% links.each do |link_data| %>
3 | <% next unless link_data %>
4 | <%= link_to link_data[0], link_data[1], class: "btn #{link_data[2] || ''}" %>
5 | <% end %>
6 |
7 |
--------------------------------------------------------------------------------
/app/views/shared/_chapter_select.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= f.label :chapter_id, local_assigns[:label] || 'Chapter', class: 'control-label', label: 'org' %>
3 |
4 | <%= f.collection_select(:chapter_id,
5 | Chapter.all,
6 | :id,
7 | :name,
8 | {prompt: true}) %>
9 |
19 |
--------------------------------------------------------------------------------
/app/views/shared/_checkin_event_sessions.html.erb:
--------------------------------------------------------------------------------
1 |
2 | Check in students and volunteers
3 | <% @event.event_sessions.each do |session| %>
4 | <%= render partial: 'shared/organizer_action', locals: {
5 | path: event_event_session_checkins_path(@event, session),
6 | icon: 'fa fa-check-square-o',
7 | text: "Check in for #{session.name}",
8 | tip: "Check those students and volunteers in! This will make for easy class sorting."
9 | } %>
10 | <% end %>
11 |
12 |
--------------------------------------------------------------------------------
/app/views/shared/_devise_error_messages.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= I18n.t("errors.messages.not_saved",
4 | count: resource.errors.count,
5 | resource: resource.class.model_name.human.downcase)
6 | %>
7 |
8 |
9 | <% resource.errors.full_messages.each do |message| %>
10 | <%= message %>
11 | <% end %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/shared/_google_analytics.html.erb:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/views/shared/_map.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%
3 | markers = Gmaps4rails.build_markers(locations) do |location, marker|
4 | marker.lat location.latitude
5 | marker.lng location.longitude
6 | if defined?(infowindow_template)
7 | marker.infowindow render(infowindow_template, location: location)
8 | end
9 | end
10 | %>
11 |
12 |
13 | <%= javascript_include_tag 'markerclustererplus/dist/markerclusterer.min.js' %>
14 |
15 |
--------------------------------------------------------------------------------
/app/views/shared/_model_error_messages.html.erb:
--------------------------------------------------------------------------------
1 | <% if model && model.errors.any? %>
2 |
3 |
<%= pluralize(model.errors.count, "error") %> prohibited this <%= model.to_model.class.name.underscore.humanize.downcase %> from being saved:
4 |
5 |
6 | <% model.errors.full_messages.each do |msg| %>
7 | <%= msg %>
8 | <% end %>
9 |
10 |
11 | <% end %>
12 |
--------------------------------------------------------------------------------
/app/views/shared/_navbar.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to events_path, class: 'app-name' do %>
3 | <%= image_tag asset_path('logo.svg'), alt: 'Bridge Troll' %>
4 | <% end %>
5 | <% if user_signed_in? %>
6 | Found a bug? Or some unexpected behavior? Let us know !
7 | <% end %>
8 |
28 |
29 |
--------------------------------------------------------------------------------
/app/views/shared/_organizer_action.erb:
--------------------------------------------------------------------------------
1 |
2 | <% options = {
3 | class: "organizer-action",
4 | data: { }
5 | } %>
6 | <% options[:data][:confirm] = local_assigns[:confirm] if local_assigns[:confirm] %>
7 | <% options[:class] += ' organizer-action-inline' if local_assigns[:style] == :inline %>
8 | <% options[:method] = verb if local_assigns[:verb] %>
9 | <%= link_to path, options do %>
10 |
11 |
12 | <%= text %>
13 |
14 | <% end %>
15 | <% if defined?(tip) %>
16 |
17 | <%= tip %>
18 |
19 | <% end %>
20 |
21 |
--------------------------------------------------------------------------------
/app/views/shared/_organizer_breadcrumb.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to 'Events', events_path %>
3 | »
4 | <%= link_to @event.title, event_path(@event) %>
5 | <% if defined?(current_page_title) %>
6 | »
7 | <%= current_page_title %>
8 | <% end %>
9 |
15 |
--------------------------------------------------------------------------------
/app/views/shared/_plus_one_disclaimer.html.erb:
--------------------------------------------------------------------------------
1 | All workshops on Bridge Troll focus on providing opportunities for under-represented populations in tech. If you are not a member of an underrepresented group, you can only attend as a student if you're the guest of someone who is a member of the workshop's target population. (For example, men can come as the guest of a woman, a straight person can attend as the guest of a gay person, etc.)
2 | If you are not a member of this workshop's target demographic, list the name of the person who is bringing you here:
3 |
--------------------------------------------------------------------------------
/app/views/shared/_profile_gravatars.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= gravatar_image_tag(@user.email, alt: '', size: 115) %>
3 |
4 |
--------------------------------------------------------------------------------
/app/views/shared/_region_select.html.erb:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/app/views/shared/_rsvp_actions_unauthenticated.html.erb:
--------------------------------------------------------------------------------
1 | <% if event.open? %>
2 | <% if event.allow_student_rsvp? %>
3 |
7 | <%= student_attend_button_text(event) %>
8 |
9 | <% end %>
10 |
14 | <%= volunteer_attend_button_text(event) %>
15 |
16 | <% else %>
17 | RSVPs are closed!
18 | <% end %>
19 |
--------------------------------------------------------------------------------
/app/views/shared/_rsvps.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Title
5 | Date
6 | Location
7 | Role
8 | Session Checkins
9 |
10 |
11 |
12 | <% rsvps.each do |rsvp| %>
13 | <% event = rsvp.event %>
14 |
15 |
16 | <%= link_to event.title, event %>
17 | <%= imported_event_popover_trigger(event) %>
18 |
19 | <%= formatted_date(event.event_sessions.first) %>
20 | <%= event.location.name rescue nil %>
21 | <%= rsvp.role.title %>
22 | <%= event.historical? ? 'N/A' : rsvp.checkins_count %>
23 |
24 | <% end %>
25 |
26 |
--------------------------------------------------------------------------------
/app/views/shared/_sign_in.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Sign in with BridgeTroll
4 | <%= simple_form_for(resource, as: resource_name, url: session_path(resource_name), wrapper_mappings: {boolean: :vertical_boolean}) do |f| %>
5 | <%= f.input :email %>
6 |
7 | <%= f.input :password %>
8 |
9 | <%= f.input :remember_me, as: :boolean %>
10 |
11 |
12 | <%= f.submit "Sign in", class: 'btn btn-submit' %>
13 | <%= link_to "Sign up", new_registration_path(resource_name), class: 'sign_up_link' %>
14 |
15 | <% end %>
16 |
17 |
18 |
Sign in with something else
19 |
20 | <% OmniauthProviders.provider_data.each do |provider| %>
21 |
22 | <%= link_to omniauth_authorize_path(:user, provider[:key], origin: session["user_return_to"]), method: :post do %>
23 |
24 | <%= provider[:name] %>
25 | <% end %>
26 |
27 | <% end %>
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/views/shared/_sign_in_dialog.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 | <%= render "shared/sign_in", resource: User.new %>
10 | <%= devise_links(:forgot_password, :confirmation_instructions) %>
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/views/shared/_welcome.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= image_tag asset_path('troll_plus_logo.svg'), alt: '' %>
6 |
7 |
8 |
Do you want to learn to code with fun people? <%= link_to "Sign up", new_user_registration_path %> and RSVP for an event.
9 | Or, learn more <%= link_to "about Bridge Troll",
10 | about_path %>.
11 | This is open source software, so feel free to <%= link_to "peruse our code.", "http://www.github.com/railsbridge/bridge_troll" %>
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/views/surveys/_results.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | What was great?
5 | What could have been better?
6 | <% if student_results %>
7 | Was the class or section you were in appropriate to your skill level?
8 | <% end %>
9 | Comments
10 | #
11 |
12 |
13 | <% surveys.each do |survey| %>
14 |
15 | <%= survey.good_things%>
16 | <%= survey.bad_things %>
17 | <% if student_results %>
18 | <%= survey.appropriate_for_skill %>
19 | <% end %>
20 | <%= survey.other_comments %>
21 | <%= survey.recommendation_likelihood %>
22 |
23 | <% end %>
24 |
25 |
--------------------------------------------------------------------------------
/app/views/surveys/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 | <%= content_for(:header_link, event_organizer_tools_path(@event)) %>
3 |
4 | <%= render partial: 'shared/organizer_breadcrumb', locals: {current_page_title: 'Survey Results'} %>
5 |
6 | Student Survey Results
7 | <%= render 'results', surveys: @event.student_surveys, student_results: true %>
8 |
9 | Volunteer Survey Results
10 | <%= render 'results', surveys: @event.volunteer_surveys, student_results: false %>
11 |
--------------------------------------------------------------------------------
/app/views/users/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, "Users") %>
2 |
3 |
4 |
5 |
6 | Name
7 | Meetup Profile
8 | # Attended as Student
9 | # Attended as Volunteer
10 | # Organized
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/views/volunteers/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_for(:header_text, @event.title) %>
2 | <%= content_for(:header_link, event_organizer_tools_path(@event)) %>
3 |
4 | <%= render partial: 'shared/organizer_breadcrumb', locals: {current_page_title: 'Attending volunteer details'} %>
5 |
6 |
7 |
8 |
9 | Name
10 | Subject Experience
11 | Teaching Experience
12 | Volunteer's Preference
13 | <% @volunteer_rsvps.each do |rsvp| %>
14 |
15 |
16 | <%= user_gravatar(rsvp.user) %>
17 | <%= link_to rsvp.user.full_name, user_profile_path(rsvp.user) %>
18 |
19 | <%= rsvp.subject_experience %>
20 | <%= rsvp.teaching_experience %>
21 | <%= rsvp.formatted_preference %>
22 |
23 | <% end %>
24 |
25 |
26 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/bin/dev:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | exec "./bin/rails", "server", *ARGV
3 |
--------------------------------------------------------------------------------
/bin/find_spec_or_impl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | filename = ARGV[0]
4 | in_spec = filename =~ /_spec/
5 | in_js = filename =~ /js$/
6 |
7 | if in_js
8 | impl_prefix, spec_prefix, extension = "app/assets/javascripts/", "spec/javascripts/", ".js"
9 | elsif filename =~ %r{^(lib|spec/lib)}
10 | impl_prefix, spec_prefix, extension = "lib/", "spec/lib/", ".rb"
11 | else
12 | impl_prefix, spec_prefix, extension = "app/", "spec/", ".rb"
13 | end
14 |
15 | related_file = if in_spec
16 | filename.gsub(/^#{spec_prefix}/, impl_prefix).gsub(/_spec#{extension}$/, extension.to_s)
17 | else
18 | filename.gsub(/^#{impl_prefix}/, spec_prefix).gsub(/#{extension}$/, "_spec#{extension}")
19 | end
20 |
21 | print related_file
22 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/bin/rerun_failures:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | puts "Paste a list of rspec failures, followed by a blank line:"
4 |
5 | lines = []
6 | while (line = gets)
7 | break if line == "\n"
8 | lines << line
9 | end
10 |
11 | tests = lines.map { |line| line.match(/rspec (.*?) #/)[1] }.compact
12 | cmd = "rspec #{tests.join(' ')}"
13 | puts cmd
14 | exec(cmd)
15 |
--------------------------------------------------------------------------------
/bin/rspec:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | require 'bundler/setup'
8 | load Gem.bin_path('rspec-core', 'rspec')
9 |
--------------------------------------------------------------------------------
/bin/rubocop:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "rubygems"
3 | require "bundler/setup"
4 |
5 | # explicit rubocop config increases performance slightly while avoiding config confusion.
6 | ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
7 |
8 | load Gem.bin_path("rubocop", "rubocop")
9 |
--------------------------------------------------------------------------------
/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Updating database =="
24 | system! 'bin/rails db:migrate'
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! 'bin/rails log:clear tmp:clear'
28 |
29 | puts "\n== Restarting application server =="
30 | system! 'bin/rails restart'
31 | end
32 |
--------------------------------------------------------------------------------
/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
5 | select { |dir| File.expand_path(dir) != __dir__ }.
6 | product(["yarn", "yarn.cmd", "yarn.ps1"]).
7 | map { |dir, file| File.expand_path(file, dir) }.
8 | find { |file| File.executable?(file) }
9 |
10 | if yarn
11 | exec yarn, *ARGV
12 | else
13 | $stderr.puts "Yarn executable was not detected in the system."
14 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
15 | exit 1
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is used by Rack-based servers to start the application.
4 |
5 | require_relative 'config/environment'
6 |
7 | run Rails.application
8 | Rails.application.load_server
9 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
4 |
5 | require 'bundler/setup' # Set up gems listed in the Gemfile.
6 | require 'logger' # Fix concurrent-ruby removing logger dependency which Rails itself does not have
7 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
8 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | development:
7 | adapter: sqlite3
8 | database: db/development.sqlite3
9 | pool: 5
10 | timeout: 5000
11 |
12 | <% if ENV['FORCE_POSTGRES'] %>
13 | development:
14 | adapter: postgresql
15 | database: bridgetroll_development
16 | host: localhost
17 | <% end %>
18 |
19 | # Warning: The database defined as "test" will be erased and
20 | # re-generated from your development database when you run "rake".
21 | # Do not set this db to the same as development or production.
22 | test:
23 | adapter: sqlite3
24 | database: db/test<%= ENV['TEST_ENV_NUMBER'] %>.sqlite3
25 | pool: 5
26 | timeout: 5000
27 |
28 | <% if ENV['FORCE_POSTGRES'] %>
29 | test:
30 | adapter: postgresql
31 | database: bridgetroll_test<%= ENV['TEST_ENV_NUMBER'] %>
32 | host: localhost
33 | user: <%= ENV["POSTGRES_USER"] %>
34 | password: <%= ENV["POSTGRES_PASSWORD"] %>
35 | <% end %>
36 |
37 | production:
38 | adapter: postgresql
39 | encoding: unicode
40 | url: <%= ENV["DATABASE_URL"] %>
41 | pool: <%= ENV["DB_POOL"] || ENV['RAILS_MAX_THREADS'] || 5 %>
42 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Load the Rails application.
4 | require_relative 'application'
5 |
6 | # Initialize the Rails application.
7 | Rails.application.initialize!
8 |
--------------------------------------------------------------------------------
/config/environments/staging.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Just use the production settings
4 | require File.expand_path('production.rb', __dir__)
5 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # ActiveSupport::Reloader.to_prepare do
6 | # ApplicationController.renderer.defaults.merge!(
7 | # http_host: 'example.org',
8 | # https: false
9 | # )
10 | # end
11 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Version of your assets, change this if you want to expire all your assets.
6 | Rails.application.config.assets.version = '1.2'
7 |
8 | # Add additional assets to the asset load path.
9 | # Rails.application.config.assets.paths << Emoji.images_path
10 | # Add Yarn node_modules folder to the asset load path.
11 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
12 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
6 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) }
7 |
8 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
9 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
10 | Rails.backtrace_cleaner.remove_silencers! if ENV['BACKTRACE']
11 |
--------------------------------------------------------------------------------
/config/initializers/constants.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | COOKBOOK_LINK = 'https://github.com/bridgefoundry/WorkshopCookbook/wiki/Cookbook'
4 | COOKBOOK_PLANNING_TASKS_LINK = 'https://github.com/bridgefoundry/WorkshopCookbook/wiki/Workshop-Planning-Tasks'
5 |
--------------------------------------------------------------------------------
/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Define an application-wide content security policy.
6 | # See the Securing Rails Applications Guide for more information:
7 | # https://guides.rubyonrails.org/security.html#content-security-policy-header
8 |
9 | # Rails.application.configure do
10 | # config.content_security_policy do |policy|
11 | # policy.default_src :self, :https
12 | # policy.font_src :self, :https, :data
13 | # policy.img_src :self, :https, :data
14 | # policy.object_src :none
15 | # policy.script_src :self, :https
16 | # policy.style_src :self, :https
17 | # # Specify URI for violation reports
18 | # # policy.report_uri "/csp-violation-report-endpoint"
19 | # end
20 | #
21 | # # Generate session nonces for permitted importmap, inline scripts, and inline styles.
22 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
23 | # config.content_security_policy_nonce_directives = %w(script-src style-src)
24 | #
25 | # # Report violations without enforcing the policy.
26 | # # config.content_security_policy_report_only = true
27 | # end
28 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Specify a serializer for the signed and encrypted cookie jars.
6 | # Valid options are :json, :marshal, and :hybrid.
7 | Rails.application.config.action_dispatch.cookies_serializer = :json
8 |
--------------------------------------------------------------------------------
/config/initializers/cors.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Avoid CORS issues when API is called from the frontend app.
6 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
7 |
8 | # Read more: https://github.com/cyu/rack-cors
9 |
10 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do
11 | # allow do
12 | # origins 'example.com'
13 | #
14 | # resource '*',
15 | # headers: :any,
16 | # methods: [:get, :post, :put, :patch, :delete, :options, :head]
17 | # end
18 | # end
19 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
6 | # Use this to limit dissemination of sensitive information.
7 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
8 | Rails.application.config.filter_parameters += %i[
9 | passw email secret token _key crypt salt certificate otp ssn cvv cvc
10 | ]
11 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Add new inflection rules using the following format. Inflections
6 | # are locale specific, and you may define rules for as many different
7 | # locales as you wish. All of these examples are active by default:
8 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
9 | # inflect.plural /^(ox)$/i, '\1en'
10 | # inflect.singular /^(ox)en/i, '\1'
11 | # inflect.irregular 'person', 'people'
12 | # inflect.uncountable %w( fish sheep )
13 | # end
14 |
15 | # These inflection rules are supported but not enabled by default:
16 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
17 | # inflect.acronym 'RESTful'
18 | # end
19 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Add new mime types for use in respond_to blocks:
6 | # Mime::Type.register "text/richtext", :rtf
7 |
--------------------------------------------------------------------------------
/config/initializers/per_form_csrf_tokens.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Enable per-form CSRF tokens.
6 | Rails.application.config.action_controller.per_form_csrf_tokens = true
7 |
--------------------------------------------------------------------------------
/config/initializers/permissions_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Define an application-wide HTTP permissions policy. For further
4 | # information see https://developers.google.com/web/updates/2018/06/feature-policy
5 | #
6 | # Rails.application.config.permissions_policy do |f|
7 | # f.camera :none
8 | # f.gyroscope :none
9 | # f.microphone :none
10 | # f.usb :none
11 | # f.fullscreen :self
12 | # f.payment :self, "https://secure.example.com"
13 | # end
14 |
--------------------------------------------------------------------------------
/config/initializers/rack_profiler.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rack-mini-profiler'
4 |
5 | # initialization is skipped so trigger it
6 | Rack::MiniProfilerRails.initialize!(Rails.application)
7 |
--------------------------------------------------------------------------------
/config/initializers/request_forgery_protection.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Enable origin-checking CSRF mitigation.
6 | Rails.application.config.action_controller.forgery_protection_origin_check = true
7 |
--------------------------------------------------------------------------------
/config/initializers/sentry.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | if Rails.env.production?
4 | Sentry.init do |config|
5 | # get breadcrumbs from logs
6 | config.breadcrumbs_logger = %i[active_support_logger http_logger]
7 | # Add data like request headers and IP for users, if applicable;
8 | # see https://docs.sentry.io/platforms/ruby/data-management/data-collected/ for more info
9 | config.send_default_pii = true
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | Rails.application.config.session_store :cookie_store, key: '_bridgetroll_session'
6 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # This file contains settings for ActionController::ParamsWrapper which
6 | # is enabled by default.
7 |
8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
9 | ActiveSupport.on_load(:action_controller) do
10 | wrap_parameters format: [:json]
11 | end
12 |
13 | # To enable root element in JSON for ActiveRecord objects.
14 | # ActiveSupport.on_load(:active_record) do
15 | # self.include_root_in_json = true
16 | # end
17 |
--------------------------------------------------------------------------------
/config/locales/simple_form.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | simple_form:
3 | "yes": 'Yes'
4 | "no": 'No'
5 | required:
6 | text: 'required'
7 | mark: '*'
8 | # You can uncomment the line below if you need to overwrite the whole required html.
9 | # When using html, text and mark won't be used.
10 | # html: '* '
11 | error_notification:
12 | default_message: "Please review the problems below:"
13 | # Labels and hints examples
14 | # labels:
15 | # defaults:
16 | # password: 'Password'
17 | # user:
18 | # new:
19 | # email: 'E-mail to sign in.'
20 | # edit:
21 | # email: 'E-mail.'
22 | # hints:
23 | # defaults:
24 | # username: 'User name to sign in.'
25 | # password: 'No special characters, please.'
26 |
27 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Spring.watch(
4 | '.ruby-version',
5 | '.rbenv-vars',
6 | 'tmp/restart.txt',
7 | 'tmp/caching-dev.txt'
8 | )
9 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/db/migrate/20160110000811_add_location_to_event_sessions.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddLocationToEventSessions < ActiveRecord::Migration[4.2]
4 | def change
5 | add_reference :event_sessions, :location, index: true, foreign_key: true
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20160211061631_add_chapter_leaderships_again.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddChapterLeadershipsAgain < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :chapter_leaderships do |t|
6 | t.integer :user_id
7 | t.integer :chapter_id
8 |
9 | t.references
10 | end
11 |
12 | add_foreign_key :chapter_leaderships, :users
13 | add_foreign_key :chapter_leaderships, :chapters
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20160312224628_create_organization_leaderships.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateOrganizationLeaderships < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :organization_leaderships do |t|
6 | t.references :user, index: true, foreign_key: true
7 | t.references :organization, index: true, foreign_key: true
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20160620044512_add_unaccent_extension_for_postgres.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddUnaccentExtensionForPostgres < ActiveRecord::Migration[4.2]
4 | def change
5 | enable_extension 'unaccent'
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20160710221707_create_organization_subscriptions.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateOrganizationSubscriptions < ActiveRecord::Migration[4.2]
4 | def change
5 | create_table :organization_subscriptions do |t|
6 | t.references :user
7 | t.references :organization
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20160807055624_add_twitter_username_to_profiles.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddTwitterUsernameToProfiles < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :profiles, :twitter_username, :string
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20161002052810_add_external_event_editor_to_users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddExternalEventEditorToUsers < ActiveRecord::Migration[4.2]
4 | def change
5 | add_column :users, :external_event_editor, :boolean, default: false
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20161204040052_rename_external_event_data_to_imported_event_data.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RenameExternalEventDataToImportedEventData < ActiveRecord::Migration[4.2]
4 | def change
5 | rename_column :events, :external_event_data, :imported_event_data
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20161206055208_regenerate_canonical_sfruby_meetup_urls.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RegenerateCanonicalSfrubyMeetupUrls < ActiveRecord::Migration[4.2]
4 | class Event < ApplicationRecord
5 | serialize :imported_event_data, coder: JSON
6 | end
7 |
8 | def change
9 | # The old format stored was like http://www.sfruby.info/events/13311831/,
10 | # but those URLs don't work anymore for whatever reason.
11 |
12 | # URLs of the form https://www.meetup.com/sfruby/events/13311831/
13 | # still work fine.
14 | meetup_events = Event.where.not(imported_event_data: nil)
15 | .select { |e| e.imported_event_data['student_event']['url'].include?('sfruby') }
16 | meetup_events.each do |e|
17 | imported_event_data = e.imported_event_data
18 | imported_event_data['volunteer_event']['url'] =
19 | "https://www.meetup.com/sfruby/events/#{imported_event_data['volunteer_event']['id']}/"
20 | imported_event_data['student_event']['url'] =
21 | "https://www.meetup.com/sfruby/events/#{imported_event_data['student_event']['id']}/"
22 | e.update_attribute(:imported_event_data, imported_event_data)
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/db/migrate/20161228162402_create_courses.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateCourses < ActiveRecord::Migration[5.0]
4 | def change
5 | create_table :courses do |t|
6 | t.string :name
7 | t.string :title
8 | t.text :description
9 |
10 | t.timestamps null: false
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20161228162440_create_levels.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateLevels < ActiveRecord::Migration[5.0]
4 | def change
5 | create_table :levels do |t|
6 | t.references :course, foreign_key: true
7 | t.integer :num
8 | t.string :color
9 | t.string :title
10 | t.text :level_description
11 |
12 | t.timestamps null: false
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20170110062210_set_id_sequence_for_new_courses.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class SetIdSequenceForNewCourses < ActiveRecord::Migration[5.0]
4 | def up
5 | if Rails.application.using_postgres?
6 | execute('ALTER SEQUENCE courses_id_seq START with 10000 RESTART;')
7 | else
8 | execute("UPDATE sqlite_sequence SET seq = 10000 WHERE name = 'courses';")
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20170202035358_add_food_provided_to_events.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddFoodProvidedToEvents < ActiveRecord::Migration[5.0]
4 | def change
5 | add_column(:events, :food_provided, :boolean, default: true, null: false)
6 | Event.update_all(food_provided: true)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20170319191233_add_custom_question_to_events.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddCustomQuestionToEvents < ActiveRecord::Migration[5.0]
4 | def change
5 | add_column :events, :custom_question, :text
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20170319192836_add_custom_question_answer_to_rsvps.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddCustomQuestionAnswerToRsvps < ActiveRecord::Migration[5.0]
4 | def change
5 | add_column :rsvps, :custom_question_answer, :text
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20170707161519_add_appropriate_class_for_skill_to_survey.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddAppropriateClassForSkillToSurvey < ActiveRecord::Migration[5.0]
4 | def change
5 | add_column :surveys, :appropriate_for_skill, :text
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200102135517_add_primary_key_to_regions_users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AddPrimaryKeyToRegionsUsers < ActiveRecord::Migration[5.2]
4 | def change
5 | add_column :regions_users, :id, :primary_key
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Dir[Rails.root.join('db/seeds/*.rb')].each do |seed_file|
4 | require seed_file
5 | end
6 |
7 | if Rails.env.development?
8 | Seeder.seed_region
9 | Seeder.seed_chapter
10 | Seeder.admin_user
11 | Seeder.seed_courses
12 | Seeder.seed_event
13 | Seeder.seed_multiple_location_event
14 | Seeder.seed_past_event
15 | end
16 |
--------------------------------------------------------------------------------
/db/seeds/admin_user.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Seeder
4 | def self.admin_user
5 | # seeds the database with an admin user
6 | admin = User.where(email: 'admin@example.com').first_or_initialize
7 | admin.update(
8 | password: 'password',
9 | first_name: 'Admin',
10 | last_name: 'User',
11 | time_zone: 'Pacific Time (US & Canada)',
12 | admin: true
13 | )
14 | admin.confirm
15 | admin.save!
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/db/seeds/course_populator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module CoursePopulator
4 | module_function
5 |
6 | def populate_courses
7 | file = YAML.safe_load(File.expand_path('courses.yaml', __dir__))
8 | YAML.load_file(file).each do |course|
9 | c = Course.where(
10 | id: course[:id]
11 | ).first_or_create!(
12 | name: course[:name],
13 | title: course[:title],
14 | description: course[:description]
15 | )
16 | course[:levels].each do |level|
17 | c.levels.where(
18 | num: level[:level]
19 | ).first_or_create!(
20 | color: level[:color],
21 | title: level[:title],
22 | level_description: level[:level_description]
23 | )
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/db/seeds/seed_chapter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Seeder
4 | def self.seed_chapter
5 | org = Organization.find_or_create_by(name: 'RailsBridge')
6 |
7 | Chapter.find_or_create_by(name: 'RailsBridge Seattle', organization: org)
8 | Chapter.find_or_create_by(name: 'RailsBridge San Francisco', organization: org)
9 | Chapter.find_or_create_by(name: 'RailsBridge Tulsa', organization: org)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/seeds/seed_courses.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative 'course_populator'
4 |
5 | module Seeder
6 | def self.seed_courses
7 | CoursePopulator.populate_courses
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/seeds/seed_region.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Seeder
4 | def self.seed_region
5 | Region.find_or_create_by(name: 'Seattle')
6 | Region.find_or_create_by(name: 'San Francisco')
7 | Region.find_or_create_by(name: 'Tulsa')
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/devenv.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | lib,
4 | ...
5 | }:
6 |
7 | let
8 | isCI = builtins.getEnv "CI" != "";
9 | in
10 | {
11 | packages = with pkgs; [
12 | git
13 | libyaml.dev # needed by psych / needed by rails
14 | ];
15 |
16 | # this is required by the pg gem on linux
17 | env = lib.mkIf (isCI) {
18 | LD_LIBRARY_PATH = lib.makeLibraryPath [
19 | pkgs.krb5
20 | pkgs.openldap
21 | ];
22 | };
23 |
24 | services.postgres = {
25 | enable = true;
26 | listen_addresses = "localhost";
27 | package = pkgs.postgresql_17; # this aligns with heroku
28 | };
29 |
30 | languages.ruby = {
31 | enable = true;
32 | versionFile = ./.ruby-version;
33 | bundler.enable = true;
34 | };
35 |
36 | languages.javascript = {
37 | enable = true;
38 | yarn.enable = true;
39 | yarn.install.enable = true;
40 | };
41 |
42 | # don't enable git-hooks on CI
43 | # although this also disables being able to execute these checks with `devenv test`
44 | # it's ok because hooks are broken in CI anyways.
45 | #
46 | # If we have a dev that can't use hooks on linux we should change this conditional
47 | git-hooks.hooks = lib.mkIf (!isCI) {
48 | shellcheck.enable = true;
49 | nixfmt-rfc-style.enable = true;
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/devenv.yaml:
--------------------------------------------------------------------------------
1 | inputs:
2 | nixpkgs:
3 | url: github:cachix/devenv-nixpkgs/rolling
4 | nixpkgs-ruby:
5 | url: github:bobvanderlinden/nixpkgs-ruby
6 | inputs:
7 | nixpkgs:
8 | follows: nixpkgs
9 |
--------------------------------------------------------------------------------
/doc/admin.md:
--------------------------------------------------------------------------------
1 | # Admin Tasks
2 |
3 | ## Create a new chapter
4 |
5 | 1. Log in to https://events.bridgefoundry.org/
6 | 1. Navigate to https://events.bridgefoundry.org/chapters
7 | 1. At the bottom on the page click "New Chapter" which will direct you to https://events.bridgefoundry.org/chapters/new
8 | 1. Fill out the appropriate information for creating a new chapter.
9 | 1. After the new chapter is created, you have the options to edit chapter leaders and edit the new chapter information you just entered.
10 |
--------------------------------------------------------------------------------
/lib/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/lib/assets/.gitkeep
--------------------------------------------------------------------------------
/lib/tasks/external_event_import.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | desc 'Import external events from a csv'
4 | task :import_external_events, [:url] => :environment do |_t, args|
5 | ExternalEventImporter.new.import(args[:url])
6 | end
7 |
--------------------------------------------------------------------------------
/lib/tasks/jasmine.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | namespace :jasmine do
4 | desc 'Precompile assets, run Jasmine browser specs, and clean up precompiled assets'
5 | task ci: :environment do
6 | # 1. Precompile assets for Jasmine tests
7 | Rake::Task['assets:precompile'].invoke
8 |
9 | # 2. Run Jasmine browser specs
10 | raise 'Jasmine browser specs failed' unless system('jasmine-browser-runner runSpecs')
11 | ensure
12 | # 3. Clean up precompiled assets
13 | Rake::Task['assets:clobber'].invoke
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/tasks/populate_courses.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require Rails.root.join('db/seeds/course_populator')
4 |
5 | desc 'Populate courses (only run once)'
6 | task populate_courses: :environment do |_t, _args|
7 | CoursePopulator.populate_courses
8 | end
9 |
--------------------------------------------------------------------------------
/lib/tasks/rspec_with_retries.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative 'support/rspec_rerunner'
4 |
5 | desc 'Run rspec tests, rerunning if anything fails'
6 | task rspec_with_retries: :environment do
7 | RspecRerunner.new.run_tests
8 | end
9 |
--------------------------------------------------------------------------------
/lib/tasks/scheduler.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # these reminders are sent by the heroku reminder addon
4 |
5 | desc 'Send reminders for upcoming events within the reminder window'
6 | task send_reminders: :environment do
7 | puts 'Sending reminder emails...'
8 | ReminderSender.send_all_reminders
9 | puts '...done.'
10 | end
11 |
12 | desc 'Send surveys for all past events'
13 | task send_surveys: :environment do
14 | puts 'Sending surveys...'
15 | SurveySender.send_all_surveys
16 | puts '...done.'
17 | end
18 |
--------------------------------------------------------------------------------
/lib/tasks/support/failure_file_parser.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class FailureFileParser
4 | def initialize(filename)
5 | @filename = filename
6 | end
7 |
8 | def failures_from_persistence_file
9 | File
10 | .readlines(@filename)[2..]
11 | .map { |l| l.split('|').map(&:strip) }
12 | .select { |file_ref| file_ref[1] == 'failed' }
13 | .map { |file_ref| { example_id: file_ref[0] } }
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/tasks/support/rspec_rerunner.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'yaml'
4 | require_relative 'failure_file_parser'
5 |
6 | class RspecRerunner
7 | RERUN_ATTEMPTS = ENV.fetch('RERUN_ATTEMPTS', 3).to_i
8 | RERUN_THRESHOLD = ENV.fetch('RERUN_THRESHOLD', 5).to_i
9 |
10 | def initialize
11 | @failure_file_parser = FailureFileParser.new(
12 | Rails.root.join('tmp/rspec_examples.txt')
13 | )
14 | end
15 |
16 | def run_tests
17 | Bundler.with_unbundled_env do
18 | succeeded_initially = system('bundle exec rake parallel:spec')
19 | return if succeeded_initially
20 | end
21 |
22 | failure_count = @failure_file_parser.failures_from_persistence_file.length
23 | if failure_count > RERUN_THRESHOLD
24 | puts "#{failure_count} tests failed, first run, which is over the rerun threshold of #{RERUN_THRESHOLD}"
25 | exit 1
26 | end
27 |
28 | RERUN_ATTEMPTS.times do
29 | Bundler.with_unbundled_env do
30 | succeeded_on_retry = system("SPEC_OPTS='--only-failures' bundle exec rake parallel:spec")
31 | return if succeeded_on_retry
32 | end
33 | end
34 |
35 | puts "TESTS HAVE FAILED AFTER #{RERUN_ATTEMPTS} RETRIES!"
36 | exit 1
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bridge_troll",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "datatables.net": "1.10.22",
6 | "datatables.net-bs": "1.10.13",
7 | "markerclustererplus": "2.1.4",
8 | "masonry-layout": "4.1.1",
9 | "select2": "4.0.3"
10 | },
11 | "devDependencies": {
12 | "jasmine-browser-runner": "^3.0.0",
13 | "jasmine-core": "^5.7.1",
14 | "jasmine-jquery": "^2.1.1",
15 | "sinon": "1.17.1"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/public/email_images/confirm_account.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/public/email_images/confirm_account.gif
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/public/favicon.ico
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/public/troll_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/public/troll_icon.png
--------------------------------------------------------------------------------
/script/rubymine_open_spec_or_impl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ -z "$1" ]; then
4 | echo "This script must be called with an argument" >&2
5 | exit 1
6 | fi
7 |
8 | /usr/local/bin/mine $(script/find_spec_or_impl.rb $1)
9 |
--------------------------------------------------------------------------------
/spec/controllers/chapters/leaders_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe Chapters::LeadersController do
6 | let(:admin) { create(:user, admin: true, first_name: 'Admin', last_name: 'User') }
7 | let(:chapter) { create :chapter }
8 |
9 | describe 'potential leaders' do
10 | before do
11 | sign_in admin
12 | end
13 |
14 | it 'includes all users not currently assigned as leaders' do
15 | leader = create(:user, first_name: 'Steve')
16 | chapter.leaders << leader
17 |
18 | non_leader = create(:user, first_name: 'Steve')
19 |
20 | get :potential, params: { chapter_id: chapter.id, q: 'Steve' }, format: :json
21 |
22 | expect(JSON.parse(response.body).map { |u| u['id'] }).to eq([non_leader.id])
23 | end
24 | end
25 |
26 | describe '#destroy' do
27 | before do
28 | sign_in admin
29 | end
30 |
31 | it 'removes a chapter leader' do
32 | leader = create(:user)
33 | chapter.leaders << leader
34 |
35 | expect do
36 | delete :destroy, params: { chapter_id: chapter.id, id: leader.id }
37 | end.to change(chapter.leaders, :count).by(-1)
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/controllers/checkiners_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe CheckinersController do
6 | let(:event) { create(:event) }
7 | let(:user) { create(:user) }
8 |
9 | context 'a user that is logged in and is an organizer for a published event' do
10 | before do
11 | event.organizers << user
12 |
13 | sign_in user
14 | end
15 |
16 | it 'can see list of checkiners' do
17 | get :index, params: { event_id: event.id }
18 | expect(response).to be_successful
19 | end
20 |
21 | describe 'assigning checkiners' do
22 | it 'can promote a user to checkiner' do
23 | other_user_rsvp = create(:rsvp, event: event)
24 | expect do
25 | post :create, params: { event_id: event.id, event_checkiner: { rsvp_id: other_user_rsvp.id } }
26 | end.to(change { other_user_rsvp.reload.checkiner })
27 | expect(response).to redirect_to(event_checkiners_path(event))
28 | end
29 |
30 | it 'shows an error if no user is provided' do
31 | post :create, params: { event_id: event.id }
32 | expect(assigns(:event).errors[:base].length).to be >= 1
33 | end
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/spec/controllers/events/students_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe Events::StudentsController do
6 | let(:student_rsvp) { create(:student_rsvp) }
7 | let(:event) { student_rsvp.event }
8 | let(:organizer) { create(:user) }
9 |
10 | before do
11 | event.organizers << organizer
12 | sign_in organizer
13 | end
14 |
15 | describe '#index' do
16 | it 'responds successfully, with the right headers' do
17 | get :index, params: { event_id: event.to_param }, format: :csv
18 | expect(assigns(:students)).to eq(event.student_rsvps)
19 | expect(response.media_type).to eq('text/csv')
20 | expect(response).to be_successful
21 |
22 | csv_rows = CSV.parse(response.body)
23 | expect(csv_rows[0][0]).to eq('Student Name')
24 | expect(csv_rows[1][0]).to eq(student_rsvp.user.full_name)
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/controllers/profiles_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe ProfilesController do
6 | let(:user) { create(:user) }
7 | let(:other_user) { create(:user) }
8 |
9 | before do
10 | sign_in user
11 | end
12 |
13 | describe 'showing profiles' do
14 | render_views
15 |
16 | it 'lets users view their own profile' do
17 | get :show, params: { user_id: user.id }
18 | expect(response).to be_successful
19 | expect(response.body).to include(ERB::Util.html_escape(user.full_name))
20 | end
21 |
22 | it "lets users view other user's profiles" do
23 | get :show, params: { user_id: other_user.id }
24 | expect(response).to be_successful
25 | expect(response.body).to include(ERB::Util.html_escape(other_user.full_name))
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/controllers/static_pages_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe StaticPagesController do
6 | describe 'GET #style_guide' do
7 | before do
8 | get :style_guide
9 | end
10 |
11 | it 'resonds with a success message' do
12 | expect(response.status).to eq(200)
13 | end
14 |
15 | it "renders the 'style_guide' template" do
16 | expect(response).to render_template(:style_guide)
17 | end
18 | end
19 |
20 | describe 'GET #about' do
21 | before do
22 | get :about
23 | end
24 |
25 | it 'resonds with a success message' do
26 | expect(response.status).to eq(200)
27 | end
28 |
29 | it "renders the 'about' template" do
30 | expect(response).to render_template(:about)
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/controllers/users/events_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe Users::EventsController do
6 | let(:user) { FactoryBot.create(:user) }
7 | let(:event) { FactoryBot.create(:event) }
8 |
9 | before { FactoryBot.create(:rsvp, user: user, event: event, checked_in: true) }
10 |
11 | describe '#index' do
12 | it 'responds successfully with json' do
13 | get :index, params: { user_id: user.id }
14 | expect(response.media_type).to eq('application/json')
15 | expect(response).to be_successful
16 | end
17 |
18 | it 'responds with the correct count' do
19 | get :index, params: { user_id: user.id }
20 | expected_response = { event_count: 1 }.to_json
21 | expect(response.body).to eq(expected_response)
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/controllers/volunteers_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe VolunteersController do
6 | let!(:event) { create(:event) }
7 | let!(:user_organizer) { create(:user) }
8 | let!(:vol1) { create(:user, first_name: 'Vol1') }
9 | let!(:vol2) { create(:user, first_name: 'Vol2') }
10 | let!(:vol3) { create(:user, first_name: 'Vol3') }
11 |
12 | before do
13 | event.organizers << user_organizer
14 | create(:teacher_rsvp, user: vol1, event: event)
15 | create(:teacher_rsvp, user: vol2, event: event)
16 | create(:teacher_rsvp, user: vol3, event: event)
17 | sign_in user_organizer
18 | end
19 |
20 | context 'a user that is logged in and is an organizer for the event' do
21 | describe 'index' do
22 | render_views
23 |
24 | it 'is able to see list of volunteers' do
25 | get :index, params: { event_id: event.id }
26 | expect(response).to be_successful
27 |
28 | expect(response.body).to have_content 'Vol1'
29 | expect(response.body).to have_content 'Vol2'
30 | expect(response.body).to have_content 'Vol3'
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/features/admin/admin_dashboard_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe 'the admin dashboard' do
6 | context 'when signed in as a normal user' do
7 | before do
8 | sign_in_as(create(:user))
9 | end
10 |
11 | it 'redirects to the homepage' do
12 | visit '/admin_dashboard'
13 | expect(page).to have_current_path('/')
14 | end
15 | end
16 |
17 | context 'when signed in as an admin' do
18 | before do
19 | admin = create(:user, first_name: 'Gavin', last_name: 'Grapejuice', admin: true)
20 | sign_in_as(admin)
21 | end
22 |
23 | it 'shows a list of admins' do
24 | visit '/admin_dashboard'
25 | expect(page).to have_content('Gavin Grapejuice')
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/features/events/edit_event_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe 'Edit Event' do
6 | let(:user_organizer) { create(:user, email: 'organizer@mail.com', first_name: 'Sam', last_name: 'Spade') }
7 | let(:drafted_event) { create(:event, title: 'draft title', current_state: :draft) }
8 |
9 | before do
10 | drafted_event.organizers << user_organizer
11 |
12 | sign_in_as(user_organizer)
13 | visit edit_event_path(drafted_event)
14 | end
15 |
16 | context 'event saved previously as draft' do
17 | it 'allows a draft to be saved' do
18 | expect(drafted_event).to be_draft
19 | fill_in 'Title', with: 'real title'
20 | check('coc')
21 | click_on 'Submit Event For Approval'
22 |
23 | expect(page).to have_content('Event was successfully updated')
24 | expect(page).to have_content('real title')
25 |
26 | expect(page).to have_current_path event_path(drafted_event), ignore_query: true
27 | expect(Event.find(drafted_event.id)).to be_pending_approval
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/features/events/organizer/attendee_details_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe 'the attendee details modal', :js do
6 | let(:event) { create(:event) }
7 | let!(:student_rsvp) { create(:student_rsvp, user: create(:user), event: event) }
8 |
9 | before do
10 | organizer = create(:user)
11 | event.organizers << organizer
12 |
13 | sign_in_as(organizer)
14 |
15 | visit event_organize_sections_path(event)
16 |
17 | within 'ul.students' do
18 | find('i').click
19 | end
20 | end
21 |
22 | it "lists the student's RSVP details" do
23 | within '.modal-body' do
24 | expect(page).to have_content(student_rsvp.operating_system_title)
25 | expect(page).to have_content(student_rsvp.level_title)
26 | expect(page).to have_content(student_rsvp.job_details)
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/features/external_events/external_event_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe 'External Event' do
6 | let(:admin_user) { create(:user, admin: true) }
7 | let!(:region) { create(:region) }
8 | let!(:chapter) { create(:chapter) }
9 |
10 | before do
11 | sign_in_as(admin_user)
12 | end
13 |
14 | it 'can create a new external event' do
15 | visit external_events_path
16 | click_link 'New External Event'
17 |
18 | select region.name, from: 'external_event_region_id'
19 | select chapter.name, from: 'external_event_chapter_id'
20 |
21 | fill_in 'Name', with: 'Interesting External Event'
22 | fill_in 'URL', with: 'http://example.com/event'
23 | fill_in 'City', with: 'San Francisco'
24 | fill_in 'Location', with: 'Tonga Room'
25 | fill_in 'Organizers', with: 'Ham Sandwich and Cheese Fries'
26 |
27 | click_button 'Create External event'
28 |
29 | expect(ExternalEvent.last.region).to eq(region)
30 |
31 | expect(page).to have_content('Interesting External Event')
32 |
33 | visit region_path(region)
34 |
35 | expect(page).to have_content('Interesting External Event')
36 | expect(ExternalEvent.last.chapter).to eq(chapter)
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/spec/features/organizations/organizations_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe 'organization pages' do
6 | describe 'organization index' do
7 | let(:event) { create(:event, title: 'Some Event') }
8 |
9 | it 'shows a map of events with their last event', :js do
10 | visit organizations_path
11 |
12 | expect(page).to have_css('#gmaps4rails_map')
13 | end
14 | end
15 |
16 | describe 'creating an organization' do
17 | let(:admin) { create(:user, admin: true) }
18 |
19 | it 'allows admins to create a new organization' do
20 | sign_in_as(admin)
21 |
22 | visit new_organization_path
23 | fill_in 'Name', with: 'CantaloupeBridge'
24 |
25 | expect do
26 | click_on 'Create Organization'
27 | end.to change(Organization, :count).by(1)
28 |
29 | expect(Organization.last.name).to eq('CantaloupeBridge')
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/spec/features/style_guide_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe 'visiting the style guide' do
6 | describe 'as an unauthenticated user' do
7 | it 'can view page' do
8 | visit '/style_guide'
9 |
10 | expect(page).to have_content('Style Guide')
11 | end
12 |
13 | it "can redirect from '/styleguide' to #style_guide" do
14 | visit '/styleguide'
15 |
16 | expect(page).to have_current_path '/style_guide'
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/features/users/users_request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe 'the users page', :js do
6 | before do
7 | sign_in_as(create(:user, first_name: 'Some', last_name: 'LoggedInUser'))
8 | create(:user, first_name: 'Some', last_name: 'OtherUser')
9 | create(:meetup_user, full_name: 'Some MeetupUser')
10 | end
11 |
12 | it 'shows a list of users' do
13 | visit '/users'
14 | expect(page).to have_content('Some LoggedInUser')
15 | expect(page).to have_content('Some OtherUser')
16 | expect(page).to have_content('Some MeetupUser')
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/javascripts/helpers/ModelFactories.js:
--------------------------------------------------------------------------------
1 | Factories = (function () {
2 | var Role = Bridgetroll.Enums.Role;
3 |
4 | var id = 9;
5 | return {
6 | attendee: function (attrs) {
7 | id++;
8 | return _.extend({id: id, event_id: 191}, attrs);
9 | },
10 | volunteer: function (attrs) {
11 | return _.extend(this.attendee({role_id: Role.VOLUNTEER}), attrs);
12 | },
13 | student: function (attrs) {
14 | return _.extend(this.attendee({role_id: Role.STUDENT}), attrs);
15 | }
16 | };
17 | })();
--------------------------------------------------------------------------------
/spec/javascripts/helpers/SpecHelper.js:
--------------------------------------------------------------------------------
1 | var fakeServer = _.extend(sinon.fakeServer, {
2 | requestFor: function (url) {
3 | return _.findWhere(this.requests, {url: url});
4 | },
5 |
6 | completeRequest: function (url, body) {
7 | var request = this.requestFor(url);
8 | if (!request) {
9 | throw 'No pending request for ' + url + '. Existing requests: ' + _.pluck(this.requests, 'url');
10 | }
11 |
12 | request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(body));
13 | }
14 | });
15 |
16 | function getFixtures() {
17 | return $('#jasmine-fixtures');
18 | }
19 |
20 | beforeEach(function() {
21 | if (getFixtures().length === 0) {
22 | $('body').append('
');
23 | }
24 | this.server = fakeServer.create();
25 | Bridgetroll.modalContainerSelector = '#jasmine-fixtures';
26 | });
27 |
28 | afterEach(function () {
29 | this.server.restore();
30 | getFixtures().empty();
31 | });
32 |
--------------------------------------------------------------------------------
/spec/javascripts/helpers/whenReady.js:
--------------------------------------------------------------------------------
1 | window.whenReady = function (fn) {
2 | if (document.readyState != "loading") {
3 | fn();
4 | } else {
5 | document.addEventListener("DOMContentLoaded", fn);
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/spec/mailers/previews/admin_preview.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AdminPreview < ActionMailer::Preview
4 | def test_group_mail
5 | options = { to: 'test@example.com' }
6 | AdminMailer.test_group_mail(options)
7 | end
8 |
9 | def test_individual_mail
10 | options = { to: 'test@example.com' }
11 | AdminMailer.test_individual_mail(options)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/mailers/previews/devise/mailer_preview.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Devise
4 | class MailerPreview < ActionMailer::Preview
5 | def confirmation_instructions
6 | Devise::Mailer.confirmation_instructions(User.first, {})
7 | end
8 |
9 | def reset_password_instructions
10 | Devise::Mailer.reset_password_instructions(User.first, {})
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/mailers/previews/event_preview.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class EventPreview < ActionMailer::Preview
4 | def from_organizer
5 | event = Event.first
6 | options = {
7 | event: event,
8 | sender: event.organizers.first,
9 | recipients: event.students,
10 | body: "Howdy folks, what's the haps?"
11 | }
12 |
13 | EventMailer.from_organizer(options)
14 | end
15 |
16 | def unpublished_event
17 | EventMailer.unpublished_event(Event.last)
18 | end
19 |
20 | def event_pending_approval
21 | EventMailer.event_pending_approval(Event.first)
22 | end
23 |
24 | def event_has_been_approved
25 | EventMailer.event_has_been_approved(Event.first)
26 | end
27 |
28 | def new_event
29 | EventMailer.new_event(Event.first)
30 | end
31 |
32 | def new_organizer_alert
33 | EventMailer.new_organizer_alert(Event.first, User.first)
34 | end
35 |
36 | def multiple_location_event
37 | EventMailer.new_event(EventSession.where.not(location_id: nil).first.event)
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/spec/mailers/previews/rsvp_preview.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RsvpPreview < ActionMailer::Preview
4 | def confirmation
5 | RsvpMailer.confirmation(Rsvp.first)
6 | end
7 |
8 | def reminder
9 | RsvpMailer.reminder(Rsvp.first)
10 | end
11 |
12 | def multiple_location_event_reminder
13 | rsvp = EventSession.where.not(location_id: nil).all.filter_map do |event_session|
14 | event_session.rsvps.where(role_id: Role::VOLUNTEER.id).first
15 | end.first
16 | RsvpMailer.reminder(rsvp)
17 | end
18 |
19 | def reminder_for_session
20 | RsvpMailer.reminder_for_session(RsvpSession.first)
21 | end
22 |
23 | def off_waitlist
24 | RsvpMailer.off_waitlist(Rsvp.first)
25 | end
26 |
27 | def childcare_notification
28 | RsvpMailer.childcare_notification(Rsvp.first)
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/mailers/previews/survey_preview.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class SurveyPreview < ActionMailer::Preview
4 | def notification
5 | SurveyMailer.notification(Rsvp.first)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/mailers/survey_mailer_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe SurveyMailer do
6 | let(:rsvp) { create(:rsvp) }
7 | let(:user) { rsvp.user }
8 | let(:event) { rsvp.event }
9 | let(:mail) { described_class.notification(rsvp) }
10 |
11 | describe 'the survey email' do
12 | it_behaves_like 'a mailer view'
13 |
14 | it "is sent to the RSVP'd person" do
15 | expect(mail.to).to eq([user.email])
16 | end
17 |
18 | it 'includes the survey link' do
19 | expect(mail.subject).to eq "How was #{event.title}?"
20 | expect(mail.body).to include 'Click right here to take the survey!'
21 | expect(mail.body).to include 'Test greeting'
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/models/chapter_leadership_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe ChapterLeadership do
6 | describe 'validations' do
7 | let(:user) { create :user }
8 | let(:chapter) { create :chapter }
9 |
10 | describe 'uniqueness' do
11 | let(:duplicate_leadership) { described_class.new user: user, chapter: chapter }
12 |
13 | before { described_class.create user: user, chapter: chapter }
14 |
15 | it "doesn't save dupes" do
16 | expect(duplicate_leadership).to be_invalid
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/models/chapter_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe Chapter do
6 | it { is_expected.to validate_presence_of(:name) }
7 |
8 | describe '#leader?' do
9 | let(:chapter) { create :chapter }
10 | let(:user) { create :user }
11 |
12 | context 'with a user that is a leader' do
13 | before { ChapterLeadership.create(user: user, chapter: chapter) }
14 |
15 | it 'is true' do
16 | expect(chapter).to be_leader(user)
17 | end
18 | end
19 |
20 | context 'with a user that is not a leader' do
21 | it 'is false' do
22 | expect(chapter).not_to be_leader(user)
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/spec/models/course_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe Course do
6 | it { is_expected.to have_many(:levels) }
7 | it { is_expected.to have_many(:events) }
8 | it { is_expected.to validate_presence_of(:name) }
9 | it { is_expected.to validate_presence_of(:title) }
10 | it { is_expected.to validate_presence_of(:description) }
11 |
12 | describe 'validations' do
13 | it 'does not allow multiple levels to have the same position' do
14 | course = build(:course)
15 | course.levels << build(:level, color: 'blue')
16 | course.levels << build(:level, color: 'pink')
17 | expect(course).not_to be_valid
18 | expect(course.errors[:level].join).to match(/position/)
19 | end
20 |
21 | it 'does not allow multiple levels to have the same color' do
22 | course = build(:course)
23 | course.levels << build(:level, num: 1)
24 | course.levels << build(:level, num: 2)
25 | expect(course).not_to be_valid
26 | expect(course.errors[:level].join).to match(/color/)
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/models/event_email_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe EventEmail do
6 | it { is_expected.to belong_to(:event) }
7 | it { is_expected.to belong_to(:sender).required }
8 |
9 | it { is_expected.to have_many(:event_email_recipients).dependent(:destroy) }
10 | it { is_expected.to have_many(:recipient_rsvps).through(:event_email_recipients) }
11 | it { is_expected.to have_many(:recipients).through(:recipient_rsvps).source(:user) }
12 |
13 | it { is_expected.to validate_presence_of(:subject) }
14 | it { is_expected.to validate_presence_of(:body) }
15 | end
16 |
--------------------------------------------------------------------------------
/spec/models/factory_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe 'FactoryBot factories' do
6 | FactoryBot.factories.map(&:name).uniq.each do |factory_name|
7 | next if factory_name.to_s.starts_with?('event')
8 |
9 | it "has a valid #{factory_name} factory" do
10 | expect(create(factory_name)).to be_persisted
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/models/level_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe Level do
6 | it { is_expected.to belong_to(:course).optional }
7 | it { is_expected.to validate_presence_of(:num) }
8 |
9 | it {
10 | expect(described_class.new).to validate_inclusion_of(:num)
11 | .in_array((1..5).to_a)
12 | .with_message('Must be between 1 and 5')
13 | }
14 |
15 | it { is_expected.to validate_presence_of(:color) }
16 | it { is_expected.to validate_presence_of(:title) }
17 | it { is_expected.to validate_presence_of(:level_description) }
18 |
19 | describe 'description' do
20 | let(:course) { create :course }
21 |
22 | it 'shows description as array, not string' do
23 | expect(course.levels.first.description).to include('You have little to no experience with the terminal or a graphical IDE')
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/spec/models/meetup_user_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe MeetupUser do
6 | let(:user) { create(:meetup_user) }
7 |
8 | it "responds to 'profile' with an object that returns false to all profile attributes" do
9 | expect(user.profile.childcaring).to be false
10 | expect(user.profile.writing).to be false
11 | end
12 |
13 | describe '#profile_path' do
14 | it 'returns the same value as the appropriate rails helper' do
15 | expect(user.profile_path).to eq(Rails.application.routes.url_helpers.meetup_user_path(user))
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/models/region_leadership_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe RegionLeadership do
6 | describe 'validations' do
7 | let(:user) { create :user }
8 | let(:region) { create :region }
9 |
10 | describe 'uniqueness' do
11 | let(:duplicate_leadership) { described_class.new user: user, region: region }
12 |
13 | before { described_class.create user: user, region: region }
14 |
15 | it "doesn't save dupes" do
16 | expect(duplicate_leadership).to be_invalid
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/models/region_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe Region do
6 | it { is_expected.to have_many(:locations) }
7 | it { is_expected.to have_many(:users).through(:regions_users) }
8 |
9 | it { is_expected.to validate_presence_of(:name) }
10 |
11 | describe '#leader?' do
12 | let(:region) { create :region }
13 | let(:user) { create :user }
14 |
15 | context 'with a user that is a leader' do
16 | before { RegionLeadership.create(user: user, region: region) }
17 |
18 | it 'is true' do
19 | expect(region).to be_leader(user)
20 | end
21 | end
22 |
23 | context 'with a user that is not a leader' do
24 | it 'is false' do
25 | expect(region).not_to be_leader(user)
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/models/rsvp_session_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe RsvpSession do
6 | context 'checkins counter cache' do
7 | let(:rsvp) { create(:rsvp) }
8 | let!(:session1) { rsvp.rsvp_sessions.first }
9 | let!(:session2) { create(:rsvp_session, rsvp: rsvp) }
10 |
11 | it 'counts the number of checkins' do
12 | expect(rsvp.checkins_count).to eq(0)
13 |
14 | expect do
15 | session1.checked_in = true
16 | session1.save!
17 | end.to change { rsvp.reload.checkins_count }.by(1)
18 |
19 | expect do
20 | session2.checked_in = true
21 | session2.save!
22 | end.to change { rsvp.reload.checkins_count }.by(1)
23 |
24 | expect do
25 | session1.destroy
26 | end.to change { rsvp.reload.checkins_count }.by(-1)
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/services/user_searcher_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails_helper'
4 |
5 | describe UserSearcher do
6 | let!(:user1) { create(:user, first_name: 'Abrahat', last_name: 'Zachson') }
7 |
8 | before { create(:user, first_name: 'Rochalle', last_name: 'Gorfdoor') }
9 |
10 | it 'returns the users matching a search query' do
11 | searcher = described_class.new(User, 'hat')
12 | expect(searcher.as_json).to contain_exactly({ id: user1.id, text: user1.full_name })
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/support/authorization_shared_behaviors.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | shared_examples_for 'an action that requires user log-in' do
4 | it 'does not allow access to anonymous users' do
5 | make_request
6 | expect(response).to redirect_to(new_user_session_path)
7 | end
8 | end
9 |
10 | shared_examples_for 'an event action that requires an organizer' do
11 | it 'does not allow access to anonymous users' do
12 | make_request
13 | expect(response).to redirect_to(new_user_session_path)
14 | end
15 |
16 | it "does not allow access to users who aren't organizers of the event" do
17 | sign_in(create(:user))
18 | make_request
19 | expect(response).to be_redirect
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/support/form_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module FormHelper
4 | def fill_in_select2(option)
5 | page.find('.select2-selection').click
6 | page.find('.select2-search__field').set(option)
7 | expect(page.find('.select2-results__option')).to have_content(option)
8 | page.find('.select2-results__option').click
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/support/jasmine-browser.mjs:
--------------------------------------------------------------------------------
1 | export default {
2 | srcDir: ".",
3 | srcFiles: [
4 | "spec/javascripts/helpers/whenReady.js",
5 | "public/assets/application-*.js",
6 | "public/assets/section_organizer-*.js",
7 | ],
8 | specDir: ".",
9 | specFiles: ["spec/javascripts/**/*[sS]pec.?(m)js"],
10 | helpers: [
11 | "node_modules/sinon/pkg/sinon.js",
12 | "node_modules/jasmine-jquery/lib/jasmine-jquery.js",
13 | "spec/javascripts/helpers/**/*.?(m)js",
14 | ],
15 | env: {
16 | stopSpecOnExpectationFailure: false,
17 | stopOnSpecFailure: false,
18 | random: true,
19 | // Fail if a suite contains multiple suites or specs with the same name.
20 | forbidDuplicateNames: true,
21 | },
22 |
23 | // For security, listen only to localhost. You can also specify a different
24 | // hostname or IP address, or remove the property or set it to "*" to listen
25 | // to all network interfaces.
26 | listenAddress: "localhost",
27 |
28 | // The hostname that the browser will use to connect to the server.
29 | hostname: "localhost",
30 |
31 | browser: {
32 | name: "headlessChrome",
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/spec/support/mailers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec::Matchers.define :be_an_absolute_url do
4 | match do |actual|
5 | URI.parse(actual).host.present?
6 | end
7 | end
8 |
9 | shared_examples_for 'a mailer view' do
10 | def extract_body(mail)
11 | if mail.multipart?
12 | mail.parts.find do |p|
13 | p.media_type.starts_with?('text/html')
14 | end.body.encoded
15 | else
16 | mail.body.encoded
17 | end
18 | end
19 |
20 | it 'uses absolute URLs' do
21 | body = extract_body(mail)
22 |
23 | urls = Capybara.string(body).all('a').pluck(:href)
24 |
25 | expect(urls).to all(be_an_absolute_url)
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/support/row_counting.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | def assert_no_rows_present
4 | rows = {}
5 | total = 0
6 | ApplicationRecord.send(:subclasses).each do |sc|
7 | next if sc.name == 'ActiveRecord::SchemaMigration'
8 |
9 | rows[sc.name] = sc.all.size
10 | total += sc.all.size
11 | end
12 | return unless total > 0
13 |
14 | puts 'Leaked the following rows: '
15 | rows.each do |klass, count|
16 | next unless count > 0
17 |
18 | puts "#{klass}: #{count}"
19 | end
20 | expect(total).to eq(0)
21 | end
22 |
--------------------------------------------------------------------------------
/spec/support/sign_in_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module SignInHelper
4 | def sign_in_as(user, options = {})
5 | if options[:slowly]
6 | visit new_user_session_path
7 | within('#sign-in-page') do
8 | fill_in 'Email', with: user.email
9 | fill_in 'Password', with: user.password
10 | click_button 'Sign in'
11 | end
12 | expect(page).to have_content('Signed in successfully')
13 | else
14 | login_as user, scope: :user
15 | end
16 | end
17 |
18 | def sign_in_with_modal(user)
19 | expect(page).to have_selector('#sign_in_dialog')
20 | within '#sign_in_dialog' do
21 | fill_in 'Email', with: user.email
22 | fill_in 'Password', with: user.password
23 | click_button 'Sign in'
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/spec/support/waiting_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | def wait_for_condition
4 | Timeout.timeout(5) do
5 | loop do
6 | return if yield
7 |
8 | sleep 0.1
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/railsbridge/bridge_troll/3daa0091359ac1944b269cb3093fda0156112761/vendor/assets/stylesheets/.gitkeep
--------------------------------------------------------------------------------