diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..f9653486b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + - package-ecosystem: "bundler" + directory: "/" + target-branch: "develop" + schedule: + interval: "daily" + commit-message: + prefix: "build(deps): " + + - package-ecosystem: "npm" + directory: "/website" + target-branch: "develop" + schedule: + interval: "daily" + commit-message: + prefix: "build(deps): " diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b340e8c3..7208272d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,8 @@ on: branches: - master - develop + - '[0-9].[0-9]' + - '[0-9].[0-9].[0-9]' jobs: build: diff --git a/CHECKS b/CHECKS index 24d7a521f..00dbaece8 100644 --- a/CHECKS +++ b/CHECKS @@ -1,4 +1,4 @@ WAIT=10 -ATTEMPTS=300 +ATTEMPTS=100 /users/sign_in Sign in to diff --git a/Gemfile b/Gemfile index b80ae29ee..8c0e172c7 100644 --- a/Gemfile +++ b/Gemfile @@ -25,6 +25,8 @@ gem 'turbolinks', '~> 5' # gem 'jbuilder', '~> 2.5' # Use Redis adapter to run Action Cable in production gem 'redis', '~> 4.0' +# CORS support +gem 'rack-cors' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' @@ -65,6 +67,8 @@ gem 'jquery-ui-rails' gem 'selectize-rails' gem 'highcharts-rails', '~> 6.0' gem 'bootstrap', '~> 4.3.1' +gem 'fullcalendar-rails' +gem 'momentjs-rails' # Markdown parsing gem 'redcarpet' @@ -76,7 +80,7 @@ gem 'audited', '~> 4.7' # Background job processing gem 'sidekiq', '< 7' -gem 'sidekiq-cron', '~> 1.1' +gem 'sidekiq-cron', github: 'codeRIT/sidekiq-cron', branch: 'master' # Misc support gems gem 'rails-settings-cached', '~> 0.7.2' @@ -111,8 +115,8 @@ end group :test do gem 'test-unit', '~> 3.0' - gem 'shoulda', '~> 3.5' - gem 'shoulda-matchers', '~> 2.0' + gem 'shoulda', '~> 4.0.0' + gem 'shoulda-matchers', '~> 4.4.0' gem 'minitest-reporters' gem 'valid_attribute' gem 'factory_bot_rails' diff --git a/Gemfile.lock b/Gemfile.lock index f310deea9..344bc1684 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,12 @@ +GIT + remote: https://github.com/codeRIT/sidekiq-cron.git + revision: 571d7d74d2828aea2f55381a83f73ac3450e8819 + branch: master + specs: + sidekiq-cron (1.2.0) + fugit (~> 1.1) + sidekiq (>= 4.2.1) + GEM remote: https://rubygems.org/ specs: @@ -129,7 +138,7 @@ GEM dotenv (= 2.7.5) railties (>= 3.2, < 6.1) errbase (0.2.0) - erubi (1.9.0) + erubi (1.10.0) erubis (2.7.0) et-orbi (1.2.4) tzinfo @@ -149,9 +158,13 @@ GEM font-awesome-rails (4.7.0.5) railties (>= 3.2, < 6.1) formatador (0.2.5) - fugit (1.3.6) + fugit (1.4.2) et-orbi (~> 1.1, >= 1.1.8) - raabro (~> 1.3) + raabro (~> 1.4) + fullcalendar-rails (3.9.0.0) + jquery-rails (>= 4.0.5, < 5.0.0) + jquery-ui-rails (>= 5.0.2) + momentjs-rails (>= 2.9.0) globalid (0.4.2) activesupport (>= 4.2.0) groupdate (5.0.0) @@ -205,7 +218,7 @@ GEM rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.7.0) + loofah (2.8.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) lumberjack (1.2.6) @@ -226,6 +239,8 @@ GEM builder minitest (>= 5.0) ruby-progressbar + momentjs-rails (2.20.1) + railties (>= 3.1) msgpack (1.3.3) multi_json (1.15.0) multi_xml (0.6.0) @@ -233,7 +248,7 @@ GEM mustache (1.1.1) mysql2 (0.5.3) nenv (0.3.0) - nio4r (2.5.3) + nio4r (2.5.4) nokogiri (1.10.10) mini_portile2 (~> 2.4.0) notiffany (0.1.3) @@ -267,8 +282,10 @@ GEM public_suffix (4.0.5) puma (4.3.5) nio4r (~> 2.0) - raabro (1.3.1) + raabro (1.4.0) rack (2.2.3) + rack-cors (1.1.1) + rack (>= 2.0.0) rack-protection (2.0.8.1) rack rack-test (1.1.0) @@ -313,7 +330,7 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) redcarpet (3.5.0) - redis (4.2.1) + redis (4.2.5) regexp_parser (1.7.1) responders (3.0.1) actionpack (>= 5.0) @@ -355,20 +372,16 @@ GEM ruby_http_client (~> 3.4) sexp_processor (4.15.0) shellany (0.0.1) - shoulda (3.5.0) - shoulda-context (~> 1.0, >= 1.0.1) - shoulda-matchers (>= 1.4.1, < 3.0) - shoulda-context (1.2.2) - shoulda-matchers (2.8.0) - activesupport (>= 3.0.0) - sidekiq (6.0.7) + shoulda (4.0.0) + shoulda-context (~> 2.0) + shoulda-matchers (~> 4.0) + shoulda-context (2.0.0) + shoulda-matchers (4.4.1) + activesupport (>= 4.2.0) + sidekiq (6.1.3) connection_pool (>= 2.2.2) rack (~> 2.0) - rack-protection (>= 2.0.0) - redis (>= 4.1.0) - sidekiq-cron (1.2.0) - fugit (~> 1.1) - sidekiq (>= 4.2.1) + redis (>= 4.2.0) simple_form (5.0.2) actionpack (>= 5.0) activemodel (>= 5.0) @@ -386,7 +399,7 @@ GEM sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.1) + sprockets-rails (3.2.2) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) @@ -402,7 +415,7 @@ GEM turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) - tzinfo (1.2.7) + tzinfo (1.2.9) thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) @@ -451,6 +464,7 @@ DEPENDENCIES factory_bot_rails file_validators font-awesome-rails (~> 4.0) + fullcalendar-rails groupdate guard guard-minitest @@ -461,11 +475,13 @@ DEPENDENCIES jquery-ui-rails listen (>= 3.0.5, < 3.2) minitest-reporters + momentjs-rails mustache (~> 1.0) mysql2 (>= 0.4.4, < 0.6.0) omniauth-mlh (~> 0.4.1) omniauth-rails_csrf_protection puma (~> 4.3) + rack-cors rails (~> 5.2.4.4) rails-controller-testing rails-settings-cached (~> 0.7.2) @@ -479,10 +495,10 @@ DEPENDENCIES selectize-rails selenium-webdriver sendgrid-actionmailer - shoulda (~> 3.5) - shoulda-matchers (~> 2.0) + shoulda (~> 4.0.0) + shoulda-matchers (~> 4.4.0) sidekiq (< 7) - sidekiq-cron (~> 1.1) + sidekiq-cron! simple_form simple_spark simplecov diff --git a/README.md b/README.md index 186e6619d..092f64d71 100644 --- a/README.md +++ b/README.md @@ -108,14 +108,14 @@ $ bin/rails s # short for bin/rails server 6. Visit http://localhost:3000/apply, create an account, and complete an application -7. In another bash window, promote your user to an admin +7. In another bash window, promote your user to a director ```bash $ cd hackathon-manager $ bin/rails c # short for bin/rails console # Wait for the console to start... Loading development environment (Rails 5.1.1) -irb(main):001:0> User.last.update_attribute(:role, :admin) +irb(main):001:0> User.last.update_attribute(:role, :director) ``` 8. Visit http://localhost:3000/manage and set up the hackathon as needed diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8a917f699..d12456d3d 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -21,3 +21,6 @@ //= require_directory . //= require_directory ./channels //= require ./vendor/simplemde.min.js +//= require moment +//= require fullcalendar +//= require fullcalendar/locale-all diff --git a/app/assets/javascripts/events.js b/app/assets/javascripts/events.js new file mode 100644 index 000000000..fbe50362b --- /dev/null +++ b/app/assets/javascripts/events.js @@ -0,0 +1,43 @@ +function eventCalendar() { + return $('#calendar').fullCalendar({ + defaultView: 'listYear', + buttonText: { + today: 'Today' + }, + eventRender: function (event, element, view) { + element.find('.fc-event-dot').css('display', 'none'); + if(event.description) { + element.find('.fc-list-item-title').append('
' + event.description + ''); + } + if (event.location) { + element.find('.fc-list-item-title').append('
Location: ' + event.location + ''); + } + if (event.category) { + element.find('.fc-list-item-title').append('
Category: ' + event.category + ''); + } + }, + events: { + url: '/manage/events.json', + success: function (response) { + // due to "end" being a keyword in ruby and what fullcalender uses it is stored as finish and than it is + // converted to "end" when sending it to fullcalendar + response = JSON.parse(JSON.stringify(response).split('"finish":').join('"end":')); + return response; + } + }, + eventClick: function (info) { + window.location = 'events/' + info.id; + }, + height: 'auto', + }); +} + +function clearCalendar() { + $('#calendar').fullCalendar('delete'); + $('#calendar').html(''); +} + +document.addEventListener('turbolinks:load', function () { + eventCalendar(); +}); +document.addEventListener('turbolinks:before-cache', clearCalendar); diff --git a/app/assets/javascripts/manage/lib/dashboardMap.js b/app/assets/javascripts/manage/lib/dashboardMap.js new file mode 100644 index 000000000..0debe2451 --- /dev/null +++ b/app/assets/javascripts/manage/lib/dashboardMap.js @@ -0,0 +1,13 @@ +document.addEventListener('turbolinks:load', function () { + $('.map-button').click(function (){ + var map = $('#map'); + if(map.is(':visible')){ + map.hide(); + $(this).html('Show Map'); + } + else{ + map.show(); + $(this).html('Hide Map'); + } + }); +}); diff --git a/app/assets/javascripts/manage/lib/setupDataTables.js b/app/assets/javascripts/manage/lib/setupDataTables.js index f9418bc3c..2403217db 100644 --- a/app/assets/javascripts/manage/lib/setupDataTables.js +++ b/app/assets/javascripts/manage/lib/setupDataTables.js @@ -124,7 +124,7 @@ var setupDataTables = function () { ] }); - $('.datatable.stats-mlhinfo-applied').DataTable({ + $('.datatable.stats-info-applied').DataTable({ order: [1, 'asc'], columns: [ { orderable: true, data: 'id', visible: false }, @@ -136,7 +136,7 @@ var setupDataTables = function () { ] }); - $('.datatable.stats-mlhinfo-checkedin').DataTable({ + $('.datatable.stats-info-checkedin').DataTable({ order: [1, 'asc'], columns: [ { orderable: true, data: 'id', visible: false }, diff --git a/app/assets/javascripts/validate.js b/app/assets/javascripts/validate.js index aa37eb8ab..d61bbcb79 100644 --- a/app/assets/javascripts/validate.js +++ b/app/assets/javascripts/validate.js @@ -18,8 +18,8 @@ document.addEventListener('turbolinks:load', function() { switch (types[i]) { case 'presence': if (!value || $.trim(value).length < 1) { - if ($(this).parents('.agreement_input')) { - notify('.agreement_input', 'Please read & accept'); + if ($(this).hasClass("agreement_input")) { + notify($(this).parent(), 'Please read & accept'); } else { notify(this, 'Missing Information'); } @@ -74,7 +74,9 @@ document.addEventListener('turbolinks:load', function() { .fadeOut(200, function() { $(this).remove(); }); - $(".agreement_input") + // this removes the notification for agreements + $(this) + .parent() .parent() .removeClass('field_with_errors') .find('.error') diff --git a/app/assets/stylesheets/forms/_forms.sass b/app/assets/stylesheets/forms/_forms.sass index 6d26169c0..819306334 100644 --- a/app/assets/stylesheets/forms/_forms.sass +++ b/app/assets/stylesheets/forms/_forms.sass @@ -73,6 +73,8 @@ hr @include css4 color: var(--grey) .error + display: inline-block + width: 100% @include css4 background: var(--input--error--background) color: var(--input--error--text) diff --git a/app/assets/stylesheets/manage.sass b/app/assets/stylesheets/manage.sass index 970171e2d..8e7464574 100644 --- a/app/assets/stylesheets/manage.sass +++ b/app/assets/stylesheets/manage.sass @@ -6,6 +6,7 @@ @import general/variables @import general/css4 @import manage/autocomplete +@import manage/events $grey-dark: #555 $grey-med: #999 @@ -15,6 +16,7 @@ $grey-med: #999 @import vendor/datatables.min @import selectize @import selectize.default +@import fullcalendar .icon-space-r margin-right: 0.5em @@ -57,6 +59,16 @@ $grey-med: #999 text-align: center overflow: auto width: 100% + @media (max-width: 768px) + display: none + +.map-button + display: none + @media (max-width: 768px) + display: block + +.narrow-icon + width: 40px .dashboard-container-title border-bottom: 3px solid #e5e5e5 @@ -69,21 +81,25 @@ $grey-med: #999 vertical-align: top position: absolute z-index: 1 - background: rgba(#ffffff,0.86) + background: rgba(#ffffff, 0.86) margin-left: 33px padding: 0 6% height: 136px + h3 color: $black font-weight: 500 margin: 1.2em 0 0 + p font-size: 14px margin: 0.5em 0 1.2em + &.double-metrics h3 margin-top: 0.45em font-size: 21px + p font-size: 13px margin-top: 0.3em @@ -92,6 +108,7 @@ $grey-med: #999 list-style: none margin: 0 0 10px padding: 0 + li margin: 0 padding: 2px @@ -111,7 +128,8 @@ $grey-med: #999 height: calc(100% + 20px) .editor-toolbar.fullscreen - z-index: 1031 // in front of the navbar + z-index: 1031 +// in front of the navbar /* * Mobile nav @@ -126,6 +144,7 @@ $grey-med: #999 .navbar-mobile-toggle--active background: #eeeeee + .fa color: black !important @@ -137,6 +156,10 @@ $grey-med: #999 height: calc(100vh - 2.25rem) overflow: scroll +.calendar-list + width: 100% + min-width: 500px + margin: auto /* * Datatable changes diff --git a/app/assets/stylesheets/manage/events.sass b/app/assets/stylesheets/manage/events.sass new file mode 100644 index 000000000..1d4ec926a --- /dev/null +++ b/app/assets/stylesheets/manage/events.sass @@ -0,0 +1,9 @@ +.fc-list-item + cursor: pointer !important + +.event_start select:first-child, .event_finish select:first-child + margin-left: 0 !important + +.event_start select:last-child, .event_finish select:last-child + margin-right: 0 !important + diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb new file mode 100644 index 000000000..30c5b00ba --- /dev/null +++ b/app/controllers/events_controller.rb @@ -0,0 +1,7 @@ +class EventsController < ApplicationController + respond_to :json + + def show + render json: Event.all + end +end diff --git a/app/controllers/manage/events_controller.rb b/app/controllers/manage/events_controller.rb new file mode 100644 index 000000000..cb89fc3e5 --- /dev/null +++ b/app/controllers/manage/events_controller.rb @@ -0,0 +1,54 @@ +class Manage::EventsController < Manage::ApplicationController + before_action :require_director + respond_to :html, :json + + def index + @start_date = HackathonConfig['event_start_date'] + respond_to do |format| + format.html + format.json { render json: Event.all } + end + end + + def new + @event = ::Event.new + end + + def create + @event = ::Event.new(event_params) + if @event.save + redirect_to(manage_events_path) + else + render('new') + end + end + + def show + @event = Event.find_by_id(params[:id]) + respond_with(:manage, @event) + end + + def update + @event = Event.find_by_id(params[:id]) + if @event.update(event_params) + redirect_to(manage_events_path) + else + render('show') + end + end + + def destroy + @event = Event.find_by_id(params[:id]) + if @event.destroy + redirect_to(manage_events_path) + else + render('show') + end + end + + def event_params + params.require(:event).permit( + :title, :description, :location, :category, :start, :finish + ) + end +end diff --git a/app/controllers/manage/stats_controller.rb b/app/controllers/manage/stats_controller.rb index 09deb7526..426423c24 100644 --- a/app/controllers/manage/stats_controller.rb +++ b/app/controllers/manage/stats_controller.rb @@ -15,11 +15,11 @@ def attendee_sponsor_info_datatable render json: AttendeeSponsorInfoDatatable.new(params, view_context: view_context) end - def mlh_applied_datatable - render json: MLHAppliedDatatable.new(params, view_context: view_context) + def applied_datatable + render json: AppliedDatatable.new(params, view_context: view_context) end - def mlh_checked_in_datatable - render json: MLHCheckedInDatatable.new(params, view_context: view_context) + def checked_in_datatable + render json: CheckedInDatatable.new(params, view_context: view_context) end end diff --git a/app/controllers/manage/users_controller.rb b/app/controllers/manage/users_controller.rb index e5d743166..6ef31a54f 100644 --- a/app/controllers/manage/users_controller.rb +++ b/app/controllers/manage/users_controller.rb @@ -1,6 +1,6 @@ class Manage::UsersController < Manage::ApplicationController before_action :require_director - before_action :find_user, only: [:show, :edit, :update, :destroy] + before_action :find_user, only: [:show, :edit, :update, :reset_password, :destroy] respond_to :html, :json @@ -16,6 +16,14 @@ def staff_datatable render json: StaffDatatable.new(params, view_context: view_context) end + def reset_password + new_password = Devise.friendly_token(50) + @user.reset_password(new_password, new_password) + @user.send_reset_password_instructions + flash[:notice] = t(:reset_password_success, scope: 'pages.manage.users.edit', full_name: @user.full_name) + respond_with(:manage, @user, location: manage_users_path) + end + def show respond_with(:manage, @user) end diff --git a/app/controllers/questionnaires_controller.rb b/app/controllers/questionnaires_controller.rb index f8520ca04..a1c5cf1f8 100644 --- a/app/controllers/questionnaires_controller.rb +++ b/app/controllers/questionnaires_controller.rb @@ -144,7 +144,7 @@ def questionnaire_params params.require(:questionnaire).permit( :email, :experience, :gender, :date_of_birth, :interest, :school_id, :school_name, :major, :level_of_study, - :shirt_size, :dietary_restrictions, :special_needs, :international, + :shirt_size, :dietary_restrictions, :special_needs, :international, :country, :portfolio_url, :vcs_url, :bus_captain_interest, :phone, :can_share_info, :travel_not_from_school, :travel_location, :graduation_year, :race_ethnicity, :resume, :delete_resume, :why_attend, agreement_ids: [] diff --git a/app/datatables/mlh_applied_datatable.rb b/app/datatables/applied_datatable.rb similarity index 94% rename from app/datatables/mlh_applied_datatable.rb rename to app/datatables/applied_datatable.rb index 7da2d25c4..9fb418866 100644 --- a/app/datatables/mlh_applied_datatable.rb +++ b/app/datatables/applied_datatable.rb @@ -1,4 +1,4 @@ -class MLHAppliedDatatable < ApplicationDatatable +class AppliedDatatable < ApplicationDatatable def_delegators :@view, :link_to, :manage_stats_path, :bold, :display_datetime def view_columns diff --git a/app/datatables/mlh_checked_in_datatable.rb b/app/datatables/checked_in_datatable.rb similarity index 94% rename from app/datatables/mlh_checked_in_datatable.rb rename to app/datatables/checked_in_datatable.rb index 7524b2c01..3aadb6c6c 100644 --- a/app/datatables/mlh_checked_in_datatable.rb +++ b/app/datatables/checked_in_datatable.rb @@ -1,4 +1,4 @@ -class MLHCheckedInDatatable < ApplicationDatatable +class CheckedInDatatable < ApplicationDatatable def_delegators :@view, :link_to, :manage_stats_path, :bold, :display_datetime def view_columns diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index ef1d03cf2..779e2c9cb 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -23,8 +23,7 @@ def incomplete_reminder_email(user_id) def rsvp_reminder_email(user_id) @user = User.find_by_id(user_id) - return if @user.blank? || !@user.questionnaire.acc_status == "accepted" || Time.now.in_time_zone.to_date > Date.parse(HackathonConfig["event_start_date"]).in_time_zone.to_date - + return if @user.blank? || @user.questionnaire.blank? || @user.questionnaire.acc_status != "accepted" || Time.now.in_time_zone.to_date > Date.parse(HackathonConfig["event_start_date"]).in_time_zone.to_date Message.queue_for_trigger("questionnaire.rsvp_reminder", @user.id) end end diff --git a/app/models/event.rb b/app/models/event.rb new file mode 100644 index 000000000..e5dde9abf --- /dev/null +++ b/app/models/event.rb @@ -0,0 +1,33 @@ +class Event < ApplicationRecord + validates_presence_of :title, :start + + validate :finish_before_start + + def finish_before_start + return if finish.nil? + unless finish > start + errors.add(:finish, 'time must be after start time') + end + end + + def description=(value) + if value.blank? + value = nil + end + super value + end + + def location=(value) + if value.blank? + value = nil + end + super value + end + + def category=(value) + if value.blank? + value = nil + end + super value + end +end diff --git a/app/models/questionnaire.rb b/app/models/questionnaire.rb index ad8c2f488..31b14dcb0 100644 --- a/app/models/questionnaire.rb +++ b/app/models/questionnaire.rb @@ -3,7 +3,6 @@ class Questionnaire < ApplicationRecord include ActiveModel::Dirty include DeletableAttachment - before_validation :consolidate_school_names before_validation :clean_for_non_rsvp before_validation :clean_negative_special_needs @@ -24,6 +23,7 @@ class Questionnaire < ApplicationRecord validates_presence_of :phone, :date_of_birth, :school_id, :experience, :shirt_size, :interest validates_presence_of :gender, :major, :level_of_study, :graduation_year, :race_ethnicity + validates :country, presence: { message: %[information is missing from application. Please update your Application] } DIETARY_SPECIAL_NEEDS_MAX_LENGTH = 500 validates_length_of :dietary_restrictions, maximum: DIETARY_SPECIAL_NEEDS_MAX_LENGTH @@ -121,6 +121,203 @@ class Questionnaire < ApplicationRecord "Other" ].freeze + POSSIBLE_COUNTRIES = [ + "United States", + "Afghanistan", + "Albania", + "Algeria", + "Andorra", + "Angola", + "Antigua and Barbuda", + "Argentina", + "Armenia", + "Australia", + "Austria", + "Azerbaijan", + "Bahamas", + "Bahrain", + "Bangladesh", + "Barbados", + "Belarus", + "Belgium", + "Belize", + "Benin", + "Bhutan", + "Bolivia", + "Bosnia and Herzegovina", + "Botswana", + "Brazil", + "Brunei", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cambodia", + "Cameroon", + "Canada", + "Cape Verde", + "Central African Republic", + "Chad", + "Chile", + "China", + "Colombia", + "Comoros", + "Congo, Democratic Republic of the Congo", + "Congo, Republic of the", + "Costa Rica", + "Cote d'Ivoire (Ivory Coast)", + "Croatia", + "Cuba", + "Cyprus", + "Czech Republic", + "Denmark", + "Djibouti", + "Dominica", + "Dominican Republic", + "Timor-Leste (East Timor)", + "Ecuador", + "Egypt", + "El Salvador", + "Equatorial Guinea", + "Eritrea", + "Estonia", + "Ethiopia", + "Fiji", + "Finland", + "France", + "Gabon", + "Gambia, The", + "Georgia", + "Germany", + "Ghana", + "Greece", + "Grenada", + "Guatemala", + "Guinea", + "Guinea-Bissau", + "Guyana", + "Haiti", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Iraq", + "Ireland", + "Israel", + "Italy", + "Jamaica", + "Japan", + "Jordan", + "Kazakhstan", + "Kenya", + "Kiribati", + "Korea, North", + "Korea, South", + "Kuwait", + "Kyrgyzstan", + "Laos", + "Latvia", + "Lebanon", + "Lesotho", + "Liberia", + "Libya", + "Liechtenstein", + "Lithuania", + "Luxembourg", + "Macedonia, North", + "Madagascar", + "Malawi", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Marshall Islands", + "Mauritania", + "Mauritius", + "Mexico", + "Micronesia", + "Moldova", + "Monaco", + "Mongolia", + "Morocco", + "Mozambique", + "Myanmar", + "Namibia", + "Nauru", + "Nepal", + "Netherlands", + "New Zealand", + "Nicaragua", + "Niger", + "Nigeria", + "Norway", + "Oman", + "Pakistan", + "Palau", + "Panama", + "Papua New Guinea", + "Paraguay", + "Peru", + "Philippines", + "Poland", + "Portugal", + "Qatar", + "Romania", + "Russia", + "Rwanda", + "Saint Kitts and Nevis", + "Saint Lucia", + "Saint Vincent", + "Samoa", + "San Marino", + "Sao Tome and Principe", + "Saudi Arabia", + "Senegal", + "Serbia and Montenegro", + "Seychelles", + "Sierra Leone", + "Singapore", + "Slovakia", + "Slovenia", + "Solomon Islands", + "Somalia", + "South Africa", + "South Sudan", + "Spain", + "Sri Lanka", + "Sudan", + "Suriname", + "Swaziland", + "Sweden", + "Switzerland", + "Syria", + "Taiwan", + "Tajikistan", + "Tanzania", + "Thailand", + "Togo", + "Tonga", + "Trinidad and Tobago", + "Tunisia", + "Turkey", + "Turkmenistan", + "Tuvalu", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "Uruguay", + "Uzbekistan", + "Vanuatu", + "Vatican City", + "Venezuela", + "Vietnam", + "Yemen", + "Zambia", + "Zimbabwe" + ].freeze + validates_inclusion_of :experience, in: POSSIBLE_EXPERIENCES validates_inclusion_of :interest, in: POSSIBLE_INTERESTS # validates_inclusion_of :school_id, :in => School.select(:id) diff --git a/app/views/application/_missing_country_notice.html.haml b/app/views/application/_missing_country_notice.html.haml new file mode 100644 index 000000000..c2c4f87b1 --- /dev/null +++ b/app/views/application/_missing_country_notice.html.haml @@ -0,0 +1,10 @@ +.alert#disclaimer + %h1.section-title + %span.emphasized.fa.fa-exclamation-circle + Missing + %span.emphasized Country Information + %p + You have not provided the country you are hacking from. Please + %strong + #{link_to "edit your application", edit_questionnaires_path} + and specify your country. diff --git a/app/views/application/_questionnaire_summary.html.haml b/app/views/application/_questionnaire_summary.html.haml index 8f3ae4b7b..818bd7fe0 100644 --- a/app/views/application/_questionnaire_summary.html.haml +++ b/app/views/application/_questionnaire_summary.html.haml @@ -7,10 +7,10 @@ = Questionnaire::POSSIBLE_EXPERIENCES[@questionnaire.experience] %p %b Portfolio link: - = @questionnaire.portfolio_url? ? link_to(@questionnaire.portfolio_url, @questionnaire.portfolio_url, target: '_blank') : 'Not provided' + = @questionnaire.portfolio_url? ? link_to(@questionnaire.portfolio_url, @questionnaire.portfolio_url, target: '_blank', rel: 'noopener') : 'Not provided' %p %b GitHub/GitLab/Bitbucket link: - = @questionnaire.vcs_url? ? link_to(@questionnaire.vcs_url, @questionnaire.vcs_url, target: '_blank') : 'Not provided' + = @questionnaire.vcs_url? ? link_to(@questionnaire.vcs_url, @questionnaire.vcs_url, target: '_blank', rel: 'noopener') : 'Not provided' %p %b Resume: = @questionnaire.resume.attached? ? link_to("Download »".html_safe, @questionnaire.resume) : 'Not provided' @@ -21,9 +21,10 @@ %p %b Shirt size: = @questionnaire.shirt_size -%p - %b Dietary restrictions - = @questionnaire.dietary_restrictions || "(none)".html_safe +- if !HackathonConfig['digital_hackathon'] + %p + %b Dietary restrictions + = @questionnaire.dietary_restrictions || "(none)".html_safe %p %b Special needs = @questionnaire.special_needs || "(none)".html_safe diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index b19583627..7d6375be4 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -1,8 +1,9 @@ - title 'Register' .form-container.signup %h1.section-title.center - Register for - .emphasized.text-overflow-center= HackathonConfig['name'] + Create an + .emphasized.text-overflow-center + Account - if !HackathonConfig['accepting_questionnaires'] || HackathonConfig['disclaimer_message'].present? #disclaimer diff --git a/app/views/layouts/manage/application.html.haml b/app/views/layouts/manage/application.html.haml index 763ca84a0..eb646c60f 100644 --- a/app/views/layouts/manage/application.html.haml +++ b/app/views/layouts/manage/application.html.haml @@ -18,7 +18,7 @@ .fa.fa-bars.m-3.mx-4.text-white.h-auto / TODO: move acc/signin inside the sidebar - %a.navbar-brand.px-3.mr-0{href: manage_root_path} #{HackathonConfig['name']} Manager + %a.navbar-brand.px-3.mr-0.col-xl-2.col-lg-3{href: manage_root_path} #{HackathonConfig['name']} Manager / %input.form-control.form-control-dark.w-100{"aria-label" => "Search", placeholder: "Search", type: "text"}/ %ul.navbar-nav.ml-auto.px-3 @@ -29,7 +29,7 @@ .container-fluid .row - %nav.col-md-3.d-none.d-md-block.bg-light.sidebar + %nav.col-md-3.d-none.d-md-block.bg-light.sidebar.col-xl-2.col-lg-3 .sidebar-sticky %h6.sidebar-heading.d-flex.justify-content-between.align-items-center.px-3.mt-4.mb-1.text-muted %span @@ -68,6 +68,10 @@ = active_link_to manage_checkins_path, class: "nav-link" do .fa.fa-drivers-license-o.fa-fw.icon-space-r-half = t(:title, scope: 'pages.manage.check-in') + %li.nav-item + = active_link_to manage_events_path, class: "nav-link" do + .fa.fa-calendar.fa-fw.icon-space-r-half + = t(:title, scope: 'pages.manage.events') %li.nav-item = active_link_to manage_stats_path, class: "nav-link" do .fa.fa-table.fa-fw.icon-space-r-half @@ -120,14 +124,14 @@ .nav-item-description = t(:doorkeeper, scope: 'layouts.manage.navigation.descriptors') %li.nav-item - = active_link_to sidekiq_web_path, target: '_blank', class: "nav-link" do + = active_link_to sidekiq_web_path, target: '_blank', rel: "noopener", class: "nav-link" do .fa.fa-tasks.fa-fw.icon-space-r-half = t(:title, scope: 'pages.manage.sidekiq') %span.fa.fa-external-link.icon-space-l-half .nav-item-description = t(:sidekiq, scope: 'layouts.manage.navigation.descriptors') %li.nav-item - = active_link_to blazer_path, target: '_blank', class: "nav-link" do + = active_link_to blazer_path, target: '_blank', rel: "noopener", class: "nav-link" do .fa.fa-terminal.fa-fw.icon-space-r-half = t(:title, scope: 'pages.manage.blazer') %span.fa.fa-external-link.icon-space-l-half @@ -139,7 +143,7 @@ = t(:title, scope: 'pages.manage.data-exports') .nav-item-description = t(:dataexports, scope: 'layouts.manage.navigation.descriptors') - %main.col-md-9.ml-sm-auto.px-4{role: "main"} + %main.col-md-9.ml-sm-auto.px-4.col-xl-10.col-lg-9{role: "main"} = render "layouts/manage/flashes" = yield %div.mb-4.text-center diff --git a/app/views/manage/agreements/index.html.haml b/app/views/manage/agreements/index.html.haml index be0c560ac..f208a571b 100644 --- a/app/views/manage/agreements/index.html.haml +++ b/app/views/manage/agreements/index.html.haml @@ -17,7 +17,7 @@ %tbody - @agreements.each do |agreement| %tr - %td + %td.narrow-icon = link_to ''.html_safe, edit_manage_agreement_path(agreement) %td = link_to ''.html_safe, manage_agreement_path(agreement), method: :delete, data: { confirm: "Are you sure? The agreement will be permanently deleted. This action is irreversible." } diff --git a/app/views/manage/dashboard/index.html.haml b/app/views/manage/dashboard/index.html.haml index c0642f5e3..615ca596e 100644 --- a/app/views/manage/dashboard/index.html.haml +++ b/app/views/manage/dashboard/index.html.haml @@ -11,6 +11,8 @@ = render "layouts/manage/page_title", title: t(:title, scope: 'pages.manage.dashboard') +%button.btn.btn-secondary.w-100.map-button Show Map + .row .col #map diff --git a/app/views/manage/data_exports/_form.html.haml b/app/views/manage/data_exports/_form.html.haml index 015ad70d3..6d108063f 100644 --- a/app/views/manage/data_exports/_form.html.haml +++ b/app/views/manage/data_exports/_form.html.haml @@ -1,11 +1,9 @@ .form-container = bs_horizontal_simple_form_for @data_export, url: url_for(action: @data_export.new_record? ? "create" : "update", controller: "data_exports") do |f| = f.error_notification - - .form-inputs - = f.input :export_type, as: :select, collection: DataExport::POSSIBLE_TYPES.map { |x| [x.titleize, x] }, include_blank: false - - .form-actions.mb-3.mt-3 - = f.button :submit, class: 'btn-primary' + .form-inputs + = f.input :export_type, as: :select, collection: DataExport::POSSIBLE_TYPES.map { |x| [x.titleize, x] }, include_blank: false + .form-actions.mb-3.mt-3 + = f.button :submit, class: 'btn-primary' .mb-4 diff --git a/app/views/manage/events/_form.html.haml b/app/views/manage/events/_form.html.haml new file mode 100644 index 000000000..10c8dd6bb --- /dev/null +++ b/app/views/manage/events/_form.html.haml @@ -0,0 +1,12 @@ += bs_horizontal_simple_form_for @event, url: url_for(action: @event.new_record? ? "create" : "update", controller: "events") do |f| + = f.error_notification + .form-inputs + = f.input :title + = f.input :description + = f.input :location + = f.input :category + = f.input :start + = f.input :finish, include_blank: true + .center + //TODO figure out why you need to add save and it doesn't work automatically like other forms + = f.button :submit, 'Save', value: ( @event.new_record? ? 'Create' : 'Save' ), class: 'btn-primary' diff --git a/app/views/manage/events/index.html.haml b/app/views/manage/events/index.html.haml new file mode 100644 index 000000000..ec20c6204 --- /dev/null +++ b/app/views/manage/events/index.html.haml @@ -0,0 +1,4 @@ += render "layouts/manage/page_title", title: t(:title, scope: 'pages.manage.events') do + = link_to "Add Event", new_manage_event_path, class: "btn btn-sm btn-outline-secondary" + +.calendar-list#calendar diff --git a/app/views/manage/events/new.html.haml b/app/views/manage/events/new.html.haml new file mode 100644 index 000000000..c195ba28c --- /dev/null +++ b/app/views/manage/events/new.html.haml @@ -0,0 +1,4 @@ += render "layouts/manage/page_title", title: "New Event" + +.form-container + = render "form" diff --git a/app/views/manage/events/show.html.haml b/app/views/manage/events/show.html.haml new file mode 100644 index 000000000..ece45ff02 --- /dev/null +++ b/app/views/manage/events/show.html.haml @@ -0,0 +1,6 @@ += render "layouts/manage/page_title", title: "Edit Event", subtitle: @event.title do + .btn-group + = link_to 'Cancel', manage_events_path, class: 'btn btn-sm btn-outline-secondary' + = link_to 'Delete', manage_event_path(@event), method: :delete, data: { confirm: 'Are you sure? This action is irreversible.' }, class: 'btn btn-sm btn-outline-secondary' + += render "form" diff --git a/app/views/manage/messages/_templating.haml b/app/views/manage/messages/_templating.haml index 6a3b1c0ad..0ba432173 100644 --- a/app/views/manage/messages/_templating.haml +++ b/app/views/manage/messages/_templating.haml @@ -2,7 +2,7 @@ %p Message bodies can make use of template variables to help personalize and streamline emails. - Templating is powered by mustache. + Templating is powered by mustache. %table.table.table-striped %thead diff --git a/app/views/manage/messages/template.haml b/app/views/manage/messages/template.haml index dd7de592c..517c48943 100644 --- a/app/views/manage/messages/template.haml +++ b/app/views/manage/messages/template.haml @@ -22,7 +22,7 @@ .form-inputs %h5.mb-3 Customize template %p.text-secondary Must save to update preview. CSS will be converted to inline styles when messages are sent. - %p.text-secondary Be sure to test with as many email providers as possible! Litmus PutsMail is one great resource. + %p.text-secondary Be sure to test with as many email providers as possible! Litmus PutsMail is one great resource. = f.input :html, input_html: { 'data-code-mirror-textarea' => '1' }, label: false, wrapper: :bootstrap_inline_form .form-actions.mt-3.mb-3 diff --git a/app/views/manage/questionnaires/_form.html.haml b/app/views/manage/questionnaires/_form.html.haml index 34e2bfed3..498e9bbee 100644 --- a/app/views/manage/questionnaires/_form.html.haml +++ b/app/views/manage/questionnaires/_form.html.haml @@ -22,6 +22,7 @@ = f.input :level_of_study, input_html: { "data-validate" => "presence" } = f.input :major, input_html: { "data-validate" => "presence" } = f.input :gender, input_html: { "data-validate" => "presence" } + = f.input :country, as: :select, collection: Questionnaire::POSSIBLE_COUNTRIES, include_blank: "(select one...)", input_html: { "data-validate" => "presence" } - if !HackathonConfig['digital_hackathon'] .card.mb-4 diff --git a/app/views/manage/questionnaires/_overview.html.haml b/app/views/manage/questionnaires/_overview.html.haml index ffdfecdd9..b6ebc3a22 100644 --- a/app/views/manage/questionnaires/_overview.html.haml +++ b/app/views/manage/questionnaires/_overview.html.haml @@ -23,6 +23,8 @@ = @questionnaire.age_at_time_of_event / 1.year %dt.col-md-4 Gender %dd.col-md-8= @questionnaire.gender + %dt.col-md-4 Country + %dd.col-md-8= @questionnaire.country = render 'checkin_compliance_card' @@ -33,13 +35,14 @@ .row %dt.col-md-4 Shirt size %dd.col-md-8= @questionnaire.shirt_size - %dt.col-md-4 Dietary restrictions - %dd.col-md-8 - - if @questionnaire.dietary_restrictions.present? - %span.fa.fa-exclamation-triangle.text-warning.icon-space-r-half - = @questionnaire.dietary_restrictions - - else - %span.text-muted (none) + - if !HackathonConfig['digital_hackathon'] + %dt.col-md-4 Dietary restrictions + %dd.col-md-8 + - if @questionnaire.dietary_restrictions.present? + %span.fa.fa-exclamation-triangle.text-warning.icon-space-r-half + = @questionnaire.dietary_restrictions + - else + %span.text-muted (none) %dt.col-md-4 Special needs %dd.col-md-8 - if @questionnaire.special_needs.present? @@ -86,10 +89,10 @@ = Questionnaire::POSSIBLE_EXPERIENCES[@questionnaire.experience] %dt.col-md-4 Portfolio %dd.col-md-8 - = @questionnaire.portfolio_url? ? link_to(@questionnaire.portfolio_url, @questionnaire.portfolio_url, target: '_blank') : not_provided + = @questionnaire.portfolio_url? ? link_to(@questionnaire.portfolio_url, @questionnaire.portfolio_url, target: '_blank', rel: 'noopener') : not_provided %dt.col-md-4 GitHub/GitLab/Bitbucket %dd.col-md-8 - = @questionnaire.vcs_url? ? link_to(@questionnaire.vcs_url, @questionnaire.vcs_url, target: '_blank') : not_provided + = @questionnaire.vcs_url? ? link_to(@questionnaire.vcs_url, @questionnaire.vcs_url, target: '_blank', rel: 'noopener') : not_provided %dt.col-md-4 Resume %dd.col-md-8 = @questionnaire.resume.attached? ? link_to("Download »".html_safe, @questionnaire.resume) : not_provided diff --git a/app/views/manage/schools/show.html.haml b/app/views/manage/schools/show.html.haml index 9a08d1bc5..41bcf4849 100644 --- a/app/views/manage/schools/show.html.haml +++ b/app/views/manage/schools/show.html.haml @@ -25,7 +25,7 @@ = @school.name %br %small - = link_to google_maps_link(@school.name), target: '_blank' do + = link_to google_maps_link(@school.name), target: '_blank', rel: 'noopener' do Search in Google Maps %span.fa.fa-external-link.icon-space-l-half %dt.col-md-4 Address @@ -37,7 +37,7 @@ %br %small - link = google_maps_link(@school.address, @school.city, @school.state) - = link_to link, target: '_blank' do + = link_to link, target: '_blank', rel: 'noopener' do Search in Google Maps %span.fa.fa-external-link.icon-space-l-half %dt.col-md-4 Home school diff --git a/app/views/manage/stats/index.html.haml b/app/views/manage/stats/index.html.haml index 7451827a2..22ae4ab48 100644 --- a/app/views/manage/stats/index.html.haml +++ b/app/views/manage/stats/index.html.haml @@ -2,79 +2,77 @@ .row .col - %h5.dashboard-container-title Dietary Restrictions or Special Needs + %h5.dashboard-container-title= t(:dietary_restrictions_special_needs, scope: 'pages.manage.stats') %table.datatable.stats-dietary.table.table-striped.table-hover{ "data-source" => dietary_restrictions_special_needs_datatable_manage_stats_path(format: :json) } %thead %tr - %th ID - %th First Name - %th Last Name - %th Email - %th Phone - %th Questionnaire - %th Checked In - %th Dietary Restrictions - %th Special Needs + %th= t(:id, scope: 'pages.manage.stats') + %th= t(:first_name, scope: 'pages.manage.stats') + %th= t(:last_name, scope: 'pages.manage.stats') + %th= t(:email, scope: 'pages.manage.stats') + %th= t(:phone, scope: 'pages.manage.stats') + %th= t(:questionnaire, scope: 'pages.manage.stats') + %th= t(:checked_in, scope: 'pages.manage.stats') + %th= t(:dietary_restrictions, scope: 'pages.manage.stats') + %th= t(:special_needs, scope: 'pages.manage.stats') %tbody - if !HackathonConfig['digital_hackathon'] .row .col - %h5.dashboard-container-title Attendees Not Traveling From Their School + %h5.dashboard-container-title= t(:attendees_not_traveling_from_their_school, scope: 'pages.manage.stats') %table.datatable.stats-notschooltravel.table.table-striped.table-hover{ "data-source" => alt_travel_datatable_manage_stats_path(format: :json) } %thead %tr - %th ID - %th First Name - %th Last Name - %th Email - %th Phone - %th Questionnaire - %th Traveling From - %th Questionnaire Status + %th= t(:id, scope: 'pages.manage.stats') + %th= t(:first_name, scope: 'pages.manage.stats') + %th= t(:last_name, scope: 'pages.manage.stats') + %th= t(:email, scope: 'pages.manage.stats') + %th= t(:phone, scope: 'pages.manage.stats') + %th= t(:questionnaire, scope: 'pages.manage.stats') + %th= t(:traveling_from, scope: 'pages.manage.stats') + %th= t(:questionnaire_status, scope: 'pages.manage.stats') %tbody .row .col - %h5.dashboard-container-title Attendee Info for Sponsors + %h5.dashboard-container-title= t(:attendee_info_for_sponsors, scope: 'pages.manage.stats') %table.datatable.stats-attendeeinfo.table.table-striped.table-hover{ "data-source" => attendee_sponsor_info_datatable_manage_stats_path(format: :json) } %thead %tr - %th ID - %th First name - %th Last name - %th Email - %th School - %th Open Source Link - %th Portfolio Link + %th= t(:id, scope: 'pages.manage.stats') + %th= t(:first_name, scope: 'pages.manage.stats') + %th= t(:last_name, scope: 'pages.manage.stats') + %th= t(:email, scope: 'pages.manage.stats') + %th= t(:school, scope: 'pages.manage.stats') + %th= t(:open_source_link, scope: 'pages.manage.stats') + %th= t(:portfolio_link, scope: 'pages.manage.stats') %tbody .row .col - %h5.dashboard-container-title MLH Info (applied) - %table.datatable.stats-mlhinfo-applied.table.table-striped.table-hover{ "data-source" => mlh_applied_datatable_manage_stats_path(format: :json) } + %h5.dashboard-container-title= t(:applied_attendees, scope: 'pages.manage.stats') + %table.datatable.stats-info-applied.table.table-striped.table-hover{ "data-source" => applied_datatable_manage_stats_path(format: :json) } %thead %tr - %th ID - %th First name - %th Last name - %th Email - %th Phone - %th School Name + %th= t(:id, scope: 'pages.manage.stats') + %th= t(:first_name, scope: 'pages.manage.stats') + %th= t(:last_name, scope: 'pages.manage.stats') + %th= t(:email, scope: 'pages.manage.stats') + %th= t(:phone, scope: 'pages.manage.stats') + %th= t(:school, scope: 'pages.manage.stats') %tbody .row .col - %h5.dashboard-container-title MLH Info (checked in) - %table.datatable.stats-mlhinfo-checkedin.table.table-striped.table-hover{ "data-source" => mlh_checked_in_datatable_manage_stats_path(format: :json) } + %h5.dashboard-container-title= t(:checked_in_attendees, scope: 'pages.manage.stats') + %table.datatable.stats-info-checkedin.table.table-striped.table-hover{ "data-source" => checked_in_datatable_manage_stats_path(format: :json) } %thead %tr - %th ID - %th First name - %th Last name - %th Email - %th Phone - %th School Name + %th= t(:id, scope: 'pages.manage.stats') + %th= t(:first_name, scope: 'pages.manage.stats') + %th= t(:last_name, scope: 'pages.manage.stats') + %th= t(:email, scope: 'pages.manage.stats') + %th= t(:phone, scope: 'pages.manage.stats') + %th= t(:school, scope: 'pages.manage.stats') %tbody - -.mb-4 diff --git a/app/views/manage/users/_form.html.haml b/app/views/manage/users/_form.html.haml index ca1af575d..6f16a467c 100644 --- a/app/views/manage/users/_form.html.haml +++ b/app/views/manage/users/_form.html.haml @@ -6,6 +6,8 @@ = f.error_notification .form-inputs + = f.input :first_name, required: true + = f.input :last_name, required: true = f.input :email, input_html: { "data-validate" => "presence" }, required: true = f.input :role, collection: User.roles.to_a.collect{|c| [c[0].titleize, c[0]]}, include_blank: false = f.input :is_active, collection: [[t(:active, scope: "pages.manage.users.edit.form"), true], [t(:inactive, scope: "pages.manage.users.edit.form"), false]], as: :radio_buttons diff --git a/app/views/manage/users/edit.html.haml b/app/views/manage/users/edit.html.haml index f2f911fa0..7a7334b68 100644 --- a/app/views/manage/users/edit.html.haml +++ b/app/views/manage/users/edit.html.haml @@ -1,6 +1,8 @@ -= render "layouts/manage/page_title", title: t(:title, scope: "pages.manage.users.edit", user_email: @user.email), subtitle: @user.email do += render "layouts/manage/page_title", title: @user.full_name, subtitle: t(:subtitle, scope: 'pages.manage.users.edit') do + .btn-group = link_to t(:cancel, scope: "pages.manage.users.edit"), manage_user_path(@user), class: 'btn btn-sm btn-outline-secondary' + = link_to t(:reset_password, scope: "pages.manage.users.edit"), reset_password_manage_user_path(@user), method: :patch, data: { confirm: t(:confirm_reset_password, scope: "pages.manage.users.edit", full_name: @user.full_name, first_name: @user.first_name)}, class: 'btn btn-sm btn-outline-secondary' = link_to t(:delete, scope: "pages.manage.users.edit"), manage_user_path(@user), method: :delete, data: { confirm: "Are you sure? #{@user.email} will be permanently deleted. This action is irreversible." }, class: 'btn btn-sm btn-outline-secondary' = render 'form' diff --git a/app/views/manage/users/show.html.haml b/app/views/manage/users/show.html.haml index 7b3387e32..1ee34f32d 100644 --- a/app/views/manage/users/show.html.haml +++ b/app/views/manage/users/show.html.haml @@ -1,4 +1,5 @@ -= render "layouts/manage/page_title", title: t(:title, scope: "pages.manage.users.show", user_email: @user.email) do += render "layouts/manage/page_title", title: @user.full_name, subtitle: t(:subtitle, scope: 'pages.manage.users.show') do + .btn-group = link_to t(:edit, scope: "pages.manage.users.show"), edit_manage_user_path(@user), class: 'btn btn-sm btn-outline-secondary' = link_to t(:delete, scope: "pages.manage.users.show"), manage_user_path(@user), method: :delete, data: { confirm: "Are you sure? #{@user.email} along with their questionnaire will be permanently deleted. This action is irreversible." }, class: 'btn btn-sm btn-outline-secondary' @@ -8,6 +9,12 @@ .card.mb-3 .card-header= t(:user_information, scope: "pages.manage.users.show") .card-body + .row + %dt.col-md-5= t(:first_name, scope: "pages.manage.users.show") + %dd.col-md-7= @user.first_name + .row + %dt.col-md-5= t(:last_name, scope: "pages.manage.users.show") + %dd.col-md-7= @user.last_name .row %dt.col-md-5= t(:email_address, scope: "pages.manage.users.show") %dd.col-md-7= @user.email diff --git a/app/views/questionnaires/_form.html.haml b/app/views/questionnaires/_form.html.haml index ba8a35948..83b270151 100644 --- a/app/views/questionnaires/_form.html.haml +++ b/app/views/questionnaires/_form.html.haml @@ -30,6 +30,7 @@ = f.input :graduation_year, as: :select, collection: Questionnaire::POSSIBLE_GRAD_YEARS, include_blank: "(select one...)", label: "Graduation year", input_html: { "data-validate" => "presence" }, wrapper_html: { class: 'input--half' } = f.input :race_ethnicity, as: :select, collection: Questionnaire::POSSIBLE_RACE_ETHNICITIES, include_blank: "(select one...)", label: "Race/Ethnicity", input_html: { "data-validate" => "presence" }, wrapper_html: { class: 'input--half' } + = f.input :country, as: :select, collection: Questionnaire::POSSIBLE_COUNTRIES, include_blank: "(select one...)", input_html: { "data-validate" => "presence" } = f.input :portfolio_url, label: "Portfolio link", placeholder: "http://mywebsite.com" = f.input :vcs_url, label: "GitHub/GitLab/Bitbucket link", placeholder: "https://github.com/coderit" @@ -44,7 +45,7 @@ = f.input :shirt_size, as: :select, collection: Questionnaire::POSSIBLE_SHIRT_SIZES, include_blank: "(select one...)", input_html: { "data-validate" => "presence" } - if !HackathonConfig['digital_hackathon'] - = f.input :dietary_restrictions, as: :text, label: "Health restrictions", wrapper_html: { class: 'input--half' }, maxlength: Questionnaire::DIETARY_SPECIAL_NEEDS_MAX_LENGTH + = f.input :dietary_restrictions, as: :text, label: "Dietary restrictions", wrapper_html: { class: 'input--half' }, maxlength: Questionnaire::DIETARY_SPECIAL_NEEDS_MAX_LENGTH = f.input :special_needs, as: :text, label: "Special needs", wrapper_html: { class: 'input--half' }, maxlength: Questionnaire::DIETARY_SPECIAL_NEEDS_MAX_LENGTH - if @agreements.any? @@ -52,8 +53,7 @@ %strong Agreements %p Please review the agreements and click the corresponding checkbox next to each agreement to agree. .form-inputs - .agreement_input - = f.association :agreements, as: :check_boxes, label_method: :formatted_agreement, value_method: :id, label: "", wrapper_html: { style: 'display: block' }, input_html: { "data-validate" => "presence" } + = f.association :agreements, as: :check_boxes, label_method: :formatted_agreement, value_method: :id, label: "", wrapper_html: { style: 'display: block' }, input_html: { "data-validate" => "presence", class: "agreement_input"} .right %button.button{ type: "button", "data-wizard" => "previous" } Previous diff --git a/app/views/questionnaires/show.html.haml b/app/views/questionnaires/show.html.haml index fe10da604..7dc357946 100644 --- a/app/views/questionnaires/show.html.haml +++ b/app/views/questionnaires/show.html.haml @@ -2,6 +2,8 @@ .form-container - if @questionnaire.unaccepted_agreements.any? = render partial: 'unaccepted_agreements_notice' + - if @questionnaire.country.blank? + = render partial: 'missing_country_notice' .form-container #disclaimer %h1.section-title diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb new file mode 100644 index 000000000..054f8b2b3 --- /dev/null +++ b/config/initializers/cors.rb @@ -0,0 +1,6 @@ +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' + resource '*/events.json', headers: :any, methods: [:get] # Workaround for now + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index b806ca6b6..ed11daa8b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -47,7 +47,11 @@ en: If many people have already RSVP'd, consider sending a message out to this bus list asking for interest as well. notes: Notes are shared with applicants. Supports Markdown and HTML. user: - role: Limited access prevents the admin from adding, modifying, or deleting any records; modifications through the check-in process are allowed. Event tracking limits to only event tracking. + role: | + Users can only access and edit their own information
+ Volunteers can only access the check-in process
+ Organizers can not modify, add, or delete any records but can view questionnaires and other records
+ Directors do not have any restrictions and can add, edit, or delete any record or setting
is_active: Deactivating a user will prevent them from logging in. Their access will be immediately revoked from the admin and application pages. receive_weekly_report: A weekly email report on admissions, bus lists, and messages. Only sent when there are weekly updates up until 7 days past the event. Disabled for inactive staff members. message: @@ -132,8 +136,29 @@ en: title: Check-in stats: title: Statistics + id: ID + first_name: First Name + last_name: Last Name + email: Email + phone: Phone + school: School + checked_in: Checked-in + traveling_from: Traveling From + questionnaire: Questionnaire + questionnaire_status: Questionnaire Status + dietary_restrictions: Dietary Restrictions + special_needs: Special Needs + open_source_link: Open Source Link + portfolio_link: Portfolio Link + dietary_restrictions_special_needs: Dietary Restrictions or Special Needs + attendes_not_traveling_from_their_school: Attendees Not Traveling From Their School + attendee_info_for_sponsors: Attendee Info for Sponsors + applied_attendees: Applied Attendees + checked_in_attendees: Checked-in Attendees trackable-tags: title: Trackable Tags + events: + title: Schedule bus-lists: title: Bus Lists schools: @@ -156,11 +181,13 @@ en: previous_signed_in_ip: Previous signed-in IP sign_in_count: Sign-in count show: - title: "%{user_email}" + subtitle: "User" edit: Edit reset_password: Reset Password delete: Delete user_information: User Information + first_name: First name + last_name: Last name email_address: Email address role: Role login_access: Login access @@ -178,10 +205,12 @@ en: sign_in_count: Sign-in count change_history: Change History edit: - title: Edit User - subtitle: "%{user_email}" + subtitle: Edit User cancel: Cancel delete: Delete + reset_password: Reset Password + confirm_reset_password: "Are you sure? %{full_name}'s password will be reset and %{first_name} will receive a recovery email." + reset_password_success: "Successfully reset password and sent recovery instructions to %{full_name}." form: active: Active inactive: Inactive diff --git a/config/routes.rb b/config/routes.rb index 55820e411..b19f3d108 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,6 +40,8 @@ patch :boarded_bus, on: :collection end + resource :events, only: :show, constraints: ->(req) { req.format == :json } + namespace :manage do authenticate :user, ->(u) { u.director? } do root to: "dashboard#index" @@ -72,6 +74,8 @@ resources :checkins do post :datatable, on: :collection end + resources :events do + end resources :messages do get :preview, on: :member get :live_preview, on: :collection @@ -97,8 +101,8 @@ post :dietary_restrictions_special_needs_datatable, on: :collection post :alt_travel_datatable, on: :collection post :attendee_sponsor_info_datatable, on: :collection - post :mlh_applied_datatable, on: :collection - post :mlh_checked_in_datatable, on: :collection + post :applied_datatable, on: :collection + post :checked_in_datatable, on: :collection end resources :users do post :user_datatable, on: :collection diff --git a/db/migrate/20200529142141_create_events.rb b/db/migrate/20200529142141_create_events.rb new file mode 100644 index 000000000..b9c06debc --- /dev/null +++ b/db/migrate/20200529142141_create_events.rb @@ -0,0 +1,13 @@ +class CreateEvents < ActiveRecord::Migration[5.2] + def change + create_table :events do |t| + t.string :title + t.string :description + t.string :location + t.datetime :start + t.datetime :finish + + t.timestamps + end + end +end diff --git a/db/migrate/20210128025749_add_category_to_events.rb b/db/migrate/20210128025749_add_category_to_events.rb new file mode 100644 index 000000000..46d18805b --- /dev/null +++ b/db/migrate/20210128025749_add_category_to_events.rb @@ -0,0 +1,5 @@ +class AddCategoryToEvents < ActiveRecord::Migration[5.2] + def change + add_column :events, :category, :string + end +end diff --git a/db/migrate/20210130175752_add_country_to_questionnaires.rb b/db/migrate/20210130175752_add_country_to_questionnaires.rb new file mode 100644 index 000000000..f736343b2 --- /dev/null +++ b/db/migrate/20210130175752_add_country_to_questionnaires.rb @@ -0,0 +1,5 @@ +class AddCountryToQuestionnaires < ActiveRecord::Migration[5.2] + def change + add_column :questionnaires, :country, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index f50a277c0..bf0f5f69e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_12_18_010133) do +ActiveRecord::Schema.define(version: 2021_01_30_175752) do create_table "active_storage_attachments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| t.string "name", null: false @@ -140,6 +140,17 @@ t.datetime "updated_at", null: false end + create_table "events", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| + t.string "title" + t.string "description" + t.string "location" + t.datetime "start" + t.datetime "finish" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "category" + end + create_table "fips", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| t.string "fips_code" t.string "city" @@ -246,6 +257,7 @@ t.integer "graduation_year" t.string "race_ethnicity" t.integer "bus_list_id" + t.string "country" t.index ["bus_list_id"], name: "index_questionnaires_on_bus_list_id" t.index ["user_id"], name: "index_questionnaires_on_user_id" end diff --git a/db/seed_messages/questionnaire--accepted.md b/db/seed_messages/questionnaire--accepted.md index 26b5341d3..f459e0edd 100644 --- a/db/seed_messages/questionnaire--accepted.md +++ b/db/seed_messages/questionnaire--accepted.md @@ -3,8 +3,8 @@ You have been accepted to attend {{hackathon_name}}! **Please RSVP:**

- Yes, I will Attend » - No, I Can't Attend » + Yes, I will Attend » + No, I Can't Attend »
- Link not working? Go to {{rsvp_url}} + Link not working? Go to {{rsvp_url}}

diff --git a/db/seed_messages/questionnaire--denied.md b/db/seed_messages/questionnaire--denied.md index 8763f3ea7..672aaaead 100644 --- a/db/seed_messages/questionnaire--denied.md +++ b/db/seed_messages/questionnaire--denied.md @@ -2,7 +2,7 @@ It is with our sincerest regret to inform you that our admissions committee has chosen to not accept your application to {{hackathon_name}} at this time. We were overjoyed with the number of applicants we received, but unfortunately we can not accept everyone. -We invite you to apply again next year. There are plenty of other hackathons this season, and it may not be too late to apply for those. Checkout https://mlh.io to find out more information. +We invite you to apply again next year. There are plenty of other hackathons this season, and it may not be too late to apply for those. Checkout https://mlh.io to find out more information. Thank you for applying,
- The {{hackathon_name}} Team diff --git a/db/seed_messages/questionnaire--rsvp_reminder.md b/db/seed_messages/questionnaire--rsvp_reminder.md index ad7577856..ab2a7b9ec 100644 --- a/db/seed_messages/questionnaire--rsvp_reminder.md +++ b/db/seed_messages/questionnaire--rsvp_reminder.md @@ -4,9 +4,9 @@

Are you coming to {{hackathon_name}}?

Let us know if we should expect you there!

- Yes, I will Attend » - No, I Can't Attend » + Yes, I will Attend » + No, I Can't Attend »
- Link not working? Go to {{rsvp_url}} + Link not working? Go to {{rsvp_url}}

\ No newline at end of file diff --git a/docs/docs-overview.md b/docs/docs-overview.md index 00554b945..1f958790e 100755 --- a/docs/docs-overview.md +++ b/docs/docs-overview.md @@ -12,6 +12,7 @@ Documentation to get you started: - [Questionnaires](questionnaires.md) - [Messages](messages.md) - [Bus Lists](busses.md) +- [Users & Staff](users-and-staff.md) - _More to come..._ ## Timeline of a hackathon diff --git a/docs/questionnaires.md b/docs/questionnaires.md index f4626bc4d..8321238a2 100644 --- a/docs/questionnaires.md +++ b/docs/questionnaires.md @@ -1,12 +1,72 @@ --- id: questionnaires title: Questionnaires +sidebar_label: Questionnaires --- A questionnaire is what an attendee submits **to apply to your hackathon.** Once a user has created an account, they complete their **questionnaire** with all required info (name, school, etc). -More to come... +## Questionnaire Questions + +### Personal Information + +The section of the **questionnaire** that asks the attendee for basic information about themselves. + +This includes: + +* **First Name** — The first name of the attendee +* **Last Name** — The attendee's last name +* **Email** — The attendee's preferred email +* **Phone** — The attendee's preferred phone to be reached at +* **Date of Birth** — The date of birth of the attendee +* **School** — The school the attendee is currently attending +* **Level of Study** — The attendee's grade level (undergraduate, postgraduate, etc) +* **Major** — The attendee's intended major +* **Gender** — The attendee's gender identity +* **Country** — The country where the attendee's hacking from + + +### Travel Information + +This is the part of the questionnaire that asks the attendee how they will be arriving to your **hackathon**. + +This includes four sections: + +* **Traveling from school** — Checkbox indicating whether or not the attendee plans to travel from their school +* **Bus List** — The sponsored bus that the attendee wishes to travel in to get to your hackathon (if the attendee does not wish to ride a bus, there is an option not to apply to one) +* **Boarded Bus** — Checkbox indicating whether or not the attendee has chosen to take another unsponsored bus +* **Bus Captain** — Another checkbox indicating whether or not the attendee plans to drive a bus of other attendees to the event + + +### Special Notices + +This section of the **questionnaire** goes over the attendee's specific needs and restrictions. + +This includes: + +* **Shirt Size** — Attendee's are able to choose from a drop down menu of different shirt sizes in both women and unisex sizes +* **Dietary Restrictions** — A list of specific allergies or other food sensitivities that an attendee might have +* **Special Needs** — A list of more specific needs an attendee needs addressing before arriving to the event. + +### Resume + +The part of the **questionnaire** that asks attendee's for their background and experience in prior hackathon events. + +Required questions include: + +* **Why Hackathon?** — This is where attendee's are able to type out why they would like to participate in your hackathon event +* **Experience** — Attendees are able to choose between three levels: First hackathon, 1-5th hackathon, and 6th hackathon or above +* **Interest** — Attendees are able to choose whether they're interested in **Design**, **Software**, **Hardware** or **Everything** +* **Graduation Year** — A field that asks attendees when they are planning to graduate. +* **Race/Ethnicity** — A field where attendees are able to choose a race/ethnicity that best fits them (also able to not answer if they prefer not to) + +Optional fields include: + +* **Resume** — Attendees are able to submit a file of their resume here +* **Portfolio Link** — A link to a portofolio of different projects the attendee had previously worked on +* **GitHub/GitLab/Bitbucket** — Link to the attendee's GitHub/GitLab/Bitbucket profile +* **Permission to share resume** — Simple checkbox asking permission to share resume with other employers ![Screenshot of a questionnaire in the dashboard](assets/questionnaire.png) diff --git a/docs/users-and-staff.md b/docs/users-and-staff.md new file mode 100644 index 000000000..a701ce205 --- /dev/null +++ b/docs/users-and-staff.md @@ -0,0 +1,72 @@ +--- +id: users-and-staff +title: Users & Staff +--- + +**Accessible by:** Directors + +The Users & Staff page is an easy way to manage the users of your hackathon's HackathonManager instance. In addition to attendees, you can also manage volunteers, organizers and directors. + +## User Management +Certain attributes within a user can be modified. + +**Email:** The email for the user, used for signing into HackathonManager. +**Role:** Role for the user, see [Roles](#roles). +**Login access:** Allow/Block login for that specific user. Will also disable the weekly report email if that user is an admin. +**Receive weekly report:** A weekly email report on admissions, bus lists, and messages. Only sent when there are weekly updates up until 7 days past the event. + + +## Roles +Roles allow for organization and permission access for users. There are four roles within HackathonManager: **User**, **Volunteer**, **Organizer** and **Director** with Director being the highest. + +### Permissions +The following are permissions for all admin roles. An admin role is any role other than User that has access to the admin dashboard. User can only create, view, edit and delete their own questionnaire. + +| | Volunteer | Organizer | Director | +|---|:-:|:-:|:-:| +| `View Questionnaires` | ✔️ | ✔️ | ✔️ | +| `Create Questionnaires` | | | ✔️ | +| `Update Questionnaires` | | | ✔️ | +| `Destroy Questionnaires` | | | ✔️ | +| `Check in Hackers` | ✔️ | ✔️ | ✔️ | +| `View Messages` | | ✔️ | ✔️ | +| `Modify Message Triggers` | | | ✔️ | +| `Send Messages` | | | ✔️ | +| `Create Messages` | | | ✔️ | +| `Update Messages` | | | ✔️ | +| `Destroy Messages` | | | ✔️ | +| `View Statistics` | | ✔️ | ✔️ | +| `View Trackable Tags` | | ✔️ | ✔️ | +| `(API) View Trackable Tags` | ✔️ | ✔️ | ✔️ | +| `Create Trackable Tags` | | | ✔️ | +| `Update Trackable Tags` | | | ✔️ | +| `Destroy Trackable Tags` | | | ✔️ | +| `View [own] Trackable Events` | ✔️ | ✔️ | ✔️ | +| `View [other] Trackable Events` | | ✔️ | ✔️ | +| `Create Trackable Events` | ✔️ | ✔️ | ✔️ | +| `Destroy [own] Trackable Events` | ✔️ | ✔️ | ✔️ | +| `Destroy [other] Trackable Events`| | | ✔️ | +| `View Schools` | ✔️ | ✔️ | ✔️ | +| `Create Schools` | | | ✔️ | +| `Update Schools` | | | ✔️ | +| `Merge Schools` | | | ✔️ | +| `Message Schools` | | | ✔️ | +| `View Users & Staff` | | | ✔️ | +| `Update Users & Staff` | | | ✔️ | +| `Destroy Users & Staff` | | | ✔️ | +| `Modify Account Role` | | | ✔️ | +| `View Legal Agreements` | | | ✔️ | +| `Update Legal Agreements` | | | ✔️ | +| `Destroy Legal Agreements` | | | ✔️ | +| `View Hackathon Settings` | | | ✔️ | +| `Update Hackathon Settings` | | | ✔️ | +| `View App Authentication` | | | ✔️ | +| `Create OAuth2 Applications` | | | ✔️ | +| `Update OAuth2 Applications` | | | ✔️ | +| `Destroy OAuth2 Applications` | | | ✔️ | +| `View Sidekiq` | | | ✔️ | +| `View Blazer` | | | ✔️ | +| `View Data Exports` | | | ✔️ | +| `Export Hackathon Data` | | | ✔️ | +_Own:_ Objects/events that were created by that user. +_Other:_ Objects/events that were created by any user. \ No newline at end of file diff --git a/test/controllers/events_controller_test.rb b/test/controllers/events_controller_test.rb new file mode 100644 index 000000000..6b003df20 --- /dev/null +++ b/test/controllers/events_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class EventsControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/manage/events_controller_test.rb b/test/controllers/manage/events_controller_test.rb new file mode 100644 index 000000000..88941587e --- /dev/null +++ b/test/controllers/manage/events_controller_test.rb @@ -0,0 +1,216 @@ +require 'test_helper' + +class Manage::EventsControllerTest < ActionController::TestCase + setup do + @event = create(:event) + end + + context "while not authenticated" do + should "redirect to sign in page on manage_events#index" do + get :index + assert_response :redirect + assert_redirected_to new_user_session_path + end + + should "not allow access to manage_events#new" do + get :new + assert_response :redirect + assert_redirected_to new_user_session_path + end + + should "not allow access to manage_events#show" do + get :show, params: { id: @event } + assert_response :redirect + assert_redirected_to new_user_session_path + end + + should "not allow access to manage_events#create" do + post :create, params: { event: { title: "should not exist title" } } + assert_response :redirect + assert_redirected_to new_user_session_path + end + + should "not allow access to manage_events#update" do + patch :update, params: { id: @event, title: "not allowed altered title" } + assert_response :redirect + assert_redirected_to new_user_session_path + end + + should "not allow access to manage_events#destroy" do + patch :destroy, params: { id: @event } + assert_response :redirect + assert_redirected_to new_user_session_path + end + end + + context "while authenticated as an user" do + setup do + @user = create(:user) + @request.env["devise.mapping"] = Devise.mappings[:user] + sign_in @user + end + + should "not allow access to manage_events#index" do + get :index + assert_response :redirect + assert_redirected_to root_path + end + + should "not allow access to manage_events#new" do + get :new + assert_response :redirect + assert_redirected_to root_path + end + + should "not allow access to manage_events#show" do + get :show, params: { id: @event } + assert_response :redirect + assert_redirected_to root_path + end + + should "not allow access to manage_events#create" do + post :create, params: { event: { title: "should not exist title" } } + assert_response :redirect + assert_redirected_to root_path + end + + should "not allow access to manage_events#update" do + patch :update, params: { id: @event, title: "not allowed altered title" } + assert_response :redirect + assert_redirected_to root_path + end + + should "not allow access to manage_events#destroy" do + patch :destroy, params: { id: @event } + assert_response :redirect + assert_redirected_to root_path + end + end + + context "while authenticated as an volunteer" do + setup do + @user = create(:volunteer) + @request.env["devise.mapping"] = Devise.mappings[:user] + sign_in @user + end + + should "not allow access to manage_events#index" do + get :index + assert_response :redirect + assert_redirected_to manage_checkins_path + end + + should "not allow access to manage_events#show" do + get :show, params: { id: @event } + assert_response :redirect + assert_redirected_to manage_checkins_path + end + + should "not allow access to manage_events#new" do + get :new + assert_response :redirect + assert_redirected_to manage_events_path + end + + should "not allow access to manage_events#create" do + post :create, params: { event: { title: "should not exist title" } } + assert_response :redirect + assert_redirected_to manage_events_path + end + + should "not allow access to manage_events#update" do + patch :update, params: { id: @event, title: "not allowed altered title" } + assert_response :redirect + assert_redirected_to manage_events_path + end + + should "not allow access to manage_events#destroy" do + patch :destroy, params: { id: @event } + assert_response :redirect + assert_redirected_to manage_events_path + end + end + + context "while authenticated as an organizer" do + setup do + @user = create(:organizer) + @request.env["devise.mapping"] = Devise.mappings[:user] + sign_in @user + end + + should "not allow access to manage_events#index" do + get :index + assert_response :redirect + assert_redirected_to manage_root_path + end + + should "not allow access to manage_events#show" do + get :show, params: { id: @event } + assert_response :redirect + assert_redirected_to manage_root_path + end + + should "not allow access to manage_events#new" do + get :new + assert_response :redirect + assert_redirected_to manage_events_path + end + + should "not allow access to manage_events#create" do + post :create, params: { event: { title: "should not exist title" } } + assert_response :redirect + assert_redirected_to manage_events_path + end + + should "not allow access to manage_events#update" do + patch :update, params: { id: @event, title: "not allowed altered title" } + assert_response :redirect + assert_redirected_to manage_events_path + end + + should "not allow access to manage_events#destroy" do + patch :destroy, params: { id: @event } + assert_response :redirect + assert_redirected_to manage_events_path + end + end + + context "while authenticated as an director" do + setup do + @user = create(:director) + @request.env["devise.mapping"] = Devise.mappings[:user] + sign_in @user + end + + should "allow access to manage_events#index" do + get :index + assert_response :success + end + + should "allow access to manage_events#new" do + get :new + assert_response :success + end + + should "allow access to manage_events#show" do + get :show, params: { id: @event } + assert_response :success + end + + should "allow access to manage_events#create" do + post :create, params: { event: { title: "test new title", start: Date.today - 1.hour, finish: Date.today } } + assert_redirected_to manage_events_path + end + + should "allow access to manage_events#update" do + patch :update, params: { id: @event, event: { title: "test update title" } } + assert_redirected_to manage_events_path + end + + should "allow access to manage_events#destroy" do + patch :destroy, params: { id: @event } + assert_response :redirect + assert_redirected_to manage_events_path + end + end +end diff --git a/test/controllers/manage/stats_controller_test.rb b/test/controllers/manage/stats_controller_test.rb index 2c48aa112..a576bab95 100644 --- a/test/controllers/manage/stats_controller_test.rb +++ b/test/controllers/manage/stats_controller_test.rb @@ -114,6 +114,6 @@ class Manage::StatsControllerTest < ActionController::TestCase private def paths - [:dietary_restrictions_special_needs_datatable, :attendee_sponsor_info_datatable, :alt_travel_datatable, :mlh_applied_datatable, :mlh_checked_in_datatable] + [:dietary_restrictions_special_needs_datatable, :attendee_sponsor_info_datatable, :alt_travel_datatable, :applied_datatable, :checked_in_datatable] end end diff --git a/test/controllers/manage/users_controller_test.rb b/test/controllers/manage/users_controller_test.rb index 2ea0cd3c9..e9cd18a1c 100644 --- a/test/controllers/manage/users_controller_test.rb +++ b/test/controllers/manage/users_controller_test.rb @@ -1,6 +1,8 @@ require 'test_helper' class Manage::UsersControllerTest < ActionController::TestCase + include ActiveJob::TestHelper + setup do @user = create(:user) end @@ -12,6 +14,16 @@ class Manage::UsersControllerTest < ActionController::TestCase assert_redirected_to new_user_session_path end + should "not allow access to user_datatable" do + get :user_datatable + assert_redirected_to new_user_session_path + end + + should "not allow access to staff_datatable" do + get :staff_datatable + assert_redirected_to new_user_session_path + end + should "not allow access to manage_users user datatables api" do post :user_datatable, format: :json, params: { "columns[0][data]" => "" } assert_response 401 @@ -59,6 +71,16 @@ class Manage::UsersControllerTest < ActionController::TestCase assert_redirected_to root_path end + should "not allow access to user_datatable" do + get :user_datatable + assert_redirected_to root_path + end + + should "not allow access to staff_datatable" do + get :staff_datatable + assert_redirected_to root_path + end + should "not allow access to manage_users users datatables api" do post :user_datatable, format: :json, params: { "columns[0][data]" => "" } assert_response :redirect @@ -108,6 +130,16 @@ class Manage::UsersControllerTest < ActionController::TestCase assert_redirected_to manage_checkins_path end + should "not allow access to user_datatable" do + get :user_datatable + assert_redirected_to manage_checkins_path + end + + should "not allow access to staff_datatable" do + get :staff_datatable + assert_redirected_to manage_checkins_path + end + should "not allow access to manage_users users datatables api" do post :user_datatable, format: :json, params: { "columns[0][data]" => "" } assert_redirected_to manage_checkins_path @@ -154,6 +186,16 @@ class Manage::UsersControllerTest < ActionController::TestCase assert_redirected_to manage_root_path end + should "not allow access to user_datatable" do + get :user_datatable + assert_redirected_to manage_root_path + end + + should "not allow access to staff_datatable" do + get :staff_datatable + assert_redirected_to manage_root_path + end + should "not allow access to manage_users users datatables api" do post :user_datatable, format: :json, params: { "columns[0][data]" => "" } assert_redirected_to manage_root_path @@ -200,55 +242,51 @@ class Manage::UsersControllerTest < ActionController::TestCase assert_response :success end - # TODO: Tests appear to be stalling Travis CI - - # should "create a new admin" do - # post :create, params: { user: { email: "test@example.com", role: 'admin' } } - # assert_response :redirect - # assert_redirected_to manage_users_path - # assert assigns(:user).admin?, "new user should be an admin" - # end - - # should "create a new limited access admin" do - # post :create, params: { user: { email: "test@example.com", role: 'admin_limited_access' } } - # assert_response :redirect - # assert_redirected_to manage_users_path - # assert !assigns(:user).admin?, "new user should not be an admin" - # assert assigns(:user).admin_limited_access?, "new user should be a limited access admin" - # end - - # should "not create an admin with duplicate emails" do - # create(:user, email: "existing@example.com") - # assert_difference('User.count', 0) do - # post :create, params: { user: { email: "existing@example.com", role: 'admin' } } - # end - # end - - # should "allow access to manage_admins#new" do - # get :new, params: { id: @user } - # assert_response :success - # end - - # should "allow access to manage_admins#show" do - # get :show, params: { id: @user } - # assert_response :success - # end - - # should "allow access to manage_admins#edit" do - # get :edit, params: { id: @user } - # assert_response :success - # end - - # should "update user" do - # patch :update, params: { id: @user, user: { email: "test@example.coma" } } - # assert_redirected_to manage_users_path - # end - - # should "destroy user" do - # assert_difference('User.count', -1) do - # patch :destroy, params: { id: @user } - # end - # assert_redirected_to manage_users_path - # end + should "allow access to user_datatable" do + get :user_datatable + assert_response :success + end + + should "allow access to staff_datatable" do + get :staff_datatable + assert_response :success + end + + should "be able to reset a user's password" do + assert_difference "enqueued_jobs.size", 1 do + patch :reset_password, params: { id: @user } + end + assert_redirected_to manage_users_path + end + + should "allow access to manage_users#show" do + get :show, params: { id: @user } + assert_response :success + end + + should "allow access to manage_users#edit" do + get :edit, params: { id: @user } + assert_response :success + end + + should "update user" do + patch :update, params: { id: @user, user: { email: "test@example.coma" } } + assert_redirected_to manage_users_path + end + + should "destroy user" do + assert_difference('User.count', -1) do + patch :destroy, params: { id: @user } + end + assert_redirected_to manage_users_path + end + + should "destroy user and user's questionnaire" do + @questionnaire = create(:questionnaire, user_id: @user.id) + assert_difference('Questionnaire.count', -1) do + patch :destroy, params: { id: @user } + end + assert_redirected_to manage_users_path + end end end diff --git a/test/controllers/questionnaires_controller_test.rb b/test/controllers/questionnaires_controller_test.rb index 490d07d2d..9bde93f97 100644 --- a/test/controllers/questionnaires_controller_test.rb +++ b/test/controllers/questionnaires_controller_test.rb @@ -47,7 +47,7 @@ class QuestionnairesControllerTest < ActionController::TestCase should "create questionnaire" do assert_difference('Questionnaire.count', 1) do - post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_id: @school.id, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } + post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, country: @questionnaire.country, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_id: @school.id, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } end assert_redirected_to questionnaires_path @@ -60,8 +60,8 @@ class QuestionnairesControllerTest < ActionController::TestCase should "not allow multiple questionnaires" do assert_difference('Questionnaire.count', 1) do - post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_id: @school.id, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } - post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_id: @school.id, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } + post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, country: @questionnaire.country, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_id: @school.id, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } + post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, country: @questionnaire.country, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_id: @school.id, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } end assert_redirected_to questionnaires_path @@ -80,7 +80,7 @@ class QuestionnairesControllerTest < ActionController::TestCase should "not allow creation" do HackathonConfig['accepting_questionnaires'] = false assert_difference('Questionnaire.count', 0) do - post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_id: @school.id, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } + post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, country: @questionnaire.country, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_id: @school.id, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } end end end @@ -88,13 +88,13 @@ class QuestionnairesControllerTest < ActionController::TestCase context "#school_name" do context "on create" do should "save existing school name" do - post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_name: @school.name, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } + post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, country: @questionnaire.country, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_name: @school.name, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } assert_redirected_to questionnaires_path assert_equal 1, School.all.count end should "create a new school when unknown" do - post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_name: "New School", major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } + post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, country: @questionnaire.country, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_name: "New School", major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } assert_redirected_to questionnaires_path assert_equal 2, School.all.count end @@ -103,7 +103,7 @@ class QuestionnairesControllerTest < ActionController::TestCase message = create(:message, type: 'automated', trigger: "questionnaire.pending") assert_difference 'enqueued_jobs.size', 1 do assert_difference 'Questionnaire.count', 1 do - post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_name: @school.name, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } + post :create, params: { questionnaire: { experience: @questionnaire.experience, interest: @questionnaire.interest, country: @questionnaire.country, phone: @questionnaire.phone, level_of_study: @questionnaire.level_of_study, date_of_birth: @questionnaire.date_of_birth, shirt_size: @questionnaire.shirt_size, school_name: @school.name, major: @questionnaire.major, gender: @questionnaire.gender, why_attend: @questionnaire.why_attend, graduation_year: @questionnaire.graduation_year, race_ethnicity: @questionnaire.race_ethnicity, agreement_ids: @questionnaire.agreements.map(&:id) } } end end questionnaire = Questionnaire.last diff --git a/test/factories/event.rb b/test/factories/event.rb new file mode 100644 index 000000000..ecb8cb57d --- /dev/null +++ b/test/factories/event.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :event do + title { "Testing" } + description { "This is fun to test" } + start { Date.today - 1.hour } + finish { Date.today } + location { "GOL-1455" } + end +end diff --git a/test/factories/questionnaire.rb b/test/factories/questionnaire.rb index de734683b..f6d16dd15 100644 --- a/test/factories/questionnaire.rb +++ b/test/factories/questionnaire.rb @@ -11,6 +11,7 @@ special_needs { "" } can_share_info { true } gender { "Male" } + country { "United States" } major { "Computer Science" } level_of_study { "University (Undergraduate)" } graduation_year { Date.today.year } diff --git a/test/fixtures/events.yml b/test/fixtures/events.yml new file mode 100644 index 000000000..9ffbd5d90 --- /dev/null +++ b/test/fixtures/events.yml @@ -0,0 +1,15 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + title: MyString + description: MyString + location: MyString + start: 2020-05-29 10:21:41 + finish: 2020-05-29 10:21:41 + +two: + title: MyString + description: MyString + location: MyString + start: 2020-05-29 10:21:41 + finish: 2020-05-29 10:21:41 diff --git a/test/models/event_test.rb b/test/models/event_test.rb new file mode 100644 index 000000000..9b73cff83 --- /dev/null +++ b/test/models/event_test.rb @@ -0,0 +1,6 @@ +require 'test_helper' + +class EventTest < ActiveSupport::TestCase + should validate_presence_of :title + should validate_presence_of :start +end diff --git a/test/models/questionnaire_test.rb b/test/models/questionnaire_test.rb index 7cf590185..4fa2d3863 100644 --- a/test/models/questionnaire_test.rb +++ b/test/models/questionnaire_test.rb @@ -5,7 +5,7 @@ class QuestionnaireTest < ActiveSupport::TestCase should belong_to :user should belong_to :school - should belong_to :bus_list + should belong_to(:bus_list).optional should have_and_belong_to_many :agreements should validate_uniqueness_of :user_id diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 1b288f72f..a5956b0a0 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -7,7 +7,7 @@ class UserTest < ActiveSupport::TestCase should strip_attribute :last_name should strip_attribute :email - should validate_uniqueness_of :email + should validate_uniqueness_of(:email).ignoring_case_sensitivity should validate_presence_of :first_name should validate_presence_of :last_name diff --git a/test/test_helper.rb b/test/test_helper.rb index 2d3f333a8..4208de352 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -51,3 +51,10 @@ class ActiveSupport::TestCase class ActionController::TestCase include Devise::Test::ControllerHelpers end + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :minitest + with.library :rails + end +end diff --git a/vendor/cache/erubi-1.10.0.gem b/vendor/cache/erubi-1.10.0.gem new file mode 100644 index 000000000..31894cf32 Binary files /dev/null and b/vendor/cache/erubi-1.10.0.gem differ diff --git a/vendor/cache/erubi-1.9.0.gem b/vendor/cache/erubi-1.9.0.gem deleted file mode 100644 index e169e6582..000000000 Binary files a/vendor/cache/erubi-1.9.0.gem and /dev/null differ diff --git a/vendor/cache/ffi-1.13.1-x64-mingw32.gem b/vendor/cache/ffi-1.13.1-x64-mingw32.gem new file mode 100644 index 000000000..cdf741aa3 Binary files /dev/null and b/vendor/cache/ffi-1.13.1-x64-mingw32.gem differ diff --git a/vendor/cache/fugit-1.3.6.gem b/vendor/cache/fugit-1.3.6.gem deleted file mode 100644 index cad39ae0e..000000000 Binary files a/vendor/cache/fugit-1.3.6.gem and /dev/null differ diff --git a/vendor/cache/fugit-1.4.2.gem b/vendor/cache/fugit-1.4.2.gem new file mode 100644 index 000000000..4e0430480 Binary files /dev/null and b/vendor/cache/fugit-1.4.2.gem differ diff --git a/vendor/cache/fullcalendar-rails-3.9.0.0.gem b/vendor/cache/fullcalendar-rails-3.9.0.0.gem new file mode 100644 index 000000000..f397ddc19 Binary files /dev/null and b/vendor/cache/fullcalendar-rails-3.9.0.0.gem differ diff --git a/vendor/cache/loofah-2.7.0.gem b/vendor/cache/loofah-2.7.0.gem deleted file mode 100644 index 13bd5eb16..000000000 Binary files a/vendor/cache/loofah-2.7.0.gem and /dev/null differ diff --git a/vendor/cache/loofah-2.8.0.gem b/vendor/cache/loofah-2.8.0.gem new file mode 100644 index 000000000..6d89bedae Binary files /dev/null and b/vendor/cache/loofah-2.8.0.gem differ diff --git a/vendor/cache/momentjs-rails-2.20.1.gem b/vendor/cache/momentjs-rails-2.20.1.gem new file mode 100644 index 000000000..bbe07f855 Binary files /dev/null and b/vendor/cache/momentjs-rails-2.20.1.gem differ diff --git a/vendor/cache/msgpack-1.3.3-x64-mingw32.gem b/vendor/cache/msgpack-1.3.3-x64-mingw32.gem new file mode 100644 index 000000000..fdf50d9d6 Binary files /dev/null and b/vendor/cache/msgpack-1.3.3-x64-mingw32.gem differ diff --git a/vendor/cache/mysql2-0.5.3-x64-mingw32.gem b/vendor/cache/mysql2-0.5.3-x64-mingw32.gem new file mode 100644 index 000000000..cd7a490c4 Binary files /dev/null and b/vendor/cache/mysql2-0.5.3-x64-mingw32.gem differ diff --git a/vendor/cache/nio4r-2.5.3.gem b/vendor/cache/nio4r-2.5.3.gem deleted file mode 100644 index 9947d76b0..000000000 Binary files a/vendor/cache/nio4r-2.5.3.gem and /dev/null differ diff --git a/vendor/cache/nio4r-2.5.4.gem b/vendor/cache/nio4r-2.5.4.gem new file mode 100644 index 000000000..f5eaa0c43 Binary files /dev/null and b/vendor/cache/nio4r-2.5.4.gem differ diff --git a/vendor/cache/raabro-1.3.1.gem b/vendor/cache/raabro-1.3.1.gem deleted file mode 100644 index d82114cd2..000000000 Binary files a/vendor/cache/raabro-1.3.1.gem and /dev/null differ diff --git a/vendor/cache/raabro-1.4.0.gem b/vendor/cache/raabro-1.4.0.gem new file mode 100644 index 000000000..44f6043be Binary files /dev/null and b/vendor/cache/raabro-1.4.0.gem differ diff --git a/vendor/cache/rack-cors-1.1.1.gem b/vendor/cache/rack-cors-1.1.1.gem new file mode 100644 index 000000000..d0b77079e Binary files /dev/null and b/vendor/cache/rack-cors-1.1.1.gem differ diff --git a/vendor/cache/rack-protection-2.0.8.1.gem b/vendor/cache/rack-protection-2.0.8.1.gem deleted file mode 100644 index 2e6a53b42..000000000 Binary files a/vendor/cache/rack-protection-2.0.8.1.gem and /dev/null differ diff --git a/vendor/cache/redis-4.2.1.gem b/vendor/cache/redis-4.2.1.gem deleted file mode 100644 index 9d72f7304..000000000 Binary files a/vendor/cache/redis-4.2.1.gem and /dev/null differ diff --git a/vendor/cache/redis-4.2.5.gem b/vendor/cache/redis-4.2.5.gem new file mode 100644 index 000000000..1d0be7021 Binary files /dev/null and b/vendor/cache/redis-4.2.5.gem differ diff --git a/vendor/cache/sassc-2.4.0-x64-mingw32.gem b/vendor/cache/sassc-2.4.0-x64-mingw32.gem new file mode 100644 index 000000000..9b3393d35 Binary files /dev/null and b/vendor/cache/sassc-2.4.0-x64-mingw32.gem differ diff --git a/vendor/cache/shoulda-3.5.0.gem b/vendor/cache/shoulda-3.5.0.gem deleted file mode 100644 index a79d90d9d..000000000 Binary files a/vendor/cache/shoulda-3.5.0.gem and /dev/null differ diff --git a/vendor/cache/shoulda-4.0.0.gem b/vendor/cache/shoulda-4.0.0.gem new file mode 100644 index 000000000..3f271dc15 Binary files /dev/null and b/vendor/cache/shoulda-4.0.0.gem differ diff --git a/vendor/cache/shoulda-context-1.2.2.gem b/vendor/cache/shoulda-context-1.2.2.gem deleted file mode 100644 index 9f93a904e..000000000 Binary files a/vendor/cache/shoulda-context-1.2.2.gem and /dev/null differ diff --git a/vendor/cache/shoulda-context-2.0.0.gem b/vendor/cache/shoulda-context-2.0.0.gem new file mode 100644 index 000000000..ced302fb3 Binary files /dev/null and b/vendor/cache/shoulda-context-2.0.0.gem differ diff --git a/vendor/cache/shoulda-matchers-2.8.0.gem b/vendor/cache/shoulda-matchers-2.8.0.gem deleted file mode 100644 index 950440762..000000000 Binary files a/vendor/cache/shoulda-matchers-2.8.0.gem and /dev/null differ diff --git a/vendor/cache/shoulda-matchers-4.4.1.gem b/vendor/cache/shoulda-matchers-4.4.1.gem new file mode 100644 index 000000000..6695d1a87 Binary files /dev/null and b/vendor/cache/shoulda-matchers-4.4.1.gem differ diff --git a/vendor/cache/sidekiq-6.0.7.gem b/vendor/cache/sidekiq-6.0.7.gem deleted file mode 100644 index 93bdb9068..000000000 Binary files a/vendor/cache/sidekiq-6.0.7.gem and /dev/null differ diff --git a/vendor/cache/sidekiq-6.1.3.gem b/vendor/cache/sidekiq-6.1.3.gem new file mode 100644 index 000000000..12963993f Binary files /dev/null and b/vendor/cache/sidekiq-6.1.3.gem differ diff --git a/vendor/cache/sidekiq-cron-1.2.0.gem b/vendor/cache/sidekiq-cron-1.2.0.gem deleted file mode 100644 index 1cf695a2e..000000000 Binary files a/vendor/cache/sidekiq-cron-1.2.0.gem and /dev/null differ diff --git a/vendor/cache/sprockets-rails-3.2.1.gem b/vendor/cache/sprockets-rails-3.2.1.gem deleted file mode 100644 index 58921be3a..000000000 Binary files a/vendor/cache/sprockets-rails-3.2.1.gem and /dev/null differ diff --git a/vendor/cache/sprockets-rails-3.2.2.gem b/vendor/cache/sprockets-rails-3.2.2.gem new file mode 100644 index 000000000..8d2c00be6 Binary files /dev/null and b/vendor/cache/sprockets-rails-3.2.2.gem differ diff --git a/vendor/cache/tzinfo-1.2.7.gem b/vendor/cache/tzinfo-1.2.7.gem deleted file mode 100644 index 554971346..000000000 Binary files a/vendor/cache/tzinfo-1.2.7.gem and /dev/null differ diff --git a/vendor/cache/tzinfo-1.2.9.gem b/vendor/cache/tzinfo-1.2.9.gem new file mode 100644 index 000000000..9f596f54b Binary files /dev/null and b/vendor/cache/tzinfo-1.2.9.gem differ diff --git a/website/core/Footer.js b/website/core/Footer.js index 50e4faf6a..725281cd4 100755 --- a/website/core/Footer.js +++ b/website/core/Footer.js @@ -46,7 +46,7 @@ class Footer extends React.Component {
More
- + GitHub Repo