Merge master, resolve schema conflicts

This commit is contained in:
Michael Ball 2021-03-02 22:35:01 -08:00
commit 04154020a5
42 changed files with 757 additions and 131 deletions

81
.gitignore vendored
View file

@ -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?--*

View file

@ -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)

View file

@ -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)
<a href="https://heroku.com/deploy?template=https://github.com/openSUSE/osem/tree/v1.0">
<img src="https://www.herokucdn.com/deploy/button.svg" height="20px" alt="Deploy">
</a>
<a href="https://www.pivotaltracker.com/n/projects/2487653">
<img src="https://www.pivotaltracker.com/marketing_assets/shared_home/tracker-4679313e699d9ba696371344953de96c81d207d967a43f121d353391c81c9ba7.svg" height="20px" alt="Pivotal Tracker">
</a>
---
[![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.

View file

@ -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 */

View file

@ -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?

View file

@ -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) &&

View file

@ -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: []
)

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,10 +1,83 @@
Dear <%= @user.name %>,
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- CSS -->
<link rel="stylesheet" href="main.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Montserrat:100,200,300,400,500,600,700,800" rel="stylesheet">
<style>
html {
scroll-behavior: smooth;
}
User <%= @comment.user.name %> posted a new comment for event <%= @event.title %> of <%= @conference.short_title %> .
body {
background: #fff;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.06);
color: #000;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
line-height: 1.5;
margin: 0 auto;
}
"<%= @comment.body %>"
h1,
h3,
h4,
h5,
h6 {
font-weight: 400;
line-height: 1.3;
}
p {
color: #0B3559;
font-weight: 400;
line-height: 2;
}
To reply to this comment, please go to <%= h(admin_conference_program_event_url(@conference.short_title, @event, only_path: false)) %>
#border {
background-color: #0B3559;
padding: 25px;
color:#fff;
}
Best wishes,
<%= @conference.title %> Team
#content {
background-color: #fff;
padding: 100px;
color: #0B3559;
}
</style>
<!-- Page Title -->
<title>Email</title>
</head>
<body>
<div id="border">
<div class="row">
<!-- Uncomment below to get logo -->
<div class="col-md-2">
</div>
<div class="col-md-10"><h1></h1></div>
</div>
</div>
<div id="content">
<span style="white-space: pre-line">
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
</span>
</div>
<div id="border">
<h1></h1>
</div>
</body>
</html>

View file

@ -0,0 +1,77 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- CSS -->
<link rel="stylesheet" href="main.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Montserrat:100,200,300,400,500,600,700,800" rel="stylesheet">
<style>
html {
scroll-behavior: smooth;
}
body {
background: #fff;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.06);
color: #000;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
line-height: 1.5;
margin: 0 auto;
}
h1,
h3,
h4,
h5,
h6 {
font-weight: 400;
line-height: 1.3;
}
p {
color: #0B3559;
font-weight: 400;
line-height: 2;
}
#border {
background-color: #0B3559;
padding: 25px;
color:#fff;
}
#content {
background-color: #fff;
padding: 100px;
color: #0B3559;
}
</style>
<!-- Page Title -->
<title>Email</title>
</head>
<body>
<div id="border">
<div class="row">
<!-- Uncomment below to get logo -->
<div class="col-md-2">
<% if !@logo.nil? %>
<%= image_tag(@logo, style: "display:block") %>
<% end %>
</div>
<div class="col-md-10"><h1></h1></div>
</div>
</div>
<div id="content">
<span style="white-space: pre-line">
<%= @email_body %>
</span>
</div>
<div id="border">
<h1></h1>
</div>
</body>
</html>

View file

@ -1,8 +1,81 @@
Dear <%= @user.name %>,
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- CSS -->
<link rel="stylesheet" href="main.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Montserrat:100,200,300,400,500,600,700,800" rel="stylesheet">
<style>
html {
scroll-behavior: smooth;
}
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 %>.
body {
background: #fff;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.06);
color: #000;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
line-height: 1.5;
margin: 0 auto;
}
Please, find the ticket(s) pdf attached.
h1,
h3,
h4,
h5,
h6 {
font-weight: 400;
line-height: 1.3;
}
p {
color: #0B3559;
font-weight: 400;
line-height: 2;
}
Best wishes,
<%= @conference.title %> Team
#border {
background-color: #0B3559;
padding: 25px;
color:#fff;
}
#content {
background-color: #fff;
padding: 100px;
color: #0B3559;
}
</style>
<!-- Page Title -->
<title>Email</title>
</head>
<body>
<div id="border">
<div class="row">
<!-- Uncomment below to get logo -->
<div class="col-md-2">
</div>
<div class="col-md-10"><h1></h1></div>
</div>
</div>
<div id="content">
<span style="white-space: pre-line">
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
</span>
</div>
<div id="border">
<h1></h1>
</div>
</body>
</html>

View file

@ -1,10 +1,83 @@
Dear <%= @user.name %>,
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- CSS -->
<link rel="stylesheet" href="main.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Montserrat:100,200,300,400,500,600,700,800" rel="stylesheet">
<style>
html {
scroll-behavior: smooth;
}
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 %>.
body {
background: #fff;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.06);
color: #000;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
line-height: 1.5;
margin: 0 auto;
}
Please, find the ticket(s) pdf attached.
h1,
h3,
h4,
h5,
h6 {
font-weight: 400;
line-height: 1.3;
}
p {
color: #0B3559;
font-weight: 400;
line-height: 2;
}
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.
#border {
background-color: #0B3559;
padding: 25px;
color:#fff;
}
Best wishes,
<%= @conference.title %> Team
#content {
background-color: #fff;
padding: 100px;
color: #0B3559;
}
</style>
<!-- Page Title -->
<title>Email</title>
</head>
<body>
<div id="border">
<div class="row">
<!-- Uncomment below to get logo -->
<div class="col-md-2">
</div>
<div class="col-md-10"><h1></h1></div>
</div>
</div>
<div id="content">
<span style="white-space: pre-line">
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
</span>
</div>
<div id="border">
<h1></h1>
</div>
</body>
</html>

View file

@ -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'

View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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 }

View file

@ -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

View file

@ -0,0 +1,5 @@
class AddSubmissionTextToEvents < ActiveRecord::Migration[5.2]
def change
add_column :events, :submission_text, :text
end
end

View file

@ -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|

20
info.yml Normal file
View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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'

View file

@ -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'

View file

@ -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 '<span class="label label-danger test-class">CANCELED</span>'
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 '<span class="label label-info tent-class">REPLACEMENT</span>'
end
end
end
describe '#rating_tooltip' do
let(:vote_count) { pluralize(event.voters.length, 'vote') }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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