diff --git a/.gitignore b/.gitignore index 8331c5c8..c65dc0b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,27 @@ +# Global, for Macs +*~ + +# Compiled Python files +*.pyc + +# Folder view configuration files +.DS_Store +Desktop.ini + +# Thumbnail cache files +._* +Thumbs.db + +# Files that might appear on external disks +.Spotlight-V100 +.Trashes + + +# Legacy from before Feb 2021 /db/test.sqlite3-journal config/application.rb config/config.yml +config/local_env.yml config/secrets.yml /vendor/cache /vendor/cache-old @@ -40,3 +61,63 @@ docker-compose.override.yml .buildconfig osem_development osem_test +config/local_env.yml + +# From GitHub, for Ruby +# https://github.com/github/gitignore/blob/master/Ruby.gitignore +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ + +# Used by dotenv library to load environment variables. +# .env + +# Ignore Byebug command history file. +.byebug_history + +## Specific to RubyMotion: +.dat* +.repl_history +build/ +*.bridgesupport +build-iPhoneOS/ +build-iPhoneSimulator/ + +## Specific to RubyMotion (use of CocoaPods): +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# vendor/Pods/ + +## Documentation cache and generated files: +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ + +## Environment normalization: +/.bundle/ +/vendor/bundle +/lib/bundler/man/ + +# for a library or gem, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# Gemfile.lock +# .ruby-version +# .ruby-gemset + +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +.rvmrc + +# Used by RuboCop. Remote config files pulled in from inherit_from directive. +.rubocop-https?--* diff --git a/Gemfile.lock b/Gemfile.lock index 21de475f..b07d289e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -104,10 +104,13 @@ GEM rack-test (>= 0.6.3) regexp_parser (~> 1.5) xpath (~> 3.2) - carrierwave (1.3.2) - activemodel (>= 4.0.0) - activesupport (>= 4.0.0) - mime-types (>= 1.16) + carrierwave (2.2.0) + activemodel (>= 5.0.0) + activesupport (>= 5.0.0) + addressable (~> 2.6) + image_processing (~> 1.1) + mimemagic (>= 0.3.0) + mini_mime (>= 0.1.3) ssrf_filter (~> 1.0) carrierwave-bombshelter (0.2.2) activesupport (>= 3.2.0) @@ -115,7 +118,7 @@ GEM fastimage case_transform (0.2) activesupport - chartkick (3.4.0) + chartkick (3.4.2) childprocess (3.0.0) chronic (0.10.2) chunky_png (1.3.11) @@ -251,6 +254,9 @@ GEM i18n (1.8.9) concurrent-ruby (~> 1.0) i18n_data (0.8.0) + image_processing (1.12.1) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) inversion (1.1.1) loggability (~> 0.12) io-like (0.3.0) @@ -294,7 +300,7 @@ GEM method_source (0.8.2) mime-types (3.3.1) mime-types-data (~> 3.2015) - mime-types-data (3.2020.1104) + mime-types-data (3.2021.0212) mimemagic (0.3.5) mina (1.2.3) open4 (~> 1.3.4) @@ -302,7 +308,7 @@ GEM mini_magick (4.9.5) mini_mime (1.0.2) mini_portile2 (2.5.0) - minitest (5.14.3) + minitest (5.14.4) momentjs-rails (2.20.1) railties (>= 3.1) monetize (1.9.2) @@ -540,6 +546,8 @@ GEM ruby-openid (2.9.2) ruby-progressbar (1.10.1) ruby-rc4 (0.1.5) + ruby-vips (2.0.17) + ffi (~> 1.9) ruby2_keywords (0.0.4) ruby_dep (1.5.0) ruby_parser (3.11.0) diff --git a/README.md b/README.md index 489b10b9..71f3ac37 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,21 @@ +### Spring 2021 Student Project +[![Build Status](https://travis-ci.com/CactusPuppy/snapcon.svg?branch=master)](https://travis-ci.com/CactusPuppy/snapcon) +[![Maintainability](https://api.codeclimate.com/v1/badges/6c7fc446b3b10866ba71/maintainability)](https://codeclimate.com/github/CactusPuppy/snapcon/maintainability) +[![codecov](https://codecov.io/gh/CactusPuppy/snapcon/branch/master/graph/badge.svg?token=y4aEAtw6KJ)](https://codecov.io/gh/CactusPuppy/snapcon) +[![Security Status](https://hakiri.io/github/CactusPuppy/snapcon/master.svg)](https://hakiri.io/github/CactusPuppy/snapcon/master) +[![Depfu](https://badges.depfu.com/badges/16eb1ffb3a9f1a36c4e595a5ae2a1dca/overview.svg)](https://depfu.com/github/CactusPuppy/snapcon?project_id=22682) +[![Bluejay Dashboard](https://img.shields.io/badge/Bluejay-Dashboard_5-blue.svg)](http://dashboard.bluejay.governify.io/dashboard/script/dashboardLoader.js?dashboardURL=https://reporter.bluejay.governify.io/api/v4/dashboards/tpa-CS169L-GH-CactusPuppy_snapcon/main) + + + Deploy + + + + Pivotal Tracker + + +--- + [![Build Status](https://travis-ci.com/snap-cloud/snapcon.svg?branch=master)](https://travis-ci.com/snap-cloud/snapcon) [![Maintainability](https://api.codeclimate.com/v1/badges/b7b0d559a03bf218663a/maintainability)](https://codeclimate.com/github/snap-cloud/snapcon/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/b7b0d559a03bf218663a/test_coverage)](https://codeclimate.com/github/snap-cloud/snapcon/test_coverage) @@ -11,15 +29,19 @@ # [Snap!Con](https://snapcon.org) forked from: ## Open Source Event Manager - [osem.io](https://osem.io) + ![OSEM Logo](doc/osem-logo.png) An event management tool tailored to Free and Open Source Software conferences. ## Installation + Please refer to our [installation guide](INSTALL.md). ## How to contribute to OSEM + Please refer to our [contributing guide](CONTRIBUTING.md). ## Contact + GitHub issues are the primary way for communicating about specific proposed changes to this project. If you have other questions feel free to subscribe to the [opensuse-web@opensuse.org](http://lists.opensuse.org/opensuse-web/) mailinglist, all OSEM contributors are on that list! Additionally you can use #osem channel on freenode IRC. diff --git a/app/assets/javascripts/osem.js b/app/assets/javascripts/osem.js index dcab692c..0839f13b 100644 --- a/app/assets/javascripts/osem.js +++ b/app/assets/javascripts/osem.js @@ -149,15 +149,21 @@ function word_count(text, divId, maxcount) { /* Wait for the DOM to be ready before attaching events to the elements */ $( document ).ready(function() { - /* Set the minimum and maximum proposal abstract word length */ + /* Set the minimum and maximum proposal abstract and submission text word length */ $("#event_event_type_id").change(function () { var $selected = $("#event_event_type_id option:selected") var max = $selected.data("max-words"); var min = $selected.data("min-words"); $("#abstract-maximum-word-count").text(max); + $("#submission-maximum-word-count").text(max); $("#abstract-minimum-word-count").text(min); + $("#submission-minimum-word-count").text(min); word_count($('#event_abstract').get(0), 'abstract-count', max); + word_count($('#event_submission_text').get(0), 'submission-count', max); + + // Set the placeholder text for the abstract + $('#event_submission_text').attr("placeholder", $selected.data("help")); }) .trigger('change'); @@ -167,6 +173,13 @@ $( document ).ready(function() { var max = $selected.data("max-words"); word_count(this, 'abstract-count', max); } ); + + /* Count the submission text length */ + $("#event_submission_text").bind('change keyup paste input', function() { + var $selected = $("event_event_type_id option:selected") + var max = $selected.data("max-words"); + word_count(this, 'submission-count', max); + }); }); /* Commodity function for modal windows */ diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb index b24882f3..f5e68165 100644 --- a/app/controllers/admin/events_controller.rb +++ b/app/controllers/admin/events_controller.rb @@ -175,7 +175,7 @@ module Admin def event_params params.require(:event).permit( # Set also in proposals controller - :title, :subtitle, :event_type_id, :abstract, :description, :require_registration, :difficulty_level_id, + :title, :subtitle, :event_type_id, :abstract, :submission_text, :description, :require_registration, :difficulty_level_id, # Set only in admin/events controller :track_id, :state, :language, :is_highlight, :max_attendees, # Not used anymore? diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0a4eca46..c9f93a30 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,7 +25,7 @@ class ApplicationController < ActionController::Base !request.xhr?) # don't store ajax calls session[:return_to] = request.fullpath end - end + end def after_sign_in_path_for(_resource) if (can? :view, Conference) && diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb index adfdbea0..bf1b3adc 100644 --- a/app/controllers/proposals_controller.rb +++ b/app/controllers/proposals_controller.rb @@ -170,7 +170,7 @@ class ProposalsController < ApplicationController def event_params params.require(:event).permit(:event_type_id, :track_id, :difficulty_level_id, - :title, :subtitle, :abstract, :description, + :title, :subtitle, :abstract, :submission_text, :description, :require_registration, :max_attendees, :language, speaker_ids: [], volunteer_ids: [] ) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 0bf44f49..55af84df 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -31,7 +31,9 @@ class UsersController < ApplicationController end # Somewhat of a hack: users/current/edit + # rubocop:disable Naming/MemoizedInstanceVariableName def load_user @user ||= (params[:id] && params[:id] != 'current' && User.find(params[:id]) || current_user) end + # rubocop:enable Naming/MemoizedInstanceVariableName end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 90b3fa2b..1c7f5da6 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -112,7 +112,7 @@ module ApplicationHelper concurrent_events << other_event_schedule.event end end - concurrent_events.sort_by { |event_schedule| event_schedule.room&.order } + concurrent_events.sort_by { |schedule| schedule.room&.order } end def speaker_links(event) diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 95b2d106..eebaa4fe 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# TODO: Split this module into smaller modules +# rubocop:disable Metrics/ModuleLength module EventsHelper ## # Includes functions related to events @@ -193,7 +195,7 @@ module EventsHelper end end - def calendar_timestamp(timestamp, timezone) + def calendar_timestamp(timestamp, _timezone) timestamp = timestamp.in_time_zone('GMT') timestamp -= timestamp.utc_offset timestamp.strftime('%Y%m%dT%H%M%S') @@ -256,3 +258,4 @@ module EventsHelper end end end +# rubocop:enable Metrics/ModuleLength diff --git a/app/mailers/mailbot.rb b/app/mailers/mailbot.rb index 15c5846f..c055ec84 100644 --- a/app/mailers/mailbot.rb +++ b/app/mailers/mailbot.rb @@ -1,17 +1,19 @@ # frozen_string_literal: true SNAPCON_BCC_ADDRESS = 'messages@snap.berkeley.edu' +EMAIL_TEMPLATE = 'email_template' YTLF_TICKET_ID = 50 class Mailbot < ActionMailer::Base def registration_mail(conference, user) - mail(to: user.email, - bcc: SNAPCON_BCC_ADDRESS, - from: conference.contact.email, - subject: conference.email_settings.registration_subject, - body: conference.email_settings.generate_email_on_conf_updates(conference, - user, - conference.email_settings.registration_body)) + @email_body = conference.email_settings.generate_email_on_conf_updates(conference, user, conference.email_settings.registration_body) + @logo = conference.picture.thumb.url + + mail(to: user.email, + bcc: SNAPCON_BCC_ADDRESS, + from: conference.contact.email, + subject: conference.email_settings.registration_subject, + template_name: EMAIL_TEMPLATE) end def ticket_confirmation_mail(ticket_purchase) @@ -39,108 +41,128 @@ class Mailbot < ActionMailer::Base def acceptance_mail(event) conference = event.program.conference - mail(to: event.submitter.email, - bcc: SNAPCON_BCC_ADDRESS, - from: conference.contact.email, - subject: conference.email_settings.accepted_subject, - body: conference.email_settings.generate_event_mail(event, conference.email_settings.accepted_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_event_mail(event, conference.email_settings.accepted_body) + + mail(to: event.submitter.email, + bcc: SNAPCON_BCC_ADDRESS, + from: conference.contact.email, + subject: conference.email_settings.accepted_subject, + template_name: EMAIL_TEMPLATE) end def submitted_proposal_mail(event) conference = event.program.conference - mail(to: event.submitter.email, - bcc: SNAPCON_BCC_ADDRESS, - from: conference.contact.email, - subject: conference.email_settings.submitted_proposal_subject, - body: conference.email_settings.generate_event_mail(event, conference.email_settings.submitted_proposal_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_event_mail(event, conference.email_settings.submitted_proposal_body) + + mail(to: event.submitter.email, + bcc: SNAPCON_BCC_ADDRESS, + from: conference.contact.email, + subject: conference.email_settings.submitted_proposal_subject, + template_name: EMAIL_TEMPLATE) end def rejection_mail(event) conference = event.program.conference - mail(to: event.submitter.email, - bcc: SNAPCON_BCC_ADDRESS, - from: conference.contact.email, - subject: conference.email_settings.rejected_subject, - body: conference.email_settings.generate_event_mail(event, conference.email_settings.rejected_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_event_mail(event, conference.email_settings.rejected_body) + + mail(to: event.submitter.email, + bcc: SNAPCON_BCC_ADDRESS, + from: conference.contact.email, + subject: conference.email_settings.rejected_subject, + template_name: EMAIL_TEMPLATE) end def confirm_reminder_mail(event) conference = event.program.conference - mail(to: event.submitter.email, - bcc: SNAPCON_BCC_ADDRESS, - from: conference.contact.email, - subject: conference.email_settings.confirmed_without_registration_subject, - body: conference.email_settings.generate_event_mail(event, - conference.email_settings.confirmed_without_registration_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_event_mail(event, conference.email_settings.confirmed_without_registration_body) + + mail(to: event.submitter.email, + bcc: SNAPCON_BCC_ADDRESS, + from: conference.contact.email, + subject: conference.email_settings.confirmed_without_registration_subject, + template_name: EMAIL_TEMPLATE) end def conference_date_update_mail(conference, user) - mail(to: user.email, - bcc: SNAPCON_BCC_ADDRESS, - from: conference.contact.email, - subject: conference.email_settings.conference_dates_updated_subject, - body: conference.email_settings.generate_email_on_conf_updates(conference, - user, - conference.email_settings.conference_dates_updated_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_email_on_conf_updates(conference, user, conference.email_settings.conference_dates_updated_body) + + mail(to: user.email, + bcc: SNAPCON_BCC_ADDRESS, + from: conference.contact.email, + subject: conference.email_settings.conference_dates_updated_subject, + template_name: EMAIL_TEMPLATE) end def conference_registration_date_update_mail(conference, user) - mail(to: user.email, - bcc: SNAPCON_BCC_ADDRESS, - from: conference.contact.email, - subject: conference.email_settings.conference_registration_dates_updated_subject, - body: conference.email_settings.generate_email_on_conf_updates(conference, - user, - conference.email_settings.conference_registration_dates_updated_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_email_on_conf_updates(conference, user, conference.email_settings.conference_registration_dates_updated_body) + + mail(to: user.email, + bcc: SNAPCON_BCC_ADDRESS, + from: conference.contact.email, + subject: conference.email_settings.conference_registration_dates_updated_subject, + template_name: EMAIL_TEMPLATE) end def conference_venue_update_mail(conference, user) - mail(to: user.email, - bcc: SNAPCON_BCC_ADDRESS, - from: conference.contact.email, - subject: conference.email_settings.venue_updated_subject, - body: conference.email_settings.generate_email_on_conf_updates(conference, - user, - conference.email_settings.venue_updated_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_email_on_conf_updates(conference, user, conference.email_settings.venue_updated_body) + + mail(to: user.email, + bcc: SNAPCON_BCC_ADDRESS, + from: conference.contact.email, + subject: conference.email_settings.venue_updated_subject, + template_name: EMAIL_TEMPLATE) end def conference_schedule_update_mail(conference, user) - mail(to: user.email, - from: conference.contact.email, - subject: conference.email_settings.program_schedule_public_subject, - body: conference.email_settings.generate_email_on_conf_updates(conference, - user, - conference.email_settings.program_schedule_public_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_email_on_conf_updates(conference, user, conference.email_settings.program_schedule_public_body) + + mail(to: user.email, + from: conference.contact.email, + subject: conference.email_settings.program_schedule_public_subject, + template_name: EMAIL_TEMPLATE) end def conference_cfp_update_mail(conference, user) - mail(to: user.email, - from: conference.contact.email, - subject: conference.email_settings.cfp_dates_updated_subject, - body: conference.email_settings.generate_email_on_conf_updates(conference, - user, - conference.email_settings.cfp_dates_updated_body)) + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_email_on_conf_updates(conference, user, conference.email_settings.cfp_dates_updated_body) + + mail(to: user.email, + from: conference.contact.email, + subject: conference.email_settings.cfp_dates_updated_subject, + template_name: EMAIL_TEMPLATE) end def conference_booths_acceptance_mail(booth) conference = booth.conference + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_booth_mail(booth, conference.email_settings.booths_acceptance_body) - mail(to: booth.submitter.email, - from: conference.contact.email, - subject: conference.email_settings.booths_acceptance_subject, - body: conference.email_settings.generate_booth_mail(booth, conference.email_settings.booths_acceptance_body)) + mail(to: booth.submitter.email, + from: conference.contact.email, + subject: conference.email_settings.booths_acceptance_subject, + template_name: EMAIL_TEMPLATE) end def conference_booths_rejection_mail(booth) conference = booth.conference + @logo = conference.picture.thumb.url + @email_body = conference.email_settings.generate_booth_mail(booth, conference.email_settings.booths_rejection_body) - mail(to: booth.submitter.email, - from: conference.contact.email, - subject: conference.email_settings.booths_rejection_subject, - body: conference.email_settings.generate_booth_mail(booth, conference.email_settings.booths_rejection_body)) + mail(to: booth.submitter.email, + from: conference.contact.email, + subject: conference.email_settings.booths_rejection_subject, + template_name: EMAIL_TEMPLATE) end def event_comment_mail(comment, user) diff --git a/app/models/ability.rb b/app/models/ability.rb index 9db7bacf..ec17e14c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -68,6 +68,8 @@ class Ability end # Abilities for signed in users + # TODO: Refactor into multiple functions + # rubocop:disable Metrics/AbcSize def signed_in(user) # Abilities from not_signed_in user are also inherited not_signed_in @@ -140,6 +142,7 @@ class Ability user == track.submitter && !(track.accepted? || track.confirmed?) end end + # rubocop:enable Metrics/AbcSize # Abilities for users with roles wandering around in non-admin views. def common_abilities_for_admins(user) diff --git a/app/models/commercial.rb b/app/models/commercial.rb index 1d39f9c5..64631052 100644 --- a/app/models/commercial.rb +++ b/app/models/commercial.rb @@ -30,7 +30,7 @@ class Commercial < ApplicationRecord begin resource = OEmbed::Providers.get(url, maxwidth: 560, maxheight: 315) { html: resource.html.html_safe } - rescue StandardError => exception + rescue StandardError { html: iframe_fallback(url) } # { error: exception.message } end diff --git a/app/models/conference.rb b/app/models/conference.rb index 3e197280..44224ab7 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -35,6 +35,7 @@ # # index_conferences_on_organization_id (organization_id) # +# rubocop:disable Metrics/ClassLength class Conference < ApplicationRecord include RevisionCount require 'uri' @@ -1234,3 +1235,4 @@ class Conference < ApplicationRecord ] end end +# rubocop:enable Metrics/ClassLength diff --git a/app/models/event.rb b/app/models/event.rb index a2000fc9..d10ae1a1 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -19,6 +19,7 @@ # require_registration :boolean # start_time :datetime # state :string default("new"), not null +# submission_text :text # subtitle :string # title :string not null # week :integer @@ -74,6 +75,7 @@ class Event < ApplicationRecord before_create :generate_guid validate :abstract_limit + validate :submission_limit validate :before_end_of_conference, on: :create validates :title, presence: true validates :abstract, presence: true @@ -218,6 +220,10 @@ class Event < ApplicationRecord abstract.to_s.split.size end + def submission_word_count + submission_text.to_s.split.size + end + def self.get_state_color(state) COLORS[state.to_sym] || '#00FFFF' # azure end @@ -342,16 +348,28 @@ class Event < ApplicationRecord errors.add(:max_attendees, "cannot be more than the room's capacity (#{room.size})") if max_attendees && (max_attendees > room.size) end - def abstract_limit - # If we don't have an event type, there is no need to count anything - return unless event_type && abstract + def word_limit(field) + # If we don't have an event type or the requested field, don't count + return unless event_type && respond_to?(field) && self[field] - len = abstract.split.size + len = self[field].split.size + # TODO: Use different limits for different text fields + # Uncomment the two lines below this when the separate word limits are implemented. + # max_words = event_type["maximum_#{field}_length"] + # min_words = event_type["minimum_#{field}_length"] max_words = event_type.maximum_abstract_length min_words = event_type.minimum_abstract_length - errors.add(:abstract, "cannot have less than #{min_words} words") if len < min_words - errors.add(:abstract, "cannot have more than #{max_words} words") if len > max_words + errors.add(field.to_sym, "cannot have less than #{min_words} words") if len < min_words + errors.add(field.to_sym, "cannot have more than #{max_words} words") if len > max_words + end + + def abstract_limit + word_limit(:abstract) + end + + def submission_limit + word_limit(:submission_text) end # TODO: create a module to be mixed into model to perform same operation diff --git a/app/models/user.rb b/app/models/user.rb index 147c2fe6..9bd1bf80 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -305,7 +305,7 @@ class User < ApplicationRecord # TODO: Use a real authorization in the right place.... def manages_volunteers?(conference) organizer_roles = get_roles['organizer'] - organizer_roles&.include?(conference.short_title) # TODO or Volunteer Coorinator. + organizer_roles&.include?(conference.short_title) # TODO: or Volunteer Coorinator. end def registered diff --git a/app/serializers/event_serializer.rb b/app/serializers/event_serializer.rb index 56d18dba..9bf196f2 100644 --- a/app/serializers/event_serializer.rb +++ b/app/serializers/event_serializer.rb @@ -19,6 +19,7 @@ # require_registration :boolean # start_time :datetime # state :string default("new"), not null +# submission_text :text # subtitle :string # title :string not null # week :integer diff --git a/app/views/admin/events/_proposal.html.haml b/app/views/admin/events/_proposal.html.haml index 722387ac..05d48bb7 100644 --- a/app/views/admin/events/_proposal.html.haml +++ b/app/views/admin/events/_proposal.html.haml @@ -7,6 +7,8 @@ %small = @event.subtitle .btn-group.pull-right + - if @event.public + = link_to 'Preview', conference_program_proposal_path(@conference.short_title, @event.id), class: 'btn btn-mini btn-primary' = link_to 'Registrations', registrations_admin_conference_program_event_path(@conference.short_title, @event), class: 'btn btn-success' = link_to 'Edit', edit_admin_conference_program_event_path(@conference.short_title, @event), class: 'btn btn-mini btn-primary' @@ -111,6 +113,10 @@ %td %b Abstract %td= markdown(@event.abstract) + %tr + %td + %b Submission Description + %td= markdown(@event.submission_text) %tr %td %b Requirements diff --git a/app/views/mailbot/comment_template.text.erb b/app/views/mailbot/comment_template.text.erb index 0e7a8c35..d162d710 100644 --- a/app/views/mailbot/comment_template.text.erb +++ b/app/views/mailbot/comment_template.text.erb @@ -1,10 +1,83 @@ -Dear <%= @user.name %>, + + + + + + + + + + + + + + Email + + +
+
+ +
+
+

+
+
+
+ + Dear <%= @user.name %>, + + User <%= @comment.user.name %> posted a new comment for event <%= @event.title %> of <%= @conference.short_title %> . + + "<%= @comment.body %>" + + To reply to this comment, please go to <%= h(admin_conference_program_event_url(@conference.short_title, @event, only_path: false)) %> + + Best wishes, + <%= @conference.title %> Team + +
+
+

+
+ + diff --git a/app/views/mailbot/email_template.erb b/app/views/mailbot/email_template.erb new file mode 100644 index 00000000..429553be --- /dev/null +++ b/app/views/mailbot/email_template.erb @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + Email + + +
+
+ +
+ <% if !@logo.nil? %> + <%= image_tag(@logo, style: "display:block") %> + <% end %> +
+

+
+
+
+ + <%= @email_body %> + +
+
+

+
+ + diff --git a/app/views/mailbot/ticket_confirmation_template.text.erb b/app/views/mailbot/ticket_confirmation_template.text.erb index 08f5175a..0e4e54f8 100644 --- a/app/views/mailbot/ticket_confirmation_template.text.erb +++ b/app/views/mailbot/ticket_confirmation_template.text.erb @@ -1,8 +1,81 @@ -Dear <%= @user.name %>, + + + + + + + + + + + + + + Email + + +
+
+ +
+
+

+
+
+
+ + Dear <%= @user.name %>, + + Thanks! You have successfully booked <%= @ticket_purchase.quantity %> <%= @ticket_purchase.ticket.title %> ticket(s) for the event <%= @conference.title %>. Your transaction id is <%= @ticket_purchase.id %>. + + Please, find the ticket(s) pdf attached. + + Best wishes, + <%= @conference.title %> Team + +
+
+

+
+ + diff --git a/app/views/mailbot/young_thinkers_ticket_confirmation_template.text.erb b/app/views/mailbot/young_thinkers_ticket_confirmation_template.text.erb index e3e52c1f..6a4c7fb5 100644 --- a/app/views/mailbot/young_thinkers_ticket_confirmation_template.text.erb +++ b/app/views/mailbot/young_thinkers_ticket_confirmation_template.text.erb @@ -1,10 +1,83 @@ -Dear <%= @user.name %>, + + + + + + + + + + + + + + Email + + +
+
+ +
+
+

+
+
+
+ + Dear <%= @user.name %>, + + Thanks! You have successfully booked <%= @ticket_purchase.quantity %> <%= @ticket_purchase.ticket.title %> ticket(s) for the event <%= @conference.title %>. Your transaction id is <%= @ticket_purchase.id %>. + + Please, find the ticket(s) pdf attached. + + The SAP Young Thinkers team will reach out to you with information on how to participate in the event soon. In the meantime, you can check the event page (https://events.sap.com/yt-learning-festival-at-snapcon-2020/en/home) or send an email with your questions to youngthinkers@sap.com. + + Best wishes, + <%= @conference.title %> Team + +
+
+

+
+ + diff --git a/app/views/proposals/_proposal_form.html.haml b/app/views/proposals/_proposal_form.html.haml index f385bdeb..5b4e7079 100644 --- a/app/views/proposals/_proposal_form.html.haml +++ b/app/views/proposals/_proposal_form.html.haml @@ -13,7 +13,7 @@ = f.input :event_type_id, as: :select, collection: @conference.program.event_types.map {|type| ["#{type.title} - #{show_time(type.length)}", type.id, - data: { min_words: type.minimum_abstract_length, max_words: type.maximum_abstract_length }]}, + data: { min_words: type.minimum_abstract_length, max_words: type.maximum_abstract_length, help: type.description }]}, include_blank: false, label: 'Type', input_html: { class: 'select-help-toggle' } - if @program.languages.present? @@ -46,6 +46,22 @@ 250 words. + %br + + = f.input :submission_text, input_html: { rows: 5, data: { provide: 'markdown' }, placeholder: '' }, + hint: markdown_hint('Only conference organizers will read this.') + + %p + You have used + %span#submission-count #{@event.submission_word_count} + words. Submission descriptions must be between + %span#submission-minimum-word-count + 0 + and + %span#submission-maximum-word-count + 250 + words. + - if current_user.is_admin? or @program.cfp.enable_registrations? = f.inputs 'Enable pre-registration' do = f.input :require_registration, label: 'Require participants to register to your event' diff --git a/app/views/proposals/new.html.haml b/app/views/proposals/new.html.haml index b7fc22f5..bdd16e11 100644 --- a/app/views/proposals/new.html.haml +++ b/app/views/proposals/new.html.haml @@ -61,6 +61,22 @@ 250 words. + = f.input :submission_text, input_html: { rows: 5, data: { provide: 'markdown' } }, + hint: markdown_hint + + %p + You have used + %span#submission-count #{@event.submission_word_count} + words. Submission descriptions must be between + %span#submission-minimum-word-count + 0 + and + %span#submission-maximum-word-count + 250 + words. + + + - if @program.cfp.enable_registrations? = f.input :require_registration, label: 'Require participants to register to your event' diff --git a/config/application.rb b/config/application.rb index e3483d4a..18f917aa 100644 --- a/config/application.rb +++ b/config/application.rb @@ -65,5 +65,14 @@ module Osem config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=31536000' } config.active_job.queue_adapter = :delayed_job + + config.before_configuration do + env_file = File.join(Rails.root, 'config', 'local_env.yml') + if File.exist?(env_file) + YAML.safe_load(File.open(env_file)).each do |key, value| + ENV[key.to_s] = value + end + end + end end end diff --git a/config/database.yml b/config/database.yml index ecf07a0f..e8a3d502 100644 --- a/config/database.yml +++ b/config/database.yml @@ -11,8 +11,8 @@ default: &default encoding: <%= encoding %> host: <%= ENV['OSEM_DB_HOST'] || 'database' %> port: <%= ENV['OSEM_DB_PORT'] || '5432' %> - # username: <%= ENV['OSEM_DB_USER'] || 'postgres' %> - # password: <%= ENV['OSEM_DB_PASSWORD'] || 'mysecretpassword' %> + username: <%= ENV['OSEM_DB_USER'] || 'postgres' %> + password: <%= ENV['OSEM_DB_PASSWORD'] || 'mysecretpassword' %> database: <%= ENV['OSEM_DB_NAME'] || 'postgres' %> pool: 5 timeout: 50000 diff --git a/config/routes.rb b/config/routes.rb index 073bfe3e..e69232fe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,6 @@ Osem::Application.routes.draw do - mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development? + mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development? if ENV['OSEM_ICHAIN_ENABLED'] == 'true' devise_for :users, controllers: { registrations: :registrations } diff --git a/db/migrate/20180409170433_add_visible_to_tickets.rb b/db/migrate/20180409170433_add_visible_to_tickets.rb index 199f7a48..2286a065 100644 --- a/db/migrate/20180409170433_add_visible_to_tickets.rb +++ b/db/migrate/20180409170433_add_visible_to_tickets.rb @@ -2,7 +2,7 @@ class AddVisibleToTickets < ActiveRecord::Migration[5.0] def up add_column :tickets, :visible, :boolean, default: true Ticket.reset_column_information - Ticket.update_all(visible: true) # rubocop:disable Rails/SkipsModelValidations + Ticket.update_all(visible: true) end def down diff --git a/db/migrate/20210215213515_add_submission_text_to_events.rb b/db/migrate/20210215213515_add_submission_text_to_events.rb new file mode 100644 index 00000000..cf935e18 --- /dev/null +++ b/db/migrate/20210215213515_add_submission_text_to_events.rb @@ -0,0 +1,5 @@ +class AddSubmissionTextToEvents < ActiveRecord::Migration[5.2] + def change + add_column :events, :submission_text, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index d2898113..42c64634 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -258,6 +258,7 @@ ActiveRecord::Schema.define(version: 2021_03_03_052026) do t.integer "max_attendees" t.integer "comments_count", default: 0, null: false t.text "committee_review" + t.text "submission_text" end create_table "events_registrations", force: :cascade do |t| diff --git a/info.yml b/info.yml new file mode 100644 index 00000000..ab0afd5e --- /dev/null +++ b/info.yml @@ -0,0 +1,20 @@ +project: + name: 'snapcon' + owner: 'cs169' + teamId: '5' + identities: + pivotal: + url: 'https://www.pivotaltracker.com/n/projects/2487653' + heroku: + url: 'https://cs169l-sp2021-snapcon.herokuapp.com' + travis: + url: 'https://travis-ci.com/github/CactusPuppy/snapcon' + codeclimate: + url: 'https://codeclimate.com/github/CactusPuppy/snapcon' + members: + member1: + name: 'Coby' + surname: 'Zhong' + githubUsername: 'CactusPuppy' + pivotalUsername: 'cobyzhong' + herokuEmail: 'coby.zhong@berkeley.edu' diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 3c9cded7..bed74e53 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -13,24 +13,24 @@ namespace :db do end end - desc "Import a given file into the database" + desc 'Import a given file into the database' task :import, [:path] => :environment do |_t, args| dump_path = args.path connection_config = ActiveRecord::Base.connection_config case connection_config[:adapter] - when "postgresql" + when 'postgresql' system("PGPASSWORD=#{connection_config[:password]} pg_restore " \ - "--verbose --clean --no-acl --no-owner " \ + '--verbose --clean --no-acl --no-owner ' \ "--username=#{connection_config[:username]} " \ "-d #{connection_config[:database]} #{dump_path}") - when "mysql", "mysql2" + when 'mysql', 'mysql2' system("mysql -u #{connection_config[:username]} " \ "-p#{connection_config[:password]} " \ "#{connection_config[:database]} < #{dump_path}") else - raise NotImplementedError, "An importer hasn't been implemented for: " \ - "#{connection_config[:adapter]}" + raise NotImplementedError.new("An importer hasn't been implemented for: " \ + "#{connection_config[:adapter]}") end end end diff --git a/lib/tasks/registrations.rake b/lib/tasks/registrations.rake index f94bb3ae..709e13e0 100644 --- a/lib/tasks/registrations.rake +++ b/lib/tasks/registrations.rake @@ -1,17 +1,17 @@ # frozen_string_literal: true namespace :registrations do - desc "Create missing registrations for those how have a registration ticket." - task :create_missing, [:conference] => :environment do |t, args| + desc 'Create missing registrations for those how have a registration ticket.' + task :create_missing, [:conference] => :environment do |_t, args| - fail 'Please supply a conference short name.' unless args.conference + raise 'Please supply a conference short name.' unless args.conference conf = Conference.find_by(short_title: args.conference) # Check if a user is found based on the supplied email address - fail "Coud not find conference #{args.conference}" unless conf + raise "Coud not find conference #{args.conference}" unless conf purchases = conf.ticket_purchases.where(ticket: conf.registration_tickets, paid: true) - unregistered = purchases.select { |tp| !conf.user_registered?(tp.user) } + unregistered = purchases.reject { |tp| conf.user_registered?(tp.user) } puts "Found #{unregistered.count} unregistered users for #{purchases.count} ticket purchases." puts "There are currently #{conf.participants.count} registered users." @@ -19,17 +19,17 @@ namespace :registrations do puts "Creating registration for #{tp.user.email}" Registration.create(user: tp.user, conference: conf) end - puts "Done." + puts 'Done.' end - desc "Show User emails who have not paid, but did register" - task :list_unpaid, [:conference] => :environment do |t, args| + desc 'Show User emails who have not paid, but did register' + task :list_unpaid, [:conference] => :environment do |_t, args| - fail 'Please supply a conference short name.' unless args.conference + raise 'Please supply a conference short name.' unless args.conference conf = Conference.find_by(short_title: args.conference) # Check if a user is found based on the supplied email address - fail "Coud not find conference #{args.conference}" unless conf + raise "Coud not find conference #{args.conference}" unless conf registered = conf.participants unpaid = registered.select do |user| @@ -41,6 +41,6 @@ namespace :registrations do unpaid.each do |user| puts "'#{user.name}'<#{user.email}>, " end - puts "" + puts '' end end diff --git a/spec/factories/events.rb b/spec/factories/events.rb index 1ff915d6..2f68c311 100644 --- a/spec/factories/events.rb +++ b/spec/factories/events.rb @@ -19,6 +19,7 @@ # require_registration :boolean # start_time :datetime # state :string default("new"), not null +# submission_text :text # subtitle :string # title :string not null # week :integer diff --git a/spec/features/base_controller_spec.rb b/spec/features/base_controller_spec.rb index 57d5be94..6bfd0622 100644 --- a/spec/features/base_controller_spec.rb +++ b/spec/features/base_controller_spec.rb @@ -13,6 +13,10 @@ feature 'BaseController' do describe 'GET #verify_user_admin' do context 'when user is a guest' do + before(:each) do + sign_out + end + it 'redirects to sign in page' do visit admin_conferences_path expect(current_path).to eq new_user_session_path diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index 4d25e9ce..15c23fcb 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -27,6 +27,21 @@ feature Event do sign_in organizer end + scenario 'can preview a proposal if it is public', feature: true, js: true do + visit admin_conference_program_event_path(conference.short_title, @event) + expect(page).to have_selector(:link_or_button, 'Preview') + click_link 'Preview' + expect(current_path).to eq(conference_program_proposal_path(conference.short_title, @event.id)) + end + + scenario 'cannot preview a proposal if it is not public', feature: true, js: true do + event = create(:event, program: conference.program, title: 'Example Proposal') + event.public = false + event.save! + visit admin_conference_program_event_path(conference.short_title, event) + expect(page).to_not have_selector(:link_or_button, 'Preview') + end + scenario 'rejects a proposal', feature: true, js: true do visit admin_conference_program_events_path(conference.short_title) expect(page).to have_content 'Example Proposal' @@ -82,6 +97,7 @@ feature Event do fill_in 'event_title', with: 'Example Proposal' select('Example Event Type', from: 'event[event_type_id]') fill_in 'event_abstract', with: 'Lorem ipsum abstract' + fill_in 'event_submission_text', with: 'Lorem ipsum submission' click_button 'Submit Proposal' page.find('#flash') @@ -123,6 +139,9 @@ feature Event do fill_in 'event_abstract', with: 'Lorem ipsum abstract' expect(page).to have_text('You have used 3 words') + fill_in 'event_submission_text', with: 'Lorem ipsum submission_text' + expect(page).to have_text('Submission description') + click_link 'Do you require something special?' fill_in 'event_description', with: 'Lorem ipsum description' diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index b54a0bd2..4864a21b 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -79,7 +79,7 @@ describe ApplicationHelper, type: :helper do end end - describe 'navigation link titke text' do + describe 'navigation link title text' do it 'should default to OSEM' do ENV.delete('OSEM_NAME') expect(nav_link_text(nil)).to match 'OSEM' diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index c1cf5344..35866d6b 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe EventsHelper, type: :helper do let(:conference) { create(:conference) } let(:event) { create(:event_full, program: conference.program) } + let(:event_schedule) { create(:event_schedule) } let(:my_vote) { 3 } let(:max_rating) { 5 } let(:fraction) { my_vote.to_s + '/' + max_rating.to_s } @@ -28,6 +29,27 @@ describe EventsHelper, type: :helper do end end + describe '#canceled_replacement_event_label' do + describe 'returns nothing' do + it "when the event isn't cancelled and is not a replacement" do + event.state = 'confirmed' + expect(canceled_replacement_event_label(event, nil, 'text-class')).to eq nil + end + + it 'when the event is canceled' do + event.state = 'canceled' + expect(canceled_replacement_event_label(event, nil, 'test-class')).to eq 'CANCELED' + end + + it 'when the event is a replacement but is not canceled' do + event.state = 'confirmed' + allow(event_schedule).to receive(:replacement?) { true } + expect(canceled_replacement_event_label(event, event_schedule, 'tent-class')).to eq 'REPLACEMENT' + end + + end + end + describe '#rating_tooltip' do let(:vote_count) { pluralize(event.voters.length, 'vote') } diff --git a/spec/mailers/mailbot_spec.rb b/spec/mailers/mailbot_spec.rb index 1cbdd8e7..1f739997 100644 --- a/spec/mailers/mailbot_spec.rb +++ b/spec/mailers/mailbot_spec.rb @@ -23,7 +23,7 @@ describe Mailbot do end it 'assigns the email body' do - expect(mail.body).to eq 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit' + expect(mail.body).to include 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit' end it 'delivers the email' do diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 2a934faa..e2c6f584 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -19,6 +19,7 @@ # require_registration :boolean # start_time :datetime # state :string default("new"), not null +# submission_text :text # subtitle :string # title :string not null # week :integer @@ -110,6 +111,35 @@ describe Event do end end + describe '#submission_limit' do + before :each do + event.event_type.maximum_abstract_length = 3 + event.event_type.minimum_abstract_length = 2 + end + + context 'is invalid' do + it 'when submission text is too long' do + event.submission_text = 'four too many words' + expect(event.valid?).to eq false + expect(event.errors[:submission_text]).to eq ['cannot have more than 3 words'] + end + + it 'when submission text is too short' do + event.submission_text = 'word' + expect(event.valid?).to eq false + expect(event.errors[:submission_text]).to eq ['cannot have less than 2 words'] + end + end + + context 'is valid' do + it 'when submission text is within limts' do + event.abstract = 'the magic three' + expect(event.valid?).to eq true + expect(event.errors.size).to eq 0 + end + end + end + describe '#before_end_of_conference' do context 'is invalid' do it 'when event is created after the conference end_date, and returns an error message' do diff --git a/spec/serializers/event_serializer_spec.rb b/spec/serializers/event_serializer_spec.rb index 4dafc33a..3ad68cad 100644 --- a/spec/serializers/event_serializer_spec.rb +++ b/spec/serializers/event_serializer_spec.rb @@ -19,6 +19,7 @@ # require_registration :boolean # start_time :datetime # state :string default("new"), not null +# submission_text :text # subtitle :string # title :string not null # week :integer diff --git a/spec/support/omniauth_macros.rb b/spec/support/omniauth_macros.rb index 25c6f039..2696f005 100644 --- a/spec/support/omniauth_macros.rb +++ b/spec/support/omniauth_macros.rb @@ -88,6 +88,10 @@ module OmniauthMacros # account is available for every supported omniauth provider. # These must be identical to the ones in /config/environments/development.rb # Remember to keep them in sync with development.rb + # + # Note that the method length check is disabled to allow for better formatting + # of the user params. + # rubocop:disable Metrics/MethodLength def mock_auth_accounts OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new( @@ -164,4 +168,5 @@ module OmniauthMacros } ) end + # rubocop:enable Metrics/MethodLength end