diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
index 7b80c2d84..93b401d76 100644
--- a/.haml-lint_todo.yml
+++ b/.haml-lint_todo.yml
@@ -46,6 +46,7 @@ linters:
- "app/views/admin/events/index.html.haml"
- "app/views/admin/tracks/index.html.haml"
- "app/views/admin/tracks/show.html.haml"
+ - "app/views/admin/registrations/index.html.haml"
- "app/views/admin/versions/_object_desc_and_link.html.haml"
- "app/views/conference_registrations/show.html.haml"
- "app/views/layouts/_admin_sidebar.html.haml"
diff --git a/Gemfile.lock b/Gemfile.lock
index 325221c9e..d874769dd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -11,47 +11,47 @@ GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
- actioncable (7.0.8.1)
- actionpack (= 7.0.8.1)
- activesupport (= 7.0.8.1)
+ actioncable (7.0.8.4)
+ actionpack (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (7.0.8.1)
- actionpack (= 7.0.8.1)
- activejob (= 7.0.8.1)
- activerecord (= 7.0.8.1)
- activestorage (= 7.0.8.1)
- activesupport (= 7.0.8.1)
+ actionmailbox (7.0.8.4)
+ actionpack (= 7.0.8.4)
+ activejob (= 7.0.8.4)
+ activerecord (= 7.0.8.4)
+ activestorage (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
- actionmailer (7.0.8.1)
- actionpack (= 7.0.8.1)
- actionview (= 7.0.8.1)
- activejob (= 7.0.8.1)
- activesupport (= 7.0.8.1)
+ actionmailer (7.0.8.4)
+ actionpack (= 7.0.8.4)
+ actionview (= 7.0.8.4)
+ activejob (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
- actionpack (7.0.8.1)
- actionview (= 7.0.8.1)
- activesupport (= 7.0.8.1)
+ actionpack (7.0.8.4)
+ actionview (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (7.0.8.1)
- actionpack (= 7.0.8.1)
- activerecord (= 7.0.8.1)
- activestorage (= 7.0.8.1)
- activesupport (= 7.0.8.1)
+ actiontext (7.0.8.4)
+ actionpack (= 7.0.8.4)
+ activerecord (= 7.0.8.4)
+ activestorage (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (7.0.8.1)
- activesupport (= 7.0.8.1)
+ actionview (7.0.8.4)
+ activesupport (= 7.0.8.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
@@ -61,22 +61,22 @@ GEM
activemodel (>= 4.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
- activejob (7.0.8.1)
- activesupport (= 7.0.8.1)
+ activejob (7.0.8.4)
+ activesupport (= 7.0.8.4)
globalid (>= 0.3.6)
- activemodel (7.0.8.1)
- activesupport (= 7.0.8.1)
- activerecord (7.0.8.1)
- activemodel (= 7.0.8.1)
- activesupport (= 7.0.8.1)
- activestorage (7.0.8.1)
- actionpack (= 7.0.8.1)
- activejob (= 7.0.8.1)
- activerecord (= 7.0.8.1)
- activesupport (= 7.0.8.1)
+ activemodel (7.0.8.4)
+ activesupport (= 7.0.8.4)
+ activerecord (7.0.8.4)
+ activemodel (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
+ activestorage (7.0.8.4)
+ actionpack (= 7.0.8.4)
+ activejob (= 7.0.8.4)
+ activerecord (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
- activesupport (7.0.8.1)
+ activesupport (7.0.8.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@@ -113,7 +113,7 @@ GEM
bootstrap-switch-rails (3.3.3)
bootstrap3-datetimepicker-rails (4.17.47)
momentjs-rails (>= 2.8.1)
- builder (3.2.4)
+ builder (3.3.0)
bullet (7.0.2)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
@@ -158,7 +158,7 @@ GEM
rest-client (>= 2.0.0)
cocoon (1.2.15)
colorize (1.1.0)
- concurrent-ruby (1.2.3)
+ concurrent-ruby (1.3.3)
countable-rails (0.0.1)
railties (>= 3.1)
crack (1.0.0)
@@ -235,7 +235,7 @@ GEM
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
- erubi (1.12.0)
+ erubi (1.13.0)
execjs (2.9.1)
factory_bot (6.4.6)
activesupport (>= 5.0.0)
@@ -290,7 +290,7 @@ GEM
httparty (0.21.0)
mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2)
- i18n (1.14.4)
+ i18n (1.14.5)
concurrent-ruby (~> 1.0)
i18n_data (0.17.1)
simple_po_parser (~> 1.1)
@@ -349,7 +349,7 @@ GEM
rake
mini_magick (4.12.0)
mini_mime (1.1.5)
- minitest (5.22.3)
+ minitest (5.24.1)
momentjs-rails (2.29.4.1)
railties (>= 3.1)
monetize (1.13.0)
@@ -379,11 +379,11 @@ GEM
next_rails (1.3.0)
colorize (>= 0.8.1)
nio4r (2.7.0)
- nokogiri (1.16.3-arm64-darwin)
+ nokogiri (1.16.6-arm64-darwin)
racc (~> 1.4)
- nokogiri (1.16.3-x86_64-darwin)
+ nokogiri (1.16.6-x86_64-darwin)
racc (~> 1.4)
- nokogiri (1.16.3-x86_64-linux)
+ nokogiri (1.16.6-x86_64-linux)
racc (~> 1.4)
oauth2 (2.0.9)
faraday (>= 0.17.3, < 3.0)
@@ -466,7 +466,7 @@ GEM
public_suffix (5.0.4)
puma (6.4.2)
nio4r (~> 2.0)
- racc (1.7.3)
+ racc (1.8.0)
rack (2.2.9)
rack-openid (1.4.2)
rack (>= 1.1.0)
@@ -475,20 +475,20 @@ GEM
rack
rack-test (2.1.0)
rack (>= 1.3)
- rails (7.0.8.1)
- actioncable (= 7.0.8.1)
- actionmailbox (= 7.0.8.1)
- actionmailer (= 7.0.8.1)
- actionpack (= 7.0.8.1)
- actiontext (= 7.0.8.1)
- actionview (= 7.0.8.1)
- activejob (= 7.0.8.1)
- activemodel (= 7.0.8.1)
- activerecord (= 7.0.8.1)
- activestorage (= 7.0.8.1)
- activesupport (= 7.0.8.1)
+ rails (7.0.8.4)
+ actioncable (= 7.0.8.4)
+ actionmailbox (= 7.0.8.4)
+ actionmailer (= 7.0.8.4)
+ actionpack (= 7.0.8.4)
+ actiontext (= 7.0.8.4)
+ actionview (= 7.0.8.4)
+ activejob (= 7.0.8.4)
+ activemodel (= 7.0.8.4)
+ activerecord (= 7.0.8.4)
+ activestorage (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
bundler (>= 1.15.0)
- railties (= 7.0.8.1)
+ railties (= 7.0.8.4)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
@@ -503,9 +503,9 @@ GEM
rails-i18n (7.0.8)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
- railties (7.0.8.1)
- actionpack (= 7.0.8.1)
- activesupport (= 7.0.8.1)
+ railties (7.0.8.4)
+ actionpack (= 7.0.8.4)
+ activesupport (= 7.0.8.4)
method_source
rake (>= 12.2)
thor (~> 1.0)
@@ -526,7 +526,8 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
- rexml (3.2.6)
+ rexml (3.3.2)
+ strscan
rolify (6.0.1)
rqrcode (2.2.0)
chunky_png (~> 1.0)
@@ -646,6 +647,7 @@ GEM
dante (>= 0.2.0)
multi_json (~> 1.0)
stripe (> 5, < 6)
+ strscan (3.1.0)
sys-uname (1.2.2)
ffi (~> 1.1)
sysexits (1.2.0)
diff --git a/app/assets/javascripts/osem-schedule.js b/app/assets/javascripts/osem-schedule.js
index 45df23908..b38ae2cc8 100644
--- a/app/assets/javascripts/osem-schedule.js
+++ b/app/assets/javascripts/osem-schedule.js
@@ -15,10 +15,10 @@ var Schedule = {
schedule_id = schedule_id_param;
},
remove: function(element) {
- var e = $("#" + element);
- var event_schedule_id = e.attr("event_schedule_id");
- if(event_schedule_id != null){
- var my_url = url + '/' + event_schedule_id;
+ var e = $("#" + element);
+ var event_schedule_id = parseInt(e.attr("event_schedule_id"));
+ if(event_schedule_id > 0) {
+ var my_url = `${url}/${event_schedule_id}`;
var success_callback = function(data) {
e.attr("event_schedule_id", null);
e.appendTo($(".unscheduled-events"));
@@ -41,18 +41,18 @@ var Schedule = {
},
add: function (previous_parent, new_parent, event) {
event.appendTo(new_parent);
- var event_schedule_id = event.attr("event_schedule_id");
+ // Event Schedule Id could be an empty string.
+ var event_schedule_id = parseInt(event.attr("event_schedule_id"));
var my_url = url;
var type = 'POST';
var params = { event_schedule: {
room_id: new_parent.attr("room_id"),
start_time: (new_parent.attr("date") + ' ' + new_parent.attr("hour"))
}};
- if(event_schedule_id != null){
+ if (event_schedule_id > 0) {
type = 'PUT';
- my_url += ('/' + event_schedule_id);
- }
- else{
+ my_url += `/${event_schedule_id}`;
+ } else {
params['event_schedule']['event_id'] = event.attr("event_id");
params['event_schedule']['schedule_id'] = schedule_id;
}
@@ -80,6 +80,32 @@ $(document).ready( function() {
$('.unscheduled-events .schedule-event-delete-button').hide();
$('.non_schedulable .schedule-event-delete-button').hide();
+ $('#current-event-btn').on('click', function() {
+ var now = new Date();
+ var closestEvent = null;
+ var smallestDiff = Infinity;
+
+ $('.event-item').each(function() {
+ let $event = $(this), eventTimeStr = $event.data('time');
+
+ if (!eventTimeStr) { return; }
+
+ var eventTime = new Date(eventTimeStr);
+ var diff = Math.abs(eventTime - now);
+ if (diff < smallestDiff) {
+ smallestDiff = diff;
+ closestEvent = $event;
+ }
+ });
+
+ if (closestEvent) {
+ // Instead of relying on hash it's probably better to scroll using javascript
+ // Since the users and click button->scroll->click again, which won't re-scroll
+ $('.highlighted').removeClass('highlighted');
+ $(closestEvent).addClass('highlighted').get(0).scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+ });
+
// set events as draggable
$('.schedule-event').not('.non_schedulable').draggable({
snap: '.schedule-room-slot',
diff --git a/app/assets/javascripts/osem-switch.js b/app/assets/javascripts/osem-switch.js
index 5e8c47921..b4517c1cb 100644
--- a/app/assets/javascripts/osem-switch.js
+++ b/app/assets/javascripts/osem-switch.js
@@ -1,7 +1,5 @@
function checkboxSwitch(selector){
- $(selector).bootstrapSwitch(
-
- );
+ $(selector).bootstrapSwitch();
$(selector).on('switchChange.bootstrapSwitch', function(event, state) {
var url = $(this).attr('url') + state;
diff --git a/app/assets/stylesheets/osem-schedule.scss b/app/assets/stylesheets/osem-schedule.scss
index f722a41e9..6a1fcc52c 100644
--- a/app/assets/stylesheets/osem-schedule.scss
+++ b/app/assets/stylesheets/osem-schedule.scss
@@ -185,3 +185,10 @@
h3.event-panel-title small {
line-height: 1.4;
}
+
+#current-event-btn {
+ position: fixed;
+ bottom: 40px;
+ right: 40px;
+ z-index: 1000;
+}
diff --git a/app/controllers/admin/conferences_controller.rb b/app/controllers/admin/conferences_controller.rb
index fc149b54f..17d2bc131 100644
--- a/app/controllers/admin/conferences_controller.rb
+++ b/app/controllers/admin/conferences_controller.rb
@@ -181,7 +181,7 @@ def conference_params
:vpositions_attributes, :use_volunteers, :color,
:sponsorship_levels_attributes, :sponsors_attributes,
:registration_limit, :organization_id, :ticket_layout,
- :booth_limit, :custom_css)
+ :booth_limit, :custom_css, :registered_attendees_message)
end
end
end
diff --git a/app/controllers/admin/event_types_controller.rb b/app/controllers/admin/event_types_controller.rb
index 0cd2eed20..f0d7656d9 100644
--- a/app/controllers/admin/event_types_controller.rb
+++ b/app/controllers/admin/event_types_controller.rb
@@ -50,7 +50,7 @@ def destroy
def event_type_params
params.require(:event_type).permit(:title, :length, :minimum_abstract_length, :maximum_abstract_length,
- :submission_template, :color, :conference_id, :description)
+ :submission_template, :color, :conference_id, :description, :enable_public_submission)
end
end
end
diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb
index adf1bde7a..939b4d967 100644
--- a/app/controllers/admin/events_controller.rb
+++ b/app/controllers/admin/events_controller.rb
@@ -182,6 +182,17 @@ def toggle_attendance
end
end
+ def destroy
+ @event = Event.find(params[:id])
+ if @event.destroy
+ flash[:notice] = 'Event successfully deleted.'
+ redirect_to admin_conference_program_events_path(@conference.short_title)
+ else
+ flash[:alert] = 'Event could not be deleted.'
+ redirect_to admin_conference_program_event_path(@conference.short_title, @event)
+ end
+ end
+
private
def event_params
diff --git a/app/controllers/admin/schedules_controller.rb b/app/controllers/admin/schedules_controller.rb
index f3176245c..6e716caaf 100644
--- a/app/controllers/admin/schedules_controller.rb
+++ b/app/controllers/admin/schedules_controller.rb
@@ -4,6 +4,7 @@ module Admin
class SchedulesController < Admin::BaseController
# By authorizing 'conference' resource, we can ensure there will be no unauthorized access to
# the schedule of a conference, which should not be accessed in the first place
+ before_action :set_conference
load_and_authorize_resource :conference, find_by: :short_title
load_and_authorize_resource :program, through: :conference, singleton: true
load_and_authorize_resource :schedule, through: :program, except: %i[new create]
@@ -68,8 +69,63 @@ def destroy
end
end
+ def upload_csv
+ authorize! :update, @conference
+ return flash[:alert] = 'No file was attached!' unless file_present?
+
+ if process_csv
+ flash[:notice] = 'Schedule uploaded successfully!'
+ else
+ flash[:alert] = 'Failed to process CSV file.'
+ end
+
+ redirect_to admin_conference_schedules_path(@conference)
+ end
+
private
+ def set_conference
+ @conference = Conference.find_by!(short_title: params[:conference_id])
+ end
+
+ def file_present?
+ params[:schedule] && params[:schedule][:file].present?
+ end
+
+ def process_csv
+ file = params[:schedule][:file]
+ CSV.foreach(file.path, headers: true) do |row|
+ process_row(row)
+ end
+ true
+ rescue StandardError => e
+ Rails.logger.error "CSV Processing Error: #{e.message}"
+ false
+ end
+
+ def process_row(row)
+ event_date = parse_date(row['Date'])
+ event_time = parse_time(row['Start_Time'])
+ event_start_time = combine_datetime(event_date, event_time)
+
+ room = Room.find_or_create_by(name: row['Room'])
+ event = Event.find_by(id: row['Event_ID'])
+
+ event&.update(start_time: event_start_time, room: room)
+ end
+
+ def parse_date(date_str)
+ Date.strptime(date_str, '%m/%d/%y')
+ end
+
+ def parse_time(time_str)
+ Time.parse(time_str)
+ end
+
+ def combine_datetime(date, time)
+ DateTime.new(date.year, date.month, date.day, time.hour, time.min, time.sec, time.zone)
+ end
+
def schedule_params
params.require(:schedule).permit(:track_id) if params[:schedule]
end
diff --git a/app/controllers/admin/splashpages_controller.rb b/app/controllers/admin/splashpages_controller.rb
index 7e50bb22b..56ead72df 100644
--- a/app/controllers/admin/splashpages_controller.rb
+++ b/app/controllers/admin/splashpages_controller.rb
@@ -51,7 +51,8 @@ def splashpage_params
:include_venue, :include_registrations,
:include_tickets, :include_lodgings,
:include_sponsors, :include_social_media,
- :include_booths, :include_happening_now)
+ :include_booths, :include_happening_now,
+ :include_committee)
end
end
end
diff --git a/app/controllers/admin/venue_commercials_controller.rb b/app/controllers/admin/venue_commercials_controller.rb
index 11a87d199..753817fd0 100644
--- a/app/controllers/admin/venue_commercials_controller.rb
+++ b/app/controllers/admin/venue_commercials_controller.rb
@@ -38,7 +38,7 @@ def destroy
end
def render_commercial
- result = Commercial.render_from_url(params[:url])
+ result = Commercial.render_from_url(params[:url], params[:title])
if result[:error]
render plain: result[:error], status: :bad_request
else
diff --git a/app/controllers/conference_registrations_controller.rb b/app/controllers/conference_registrations_controller.rb
index 8df31ea05..b20a0fe84 100644
--- a/app/controllers/conference_registrations_controller.rb
+++ b/app/controllers/conference_registrations_controller.rb
@@ -29,11 +29,16 @@ def new
end
def show
- @total_price = Ticket.total_price_user(@conference, current_user, paid: true)
- @tickets = current_user.ticket_purchases.by_conference(@conference).paid
- @total_price_per_ticket = @tickets.group(:ticket_id).sum('amount_paid * quantity')
- @ticket_payments = @tickets.group_by(&:ticket_id)
- @total_quantity = @tickets.group(:ticket_id).sum(:quantity)
+ @purchases = current_user.ticket_purchases.by_conference(@conference).paid
+ summed_per_ticket_per_currency = @purchases.group(:ticket_id, :currency).sum('amount_paid_cents * quantity')
+ @total_price_per_ticket_per_currency = summed_per_ticket_per_currency.each_with_object({}) do |((ticket_id, currency), amount), hash|
+ hash[[ticket_id, currency]] = Money.new(amount, currency)
+ end
+ @total_quantity = @purchases.group(:ticket_id, :currency).sum(:quantity)
+ sum_total_currency = @purchases.group(:currency).sum('amount_paid_cents * quantity')
+ @total_price_per_currency = sum_total_currency.each_with_object({}) do |(currency, amount), hash|
+ hash[currency] = Money.new(amount, currency)
+ end
end
def edit; end
diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb
index 3eb6fd6f8..492516829 100644
--- a/app/controllers/conferences_controller.rb
+++ b/app/controllers/conferences_controller.rb
@@ -14,6 +14,7 @@ def index
end
# rubocop:disable Metrics/CyclomaticComplexity
+ # rubocop:disable Metrics/PerceivedComplexity
def show
# load conference with header content
@conference = Conference.unscoped.eager_load(
@@ -32,6 +33,7 @@ def show
# User messages at the top of the page.
@unpaid_tickets = current_user_has_unpaid_tickets?
@user_needs_to_register = current_user_needs_to_register?
+ @user_registered = current_user_registered?
@image_url = @splashpage.banner_photo_url || @conference.picture_url
@@ -65,8 +67,12 @@ def show
).order('sponsorship_levels.position ASC', 'sponsors.name')
@sponsors = @conference.sponsors
end
+ if @splashpage.include_committee?
+ @organizers = User.with_role(:organizer, @conference)
+ end
end
# rubocop:enable Metrics/CyclomaticComplexity
+ # rubocop:enable Metrics/PerceivedComplexity
def calendar
respond_to do |format|
@@ -136,6 +142,10 @@ def current_user_needs_to_register?
current_user_tickets.where(ticket: @conference.registration_tickets).paid.any?
end
+ def current_user_registered?
+ current_user && @conference.user_registered?(current_user)
+ end
+
def current_user_has_unpaid_tickets?
current_user && current_user_tickets.unpaid.any?
end
diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb
index 94d8d9859..17a4b94b2 100644
--- a/app/controllers/payments_controller.rb
+++ b/app/controllers/payments_controller.rb
@@ -23,15 +23,14 @@ def new
@has_registration_ticket = params[:has_registration_ticket]
@unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference)
- @converted_prices = {}
- @unpaid_ticket_purchases.each do |ticket_purchase|
- @converted_prices[ticket_purchase.id] = ticket_purchase.amount_paid
- end
@currency = selected_currency
end
def create
@payment = Payment.new payment_params
+ session[:selected_currency] = params[:currency] if params[:currency].present?
+ selected_currency = session[:selected_currency] || @conference.tickets.first.price_currency
+ from_currency = @conference.tickets.first.price_currency
if @payment.purchase && @payment.save
update_purchased_ticket_purchases
@@ -51,7 +50,8 @@ def create
notice: 'Thanks! Your ticket is booked successfully.'
end
else
- @total_amount_to_pay = convert_currency(@conference, Ticket.total_price(@conference, current_user, paid: false), from_currency, selected_currency)
+ # TODO-SNAPCON: This case is not tested at all
+ @total_amount_to_pay = CurrencyConversion.convert_currency(@conference, Ticket.total_price(@conference, current_user, paid: false), from_currency, selected_currency)
@unpaid_ticket_purchases = current_user.ticket_purchases.unpaid.by_conference(@conference)
flash.now[:error] = @payment.errors.full_messages.to_sentence + ' Please try again with correct credentials.'
render :new
diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb
index 48d62c817..2c3281e9a 100644
--- a/app/controllers/proposals_controller.rb
+++ b/app/controllers/proposals_controller.rb
@@ -41,6 +41,7 @@ def edit
def create
@url = conference_program_proposals_path(@conference.short_title)
+ @superevents = @program.events.where(superevent: true)
# We allow proposal submission and sign up on same page.
# If user is not signed in then first create new user and then sign them in
@@ -80,6 +81,7 @@ def create
def update
@url = conference_program_proposal_path(@conference.short_title, params[:id])
+ @superevents = @program.events.where(superevent: true)
track = Track.find_by(id: params[:event][:track_id])
if track && !track.cfp_active
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index d661a9b3e..5d29ef60d 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -2,6 +2,7 @@
class UsersController < ApplicationController
before_action :authenticate_user!, only: :search
+ before_action :set_currency_options, only: [:edit, :new, :create, :update]
load_and_authorize_resource
# GET /users/1
@@ -40,7 +41,7 @@ def search
def user_params
params[:user][:timezone] = params[:user][:timezone].presence || nil
- params.require(:user).permit(:name, :biography, :nickname, :affiliation,
+ params.require(:user).permit(:name, :biography, :nickname, :affiliation, :default_currency,
:picture, :picture_cache, :timezone)
end
@@ -49,5 +50,9 @@ def user_params
def load_user
@user ||= (params[:id] && params[:id] != 'current' && User.find(params[:id])) || current_user
end
+
# rubocop:enable Naming/MemoizedInstanceVariableName
+ def set_currency_options
+ @currency_options = CurrencyConversion::VALID_CURRENCIES.map { |currency| [currency, currency] }
+ end
end
diff --git a/app/datatables/registration_datatable.rb b/app/datatables/registration_datatable.rb
index 1639ef422..d8969fff8 100644
--- a/app/datatables/registration_datatable.rb
+++ b/app/datatables/registration_datatable.rb
@@ -5,6 +5,8 @@ class RegistrationDatatable < AjaxDatatablesRails::ActiveRecord
def_delegator :@view, :dom_id
def_delegator :@view, :edit_admin_conference_registration_path
+ # def_delegator :@view, :delete_admin_conference_registration_path
+ def_delegator :@view, :admin_conference_registration_toggle_attendance_path
def initialize(params, opts = {})
@view = opts[:view_context]
@@ -17,6 +19,8 @@ def view_columns
name: { source: 'User.name' },
email: { source: 'User.email' },
accepted_code_of_conduct: { source: 'Registration.accepted_code_of_conduct', searchable: false },
+ attended: { source: 'Registration.attended', searchable: false },
+ ticket_price: { source: 'TicketPurchase.amount_paid' },
ticket_type: { source: 'Ticket.title' },
actions: { source: 'Registration.id', searchable: false, orderable: false }
}
@@ -34,6 +38,14 @@ def conference_role_titles(record)
end.compact
end
+ def registration_ticket(record)
+ record.user.tickets.for_registration(conference)
+ end
+
+ def registration_ticket_price(record)
+ record.user.ticket_purchases.where(ticket: registration_ticket(record)).first.amount_paid
+ end
+
def data
records.map do |record|
{
@@ -42,7 +54,9 @@ def data
roles: conference_role_titles(record.user),
email: record.email,
accepted_code_of_conduct: !!record.accepted_code_of_conduct, # rubocop:disable Style/DoubleNegation
- ticket_type: record.user.tickets.where(conference: conference).pluck(:title),
+ ticket_type: registration_ticket(record).title,
+ ticket_price: registration_ticket_price(record),
+ attended: record.attended?,
edit_url: edit_admin_conference_registration_path(conference, record),
DT_RowId: dom_id(record)
}
@@ -50,7 +64,7 @@ def data
end
def get_raw_records # rubocop:disable Naming/AccessorMethodName
- conference.registrations.includes(user: %i[roles tickets]).references(:users, :roles).distinct
+ conference.registrations.includes(user: %i[roles tickets ticket_purchases]).references(:users, :roles).distinct
end
# override upstream santitation, which converts everything to strings
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 6edc74c84..492c98a7f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -122,8 +122,12 @@ def volunteer_links(event)
end, ', ')
end
- def event_types_sentence(conference)
- conference.event_types.map { |et| et.title.pluralize }.to_sentence
+ def event_types_sentence(conference, is_admin)
+ if is_admin
+ conference.event_types.map { |et| et.title.pluralize }.to_sentence
+ else
+ conference.event_types.available_for_public.map { |et| et.title.pluralize }.to_sentence
+ end
end
def sign_in_path
diff --git a/app/helpers/conference_helper.rb b/app/helpers/conference_helper.rb
index d393b6099..3a2c3a136 100644
--- a/app/helpers/conference_helper.rb
+++ b/app/helpers/conference_helper.rb
@@ -55,8 +55,9 @@ def icalendar_proposals(calendar, proposals, conference)
location += "#{v.country_name}, " if v.country_name
e.location = location
end
- e.categories = conference.title, "Difficulty: #{proposal.difficulty_level.title}",
- "Track: #{proposal.track.name}"
+ e.categories = conference.title
+ e.categories << "Difficulty: #{proposal.difficulty_level.title}" if proposal.difficulty_level.present?
+ e.categories << "Track: #{proposal.track.name}" if proposal.track.present?
end
end
calendar
diff --git a/app/helpers/event_types_helper.rb b/app/helpers/event_types_helper.rb
index ca7c3e511..f1306cfec 100644
--- a/app/helpers/event_types_helper.rb
+++ b/app/helpers/event_types_helper.rb
@@ -13,9 +13,9 @@ def event_type_select_options(event_types = {})
"#{type.title} - #{show_time(type.length)}",
type.id,
{ data: {
- min_words: type.minimum_abstract_length,
- max_words: type.maximum_abstract_length,
- instructions: type.submission_template
+ min_words: type.minimum_abstract_length,
+ max_words: type.maximum_abstract_length,
+ template: type.submission_template
} }
]
end
diff --git a/app/models/commercial.rb b/app/models/commercial.rb
index 1fcc0a45a..b01da1aff 100644
--- a/app/models/commercial.rb
+++ b/app/models/commercial.rb
@@ -28,22 +28,30 @@ class Commercial < ApplicationRecord
validate :valid_url
- def self.render_from_url(url)
+ def self.render_from_url(url, title = nil)
register_provider
begin
resource = OEmbed::Providers.get(url, maxwidth: 560, maxheight: 315)
{ html: resource.html.html_safe }
rescue StandardError
- { html: iframe_fallback(url) }
+ { html: EmbeddableURL.new(url, title).render_embed.html_safe }
# { error: exception.message }
end
end
- def self.iframe_fallback(url)
- "".html_safe
+ # TODO: Is this necessary?
+ def self.iframe_fallback(url, title)
+ iframe = <<~HTML
+
+ HTML
+ iframe.html_safe
end
def self.read_file(file)
+ require 'csv'
errors = {}
errors[:no_event] = []
errors[:validation_errors] = []
@@ -67,7 +75,8 @@ def self.read_file(file)
commercial = event.commercials.new(title: title, url: url)
unless commercial.save
- errors[:validation_errors] << ("Could not create materials for event with ID #{event.id} (" + commercial.errors.full_messages.to_sentence + ')')
+ errors[:validation_errors] <<
+ "Could not create materials for event with ID #{event.id} (#{commercial.errors.full_messages.to_sentence})"
end
end
errors
@@ -76,7 +85,9 @@ def self.read_file(file)
private
def valid_url
- result = Commercial.render_from_url(url)
+ return unless url
+
+ result = Commercial.render_from_url(url, title)
errors.add(:base, result[:error]) if result[:error]
end
diff --git a/app/models/conference.rb b/app/models/conference.rb
index 21717f7ef..20e1ce441 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -4,32 +4,33 @@
#
# Table name: conferences
#
-# id :bigint not null, primary key
-# booth_limit :integer default(0)
-# color :string
-# custom_css :text
-# custom_domain :string
-# description :text
-# end_date :date not null
-# end_hour :integer default(20)
-# events_per_week :text
-# guid :string not null
-# logo_file_name :string
-# picture :string
-# registration_limit :integer default(0)
-# revision :integer default(0), not null
-# short_title :string not null
-# start_date :date not null
-# start_hour :integer default(9)
-# ticket_layout :integer default("portrait")
-# timezone :string not null
-# title :string not null
-# use_vdays :boolean default(FALSE)
-# use_volunteers :boolean
-# use_vpositions :boolean default(FALSE)
-# created_at :datetime
-# updated_at :datetime
-# organization_id :integer
+# id :bigint not null, primary key
+# booth_limit :integer default(0)
+# color :string
+# custom_css :text
+# custom_domain :string
+# description :text
+# end_date :date not null
+# end_hour :integer default(20)
+# events_per_week :text
+# guid :string not null
+# logo_file_name :string
+# picture :string
+# registered_attendees_message :text
+# registration_limit :integer default(0)
+# revision :integer default(0), not null
+# short_title :string not null
+# start_date :date not null
+# start_hour :integer default(9)
+# ticket_layout :integer default("portrait")
+# timezone :string not null
+# title :string not null
+# use_vdays :boolean default(FALSE)
+# use_volunteers :boolean
+# use_vpositions :boolean default(FALSE)
+# created_at :datetime
+# updated_at :datetime
+# organization_id :integer
#
# Indexes
#
diff --git a/app/models/currency_conversion.rb b/app/models/currency_conversion.rb
index ffc2ca197..9d25094c6 100644
--- a/app/models/currency_conversion.rb
+++ b/app/models/currency_conversion.rb
@@ -24,6 +24,8 @@ class CurrencyConversion < ApplicationRecord
VALID_CURRENCIES = %w[AUD CAD CHF CNY EUR GBP JPY USD].freeze
belongs_to :conference
validates :rate, numericality: { greater_than: 0 }
+ # Ensure from_currency and to_currency are among the VALID_CURRENCIES
+ validates :from_currency, :to_currency, inclusion: { in: VALID_CURRENCIES }
validates :from_currency, uniqueness: { scope: :to_currency }, on: :create
def self.convert_currency(conference, amount, from_currency, to_currency)
diff --git a/app/models/event_type.rb b/app/models/event_type.rb
index 0d2943dc9..9f2c3d72e 100644
--- a/app/models/event_type.rb
+++ b/app/models/event_type.rb
@@ -4,17 +4,18 @@
#
# Table name: event_types
#
-# id :bigint not null, primary key
-# color :string
-# description :string
-# length :integer default(30)
-# maximum_abstract_length :integer default(500)
-# minimum_abstract_length :integer default(0)
-# submission_template :text
-# title :string not null
-# created_at :datetime
-# updated_at :datetime
-# program_id :integer
+# id :bigint not null, primary key
+# color :string
+# description :string
+# enable_public_submission :boolean default(TRUE), not null
+# length :integer default(30)
+# maximum_abstract_length :integer default(500)
+# minimum_abstract_length :integer default(0)
+# submission_template :text
+# title :string not null
+# created_at :datetime
+# updated_at :datetime
+# program_id :integer
#
class EventType < ApplicationRecord
belongs_to :program, touch: true
@@ -31,9 +32,12 @@ class EventType < ApplicationRecord
validates :color, format: /\A#[0-9A-F]{6}\z/
before_validation :capitalize_color
+ before_validation :strip_title
alias_attribute :name, :title
+ scope :available_for_public, -> { where(enable_public_submission: true) }
+
private
##
@@ -53,4 +57,8 @@ def capitalize_color
def conference_id
program.conference_id
end
+
+ def strip_title
+ self.title = title.strip unless title.nil?
+ end
end
diff --git a/app/models/splashpage.rb b/app/models/splashpage.rb
index 650e06575..9043f20c2 100644
--- a/app/models/splashpage.rb
+++ b/app/models/splashpage.rb
@@ -11,6 +11,7 @@
# banner_photo_updated_at :datetime
# include_booths :boolean
# include_cfp :boolean default(FALSE)
+# include_committee :boolean
# include_happening_now :boolean
# include_lodgings :boolean
# include_program :boolean
diff --git a/app/models/ticket.rb b/app/models/ticket.rb
index fd6efeace..f2c3997df 100644
--- a/app/models/ticket.rb
+++ b/app/models/ticket.rb
@@ -80,16 +80,11 @@ def self.total_price(conference, user, paid: false)
result || Money.new(0, 'USD')
end
- def self.total_price_user(conference, user, paid: false)
- tickets = TicketPurchase.where(conference: conference, user: user, paid: paid)
- tickets.inject(0) { |sum, ticket| sum + (ticket.amount_paid * ticket.quantity) }
- end
-
def tickets_turnover_total(id)
ticket = Ticket.find(id)
return Money.new(0, 'USD') unless ticket
- sum = ticket.ticket_purchases.paid.total
+ sum = ticket.price_cents * ticket.ticket_purchases.paid.total_quantity
Money.new(sum, ticket.price_currency)
end
diff --git a/app/models/ticket_purchase.rb b/app/models/ticket_purchase.rb
index e76731dec..2577269e8 100644
--- a/app/models/ticket_purchase.rb
+++ b/app/models/ticket_purchase.rb
@@ -4,17 +4,18 @@
#
# Table name: ticket_purchases
#
-# id :bigint not null, primary key
-# amount_paid :float default(0.0)
-# currency :string
-# paid :boolean default(FALSE)
-# quantity :integer default(1)
-# week :integer
-# created_at :datetime
-# conference_id :integer
-# payment_id :integer
-# ticket_id :integer
-# user_id :integer
+# id :bigint not null, primary key
+# amount_paid :float default(0.0)
+# amount_paid_cents :integer default(0)
+# currency :string
+# paid :boolean default(FALSE)
+# quantity :integer default(1)
+# week :integer
+# created_at :datetime
+# conference_id :integer
+# payment_id :integer
+# ticket_id :integer
+# user_id :integer
#
class TicketPurchase < ApplicationRecord
@@ -30,12 +31,13 @@ class TicketPurchase < ApplicationRecord
delegate :title, to: :ticket
delegate :description, to: :ticket
- delegate :price, to: :ticket
delegate :price_cents, to: :ticket
delegate :price_currency, to: :ticket
has_many :physical_tickets
+ monetize :amount_paid_cents, with_model_currency: :currency, as: 'purchase_price'
+
scope :paid, -> { where(paid: true) }
scope :unpaid, -> { where(paid: false) }
scope :by_conference, ->(conference) { where(conference_id: conference.id) }
@@ -71,12 +73,13 @@ def self.purchase_ticket(conference, quantity, ticket, user, currency)
purchase.pay(nil)
end
if quantity > 0
- purchase = new(ticket_id: ticket.id,
- conference_id: conference.id,
- user_id: user.id,
- quantity: quantity,
- amount_paid: converted_amount,
- currency: currency)
+ purchase = new(ticket_id: ticket.id,
+ conference_id: conference.id,
+ user_id: user.id,
+ quantity: quantity,
+ amount_paid: converted_amount.to_f,
+ amount_paid_cents: converted_amount.fractional,
+ currency: currency)
purchase.pay(nil) if converted_amount.zero?
end
purchase
@@ -97,6 +100,11 @@ def self.total
sum('amount_paid * quantity')
end
+ # Total quantity
+ def self.total_quantity
+ sum('quantity')
+ end
+
def pay(payment)
update(paid: true, payment: payment)
PhysicalTicket.transaction do
diff --git a/app/models/user.rb b/app/models/user.rb
index bcf38b4db..bfa75bb97 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -16,6 +16,7 @@
# confirmed_at :datetime
# current_sign_in_at :datetime
# current_sign_in_ip :string
+# default_currency :string
# email :string default(""), not null
# email_public :boolean default(FALSE)
# encrypted_password :string default(""), not null
diff --git a/app/serializers/conference_serializer.rb b/app/serializers/conference_serializer.rb
index 32d83dc71..08bce729a 100644
--- a/app/serializers/conference_serializer.rb
+++ b/app/serializers/conference_serializer.rb
@@ -4,32 +4,33 @@
#
# Table name: conferences
#
-# id :bigint not null, primary key
-# booth_limit :integer default(0)
-# color :string
-# custom_css :text
-# custom_domain :string
-# description :text
-# end_date :date not null
-# end_hour :integer default(20)
-# events_per_week :text
-# guid :string not null
-# logo_file_name :string
-# picture :string
-# registration_limit :integer default(0)
-# revision :integer default(0), not null
-# short_title :string not null
-# start_date :date not null
-# start_hour :integer default(9)
-# ticket_layout :integer default("portrait")
-# timezone :string not null
-# title :string not null
-# use_vdays :boolean default(FALSE)
-# use_volunteers :boolean
-# use_vpositions :boolean default(FALSE)
-# created_at :datetime
-# updated_at :datetime
-# organization_id :integer
+# id :bigint not null, primary key
+# booth_limit :integer default(0)
+# color :string
+# custom_css :text
+# custom_domain :string
+# description :text
+# end_date :date not null
+# end_hour :integer default(20)
+# events_per_week :text
+# guid :string not null
+# logo_file_name :string
+# picture :string
+# registered_attendees_message :text
+# registration_limit :integer default(0)
+# revision :integer default(0), not null
+# short_title :string not null
+# start_date :date not null
+# start_hour :integer default(9)
+# ticket_layout :integer default("portrait")
+# timezone :string not null
+# title :string not null
+# use_vdays :boolean default(FALSE)
+# use_volunteers :boolean
+# use_vpositions :boolean default(FALSE)
+# created_at :datetime
+# updated_at :datetime
+# organization_id :integer
#
# Indexes
#
diff --git a/app/services/embeddable_url.rb b/app/services/embeddable_url.rb
new file mode 100644
index 000000000..289a69208
--- /dev/null
+++ b/app/services/embeddable_url.rb
@@ -0,0 +1,99 @@
+# Transform a URL to a version that allows iframes
+
+class EmbeddableURL
+ attr_accessor :url, :title
+
+ DEFAULT_FRAME_ATTRS = 'width=560 height=315 frameborder=0 allowfullscreen'.freeze
+
+ TRANSFORMATIONS = {
+ /snap\.berkeley\.edu/ => :snap,
+ /docs\.google\.com/ => :google_docs,
+ /dropbox\.com/ => :dropbox
+ }.freeze
+
+ def initialize(url, title)
+ # Do some normalizing so that URIs parse correctly.
+ if url
+ self.url = url.strip
+ end
+ self.title = title
+ end
+
+ def render_embed
+ return render_dropbox if url.include?('dropbox.com')
+
+ # TODO-A11Y: Set an iframe title
+ ""
+ end
+
+ def iframe_url
+ TRANSFORMATIONS.each do |regex, fn|
+ return send(fn, url) if url.match?(regex)
+ end
+ url
+ end
+
+ # TODO: Consider adjusting the id / loading if > 1 dropbox embed per page.
+ def render_dropbox
+ <<~HTML
+
+ HTML
+ end
+
+ private
+
+ def optional_params
+ return '' unless url.include?('snap.berkeley')
+
+ 'allow="geolocation;microphone;camera"'
+ end
+
+ def google_docs(url)
+ # replace /edit, /share, /comment with /embed and remove the querystring
+ url.gsub(%r{(/edit|/share|/comment).*}, '/embed')
+ end
+
+ def dropbox(url)
+ # debugger
+ uri = URI.parse(url)
+ params = URI.decode_www_form(uri.query)&.to_h
+ params.delete('raw')
+ params['dl'] = '0'
+ # params['rlkey'] = params['rlkey']
+ uri.query = params.to_query
+ uri.to_s
+ end
+
+ def snap(url)
+ uri = URI.parse(url)
+ return url if uri.query.blank?
+
+ args = URI.decode_www_form(uri.query).to_h
+ username = args['username'] || args['user']
+ projectname = args['projectname'] || args['project']
+
+ return url if username.blank? || projectname.blank?
+
+ query = URI.encode_www_form({
+ 'projectname' => projectname,
+ 'username' => username,
+ 'showTitle' => 'true',
+ 'showAuthor' => 'true',
+ 'editButton' => 'true',
+ 'pauseButton' => 'true'
+ })
+ URI::HTTPS.build(host: uri.host, path: '/embed', query: query).to_s
+ end
+
+ def iframe_title
+ if title
+ "title='#{title} Embedded Media'"
+ else
+ 'title="Embedded Media for Presentation"'
+ end
+ end
+end
diff --git a/app/views/admin/cfps/_events_cfp.html.haml b/app/views/admin/cfps/_events_cfp.html.haml
index 6e8066b4a..e04020cd4 100644
--- a/app/views/admin/cfps/_events_cfp.html.haml
+++ b/app/views/admin/cfps/_events_cfp.html.haml
@@ -21,7 +21,7 @@
%dt
Event types:
%dd
- = event_types_sentence(conference)
+ = event_types_sentence(conference, true)
%dt
Tracks:
%dd
diff --git a/app/views/admin/conferences/_form_fields.html.haml b/app/views/admin/conferences/_form_fields.html.haml
index f5705f455..c5439f1ac 100644
--- a/app/views/admin/conferences/_form_fields.html.haml
+++ b/app/views/admin/conferences/_form_fields.html.haml
@@ -23,6 +23,10 @@
= f.label :description
= f.text_area :description, rows: 5, data: { provide: 'markdown' }, class: 'form-control'
.help-block= markdown_hint('Splash page content')
+ .form-group
+ = f.label :registered_attendees_message, 'Message for Registered Attendees'
+ = f.text_area :registered_attendees_message, rows: 5, data: { provide: 'markdown' }, class: 'form-control'
+ .help-block= markdown_hint('Splash page content')
.form-group
= f.color_field :color, size: 6, class: 'form-control'
%span.help-block
@@ -46,52 +50,8 @@
= f.select :ticket_layout, Conference.ticket_layouts.keys, {}, class: 'form-control'
%span.help-block
Layout type for tickets of the conference.
-
-%h4 Scheduling
-%hr
-.form-group
- = f.label :timezone
- = f.time_zone_select :timezone, nil, { default: 'UTC' }, { class: 'form-control' }
- %span.help-block
- Please select in what time zone your conference will take place.
-.form-group
- = f.label :start_date, "Start Date"
- %abbr{title: 'This field is required'} *
- = f.text_field :start_date, id: 'conference-start-datepicker', required: true, class: 'form-control'
-.form-group
- = f.label :end_date, "End Date"
- %abbr{title: 'This field is required'} *
- = f.text_field :end_date, id: 'conference-end-datepicker', required: true, class: 'form-control'
-- unless f.object.new_record?
- .form-group
- = f.label :start_hour
- = f.number_field :start_hour, size: 2, min: 0, max: 23, class: 'form-control'
- %span.help-block
- = rescheduling_hint(@affected_event_count)
- .form-group
- = f.label :end_hour
- = f.number_field :end_hour, size: 2, min: 1, max: 24, class: 'form-control'
- %span.help-block
- = rescheduling_hint(@affected_event_count)
- %h4 Registrations
- %hr
- .form-group
- = f.label :registration_limit
- = f.number_field :registration_limit, in: 0..9999, class: 'form-control'
- %span.help-block
- Limit the number of registrations to the conference (0 no limit). Please note that the registration limit
- does not apply to speakers of confirmed events, they will still be able to register even if the limit has been reached.
- You currently have #{pluralize(@conference.registrations.count, 'registration')}.
- %h4
- Booths
- %hr
- .form-group
- = f.label :booth_limit
- = f.number_field :booth_limit, in: 0..9999, class: 'form-control'
- %span.help-block
- #{(t'booth').capitalize} limit is the maximum number of #{(t'booth').pluralize}
- that you can accept for this conference. By setting this number (0 no limit) you can be sure that you are not going to accept more #{(t'booth').pluralize}
- than the conference can accommodate. You currently have #{pluralize(@conference.booths.accepted.count, "accepted #{t'booth'}")}.
+=render partial: 'scheduling_form_fields', locals: {f: f}
+=render partial: 'registration_and_booth_form_fields', locals: {f: f}
%p.text-right
- if f.object.new_record?
= f.submit nil, { class: 'btn btn-success' }
diff --git a/app/views/admin/conferences/_registration_and_booth_form_fields.html.haml b/app/views/admin/conferences/_registration_and_booth_form_fields.html.haml
new file mode 100644
index 000000000..c7857cec6
--- /dev/null
+++ b/app/views/admin/conferences/_registration_and_booth_form_fields.html.haml
@@ -0,0 +1,19 @@
+- unless f.object.new_record?
+ %h4 Registrations
+ %hr
+ .form-group
+ = f.label :registration_limit
+ = f.number_field :registration_limit, in: 0..9999, class: 'form-control'
+ %span.help-block
+ Limit the number of registrations to the conference (0 no limit). Please note that the registration limit
+ does not apply to speakers of confirmed events, they will still be able to register even if the limit has been reached.
+ You currently have #{pluralize(@conference.registrations.count, 'registration')}.
+ %h4 Booths
+ %hr
+ .form-group
+ = f.label :booth_limit
+ = f.number_field :booth_limit, in: 0..9999, class: 'form-control'
+ %span.help-block
+ #{(t'booth').capitalize} limit is the maximum number of #{(t'booth').pluralize}
+ that you can accept for this conference. By setting this number (0 no limit) you can be sure that you are not going to accept more #{(t'booth').pluralize}
+ than the conference can accommodate. You currently have #{pluralize(@conference.booths.accepted.count, "accepted #{t'booth'}")}.
diff --git a/app/views/admin/conferences/_scheduling_form_fields.html.haml b/app/views/admin/conferences/_scheduling_form_fields.html.haml
new file mode 100644
index 000000000..65a2c48cb
--- /dev/null
+++ b/app/views/admin/conferences/_scheduling_form_fields.html.haml
@@ -0,0 +1,26 @@
+%h4 Scheduling
+%hr
+.form-group
+ = f.label :timezone
+ = f.time_zone_select :timezone, nil, { default: 'UTC' }, { class: 'form-control' }
+ %span.help-block
+ Please select in what time zone your conference will take place.
+.form-group
+ = f.label :start_date, "Start Date"
+ %abbr{title: 'This field is required'} *
+ = f.text_field :start_date, id: 'conference-start-datepicker', required: true, class: 'form-control'
+.form-group
+ = f.label :end_date, "End Date"
+ %abbr{title: 'This field is required'} *
+ = f.text_field :end_date, id: 'conference-end-datepicker', required: true, class: 'form-control'
+- unless f.object.new_record?
+ .form-group
+ = f.label :start_hour
+ = f.number_field :start_hour, size: 2, min: 0, max: 23, class: 'form-control'
+ %span.help-block
+ = rescheduling_hint(@affected_event_count)
+ .form-group
+ = f.label :end_hour
+ = f.number_field :end_hour, size: 2, min: 1, max: 24, class: 'form-control'
+ %span.help-block
+ = rescheduling_hint(@affected_event_count)
diff --git a/app/views/admin/currency_conversions/index.html.haml b/app/views/admin/currency_conversions/index.html.haml
index 23e435454..63350cd0e 100644
--- a/app/views/admin/currency_conversions/index.html.haml
+++ b/app/views/admin/currency_conversions/index.html.haml
@@ -4,9 +4,6 @@
%h1 Currency Conversions
%p.text-muted
Enter the currency conversions for this conference
- %p.alert.alert-warning
- %strong Warning:
- Currency conversion feature has not been implemented yet. Do not add any currency conversions.
.row
.col-md-12
%table.table.table-hover#currency-conversions
diff --git a/app/views/admin/event_types/_form.html.haml b/app/views/admin/event_types/_form.html.haml
index d0a2739c1..234d50293 100644
--- a/app/views/admin/event_types/_form.html.haml
+++ b/app/views/admin/event_types/_form.html.haml
@@ -15,6 +15,9 @@
= f.label :description
= f.text_area :description, class: 'form-control', rows: 5, data: { provide: 'markdown' }
.help-block= markdown_hint
+ .form-group
+ = f.label "Allow public submission?", for: :enable_public_submission
+ = f.check_box :enable_public_submission, class: 'switch-checkbox'
.form-group
= f.label :minimum_abstract_length
%abbr{title: 'This field is required'} *
diff --git a/app/views/admin/event_types/index.html.haml b/app/views/admin/event_types/index.html.haml
index 56411fb78..b4784b446 100644
--- a/app/views/admin/event_types/index.html.haml
+++ b/app/views/admin/event_types/index.html.haml
@@ -11,7 +11,8 @@
%tr
%th Title
%th Description
- %th Instructions
+ %th Enable Public Submission
+ %th Template
%th Length
%th Abstract Length
%th Color
@@ -23,6 +24,8 @@
= event_type.title
%td
= markdown(event_type.description)
+ %td
+ = event_type.enable_public_submission ? 'Yes' : 'No'
%td
= markdown(event_type.submission_template)
%td
diff --git a/app/views/admin/events/_proposal.html.haml b/app/views/admin/events/_proposal.html.haml
index ae50c63d2..0fc5aa459 100644
--- a/app/views/admin/events/_proposal.html.haml
+++ b/app/views/admin/events/_proposal.html.haml
@@ -11,6 +11,8 @@
= 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'
+ = link_to 'Delete', admin_conference_program_event_path(@conference.short_title, @event), method: :delete, data: { confirm: 'Are you sure you want to delete this event?' }, class: 'btn btn-mini btn-danger'
+
.row
.col-md-12
diff --git a/app/views/admin/events/index.html.haml b/app/views/admin/events/index.html.haml
index 6f68faee1..2d1ac6372 100644
--- a/app/views/admin/events/index.html.haml
+++ b/app/views/admin/events/index.html.haml
@@ -66,6 +66,7 @@
%th Rating
%th Submitter
%th Speakers
+ %th Volunteers
- if @program.languages.present?
%th Language
%th Requires Registration
diff --git a/app/views/admin/physical_tickets/_physical_ticket.html.haml b/app/views/admin/physical_tickets/_physical_ticket.html.haml
index 241e88754..b9cc99a02 100644
--- a/app/views/admin/physical_tickets/_physical_ticket.html.haml
+++ b/app/views/admin/physical_tickets/_physical_ticket.html.haml
@@ -3,7 +3,8 @@
%td= physical_ticket.ticket.title
%td= physical_ticket.ticket.registration_ticket? ? 'Yes' : 'No'
%td= physical_ticket.user&.email
- %td= humanized_money_with_symbol physical_ticket.ticket_purchase.amount_paid
+ %td= humanized_money_with_symbol(physical_ticket.ticket_purchase.purchase_price)
+ %td= physical_ticket.ticket_purchase.currency || 'USD'
%td
- if physical_ticket.ticket_scannings.present?
%span Checked in:
diff --git a/app/views/admin/physical_tickets/index.html.haml b/app/views/admin/physical_tickets/index.html.haml
index 80835f901..96722dc98 100644
--- a/app/views/admin/physical_tickets/index.html.haml
+++ b/app/views/admin/physical_tickets/index.html.haml
@@ -24,6 +24,7 @@
%th Registration?
%th User
%th Paid
+ %th Currency
%th Attedance
%th Actions
%tbody
diff --git a/app/views/admin/programs/show.html.haml b/app/views/admin/programs/show.html.haml
index db1e91ffb..f2c1949fa 100644
--- a/app/views/admin/programs/show.html.haml
+++ b/app/views/admin/programs/show.html.haml
@@ -22,7 +22,7 @@
%dt
Event types:
%dd
- = event_types_sentence(@conference)
+ = event_types_sentence(@conference, true)
%dt
Tracks:
%dd
diff --git a/app/views/admin/registrations/index.html.haml b/app/views/admin/registrations/index.html.haml
index 45f66b232..c756e8dd2 100644
--- a/app/views/admin/registrations/index.html.haml
+++ b/app/views/admin/registrations/index.html.haml
@@ -28,6 +28,8 @@
%th{ width: '25%' } Name
%th{ width: '0' } E-Mail
%th{ width: '0' } Ticket Type
+ %th{ width: '0' } Price
+ %th{ width: '0' } Attended
%th{ width: '0' }
%abbr{ title: 'Code of Conduct' } CoC
%th{ width: '0' } Actions
@@ -55,12 +57,12 @@
{
"data": "name",
"className": "truncate",
- "render": function(data, type, row) {
- var content = '' + data + '
';
- $.each(row.roles, function(index, role){
- content += ' ' + role + ''
- });
- return content;
+ "render": (data, type, row) => {
+ return `
+ ${data}
+
+ ${row.roles.map(role => ` ${role}`)}
+ `;
}
},
{
@@ -69,20 +71,42 @@
{
"data": "ticket_type"
},
+ {
+ "data": "ticket_price",
+ "render": data => `$${data}`
+ },
+ {
+ "data": "attended",
+ "search": data => data,
+ "render": (data, _type, row) => {
+ let js_url = "#{toggle_attendance_admin_conference_registration_path(@conference.short_title, id: 'ROW_ID')}";
+ return `
+ ${data}
+
+ `;
+ }
+ },
{
"data": "accepted_code_of_conduct",
"className": "code-of-conduct text-center",
"searchable": false,
+ "visible": codeOfConductPresent
},
{
"data": "actions",
"className": "actions",
"searchable": false,
"sortable": false,
- "render": function (data, type, row, meta) {
- return '';
+ "render": (data, type, row) => {
+ return ``;
}
}
]
diff --git a/app/views/admin/schedules/show.html.haml b/app/views/admin/schedules/show.html.haml
index 5777f4836..0b1d0f56a 100644
--- a/app/views/admin/schedules/show.html.haml
+++ b/app/views/admin/schedules/show.html.haml
@@ -4,10 +4,37 @@
.col-md-12
.page-header
%h1= "#{@conference.title} Schedule"
+
+ .btn-group.pull-right
+ %button.btn.btn-primary{ title: 'Mass import of schedule for events',
+ data: { toggle: 'modal', target: '#mass-schedule-modal' } }
+ Upload with CSV file
+
%p.text-muted
Create the schedules for the conference
= render 'schedules/event_types_key', event_types: @event_types, favourites: false
+.modal#mass-schedule-modal
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %h1
+ Schedule events
+ .modal-body
+ = form_for :schedule, url: upload_csv_admin_conference_schedules_path(@conference), html: { multipart: true }, method: :post do |f|
+ .form-group
+ = f.file_field :file, as: :file
+ %span.help-block
+ Upload your CSV file with data in the following format:
+ %b Event_ID, Start_Time, Date, Room
+ for instance:
+ %pre
+ Event_ID, Start_Time, Date, Room
+ osemdemo, 10:00, 6/1/2024, Australia
+ osemdemo, 11:00, 6/2/2024, Brazil
+ .modal-footer
+ = f.submit 'Upload', class: 'btn btn-primary'
+
- if @rooms.present?
.row
.col-md-2
diff --git a/app/views/admin/splashpages/_form.html.haml b/app/views/admin/splashpages/_form.html.haml
index 338ad4a2e..f494e9a05 100644
--- a/app/views/admin/splashpages/_form.html.haml
+++ b/app/views/admin/splashpages/_form.html.haml
@@ -55,6 +55,10 @@
%label
= f.check_box :include_social_media
Display the social media links?
+ .checkbox
+ %label
+ = f.check_box :include_committee
+ Display the committee?
%h4
Access
%hr
diff --git a/app/views/admin/splashpages/show.html.haml b/app/views/admin/splashpages/show.html.haml
index c6fa8a70d..97fd04ebb 100644
--- a/app/views/admin/splashpages/show.html.haml
+++ b/app/views/admin/splashpages/show.html.haml
@@ -56,6 +56,9 @@
%li
%i{ class: "fa-li #{icon_for_todo @splashpage.include_social_media?}" }
Display social media links
+ %li
+ %i{ class: "fa-li #{icon_for_todo @splashpage.include_committee?}" }
+ Display social media links
%li
- if @conference.splashpage && @conference.splashpage.public?
%i{ class: "fa-li #{icon_for_todo @splashpage.public?}" }
diff --git a/app/views/conference_registrations/show.html.haml b/app/views/conference_registrations/show.html.haml
index 9d89752ab..88e4fac45 100644
--- a/app/views/conference_registrations/show.html.haml
+++ b/app/views/conference_registrations/show.html.haml
@@ -86,27 +86,27 @@
%i.fa-solid.fa-square-dashed.fa-stack-2x
%i.fa-solid.fa-ticket.fa-stack-1x
Tickets
- - if @tickets.any?
- Total Purchased:
- = "(#{@tickets.first.price.symbol}#{humanized_money @total_price})"
- %ul
- .col-md-12
- - @ticket_payments.each_pair do |ticket_id, tickets|
- %li
- = @total_quantity[ticket_id]
- = tickets.first.title
- = word_pluralize(@total_quantity[ticket_id], 'Ticket')
- for
- = tickets.first.price.symbol
- = humanized_money @total_price_per_ticket[ticket_id]
- %br
- .btn-group{ role: 'group' }
- = link_to 'View all tickets',
- conference_physical_tickets_path(@conference.short_title),
- class: 'btn btn-success'
- = link_to 'Get more tickets',
- conference_tickets_path(@conference.short_title),
- class: 'btn btn-default'
+ - if @purchases.any?
+ .col-md-12
+ - @total_price_per_currency.each do |currency, total_price|
+ %h4
+ Total Purchased (#{currency}):
+ = humanized_money_with_symbol(total_price)
+ %ul
+ - @total_price_per_ticket_per_currency.select { |(id, curr)| curr == currency }.each do |(ticket_id, curr), total_amount|
+ - purchase = @purchases.find { |p| p.ticket_id == ticket_id && p.currency == curr }
+ %li
+ - title = purchase.title.titleize.gsub(/(?i)\bticket\b/, 'Tickets')
+ - title = title.include?('Tickets') ? title : "#{title} Tickets"
+ = "#{@total_quantity[[ticket_id, currency]]} #{title} for "
+ = humanized_money_with_symbol(total_amount)
+ .btn-group{ role: 'group' }
+ = link_to 'View all tickets',
+ conference_physical_tickets_path(@conference.short_title),
+ class: 'btn btn-success'
+ = link_to 'Get more tickets',
+ conference_tickets_path(@conference.short_title),
+ class: 'btn btn-default'
- else
%p
You haven't bought any tickets.
@@ -125,7 +125,7 @@
- if @registration
.btn-group-vertical.pull-right
= link_to 'Edit your Registration', edit_conference_conference_registration_path(@conference.short_title), class: 'btn btn-success', disabled: @conference.end_date < Date.today
- - if @tickets.any?
+ - if @purchases.any?
= link_to 'Unregister', conference_conference_registration_path(@conference.short_title), method: :delete, class: 'btn btn-danger btn-xs',
data: { confirm: "Your ticket purchases won't be refunded. Are you sure you want to unregister?" }, disabled: @conference.end_date < Date.today
- else
diff --git a/app/views/conferences/_about_and_happening_now.haml b/app/views/conferences/_about_and_happening_now.haml
index 239cbbce4..05141967d 100644
--- a/app/views/conferences/_about_and_happening_now.haml
+++ b/app/views/conferences/_about_and_happening_now.haml
@@ -8,6 +8,8 @@
= content_for :about do
#about
+ -if @user_registered && conference.registered_attendees_message.present?
+ = markdown(conference.registered_attendees_message, false)
= markdown(conference.description, false)
%section#about-and-happening-now
diff --git a/app/views/conferences/_committee.haml b/app/views/conferences/_committee.haml
new file mode 100644
index 000000000..5d8b66634
--- /dev/null
+++ b/app/views/conferences/_committee.haml
@@ -0,0 +1,18 @@
+-# _committee.haml
+.row
+ .col-md-12
+ %h3.text-center
+ Committee
+.row
+ .col-sd-6
+ .row.row-centered
+ - organizers.shuffle.each do |organizer|
+ .col-sd-3.col-sm-1.col-xs-3.col-centered
+ = link_to user_path(organizer), class: 'thumbnail',
+ style: 'border: none; background: none; padding: 20px;' do
+ - if organizer.profile_picture.present?
+ = image_tag organizer.profile_picture(size: 150),
+ class: ['img-responsive', 'img-circle'],
+ title: organizer.name
+ .caption
+ %h4.text-center= organizer.name
diff --git a/app/views/conferences/show.html.haml b/app/views/conferences/show.html.haml
index 29c5014e2..49aa07b7e 100644
--- a/app/views/conferences/show.html.haml
+++ b/app/views/conferences/show.html.haml
@@ -86,6 +86,10 @@
sponsorship_levels: @sponsorship_levels,
sponsors: @sponsors
+ -# committee
+ - if @conference.splashpage.include_committee?
+ = render 'committee', organizers: @organizers
+
-# footer
- if @conference.splashpage.include_social_media? && @conference.contact.has_social_media?
= render 'social_media', contact: @conference.contact, cached: true
diff --git a/app/views/payments/_payment.html.haml b/app/views/payments/_payment.html.haml
index 27a85af5d..76544211e 100644
--- a/app/views/payments/_payment.html.haml
+++ b/app/views/payments/_payment.html.haml
@@ -15,9 +15,9 @@
%td
= ticket.quantity
%td
- = humanized_money_with_symbol @converted_prices[ticket.id]
+ = humanized_money_with_symbol ticket.purchase_price
%td
- = humanized_money_with_symbol (@converted_prices[ticket.id] * ticket.quantity)
+ = humanized_money_with_symbol ticket.purchase_price * ticket.quantity
= form_tag conference_payments_path(@conference.short_title, :has_registration_ticket => @has_registration_ticket) do
%script.stripe-button{ src: "https://checkout.stripe.com/checkout.js",
data: { amount: @total_amount_to_pay.cents,
diff --git a/app/views/proposals/_encouragement_text.html.haml b/app/views/proposals/_encouragement_text.html.haml
index f7f6033f0..8f831582f 100644
--- a/app/views/proposals/_encouragement_text.html.haml
+++ b/app/views/proposals/_encouragement_text.html.haml
@@ -1,7 +1,7 @@
%p.lead
- if @program.event_types.any?
You can submit proposals for
- = "#{event_types_sentence(@conference)}."
+ = "#{event_types_sentence(@conference, current_user&.is_admin || false)}."
- if @program.tracks.confirmed.cfp_active.any?
Proposals should fit in one of the
= "#{pluralize(@program.tracks.confirmed.cfp_active.count, 'track')}:"
diff --git a/app/views/proposals/_submission_type_content_form.haml b/app/views/proposals/_submission_type_content_form.haml
index 6a66fa5a1..76b915047 100644
--- a/app/views/proposals/_submission_type_content_form.haml
+++ b/app/views/proposals/_submission_type_content_form.haml
@@ -2,8 +2,9 @@
%p Please select a submission type, then fill in the abstract and extended details.
.form-group
- = f.label :event_type_id, 'Type'
- = f.select :event_type_id, event_type_select_options(@conference.program.event_types), { include_blank: false }, { class: 'select-help-toggle form-control' }
+ = f.label :event_type_id, "Type"
+ - visible_event_types = current_user&.is_admin ? @conference.program.event_types : @conference.program.event_types.available_for_public
+ = f.select :event_type_id, event_type_select_options(visible_event_types), { include_blank: false }, { class: 'select-help-toggle form-control' }
- program.event_types.each do |event_type|
.help-block.event_event_type_id.collapse{ id: "#{dom_id(event_type)}-help" }
diff --git a/app/views/schedules/_date_event_types.haml b/app/views/schedules/_date_event_types.haml
index 2ba3cd4d2..c129e782e 100644
--- a/app/views/schedules/_date_event_types.haml
+++ b/app/views/schedules/_date_event_types.haml
@@ -5,6 +5,8 @@
The conference timezone is #{timezone_mapping(conference.timezone)}.
- if current_user
Visit your #{link_to('user profile page', edit_user_path(current_user.id))} to set your timezone.
+ - else
+ (#{link_to('Log in', new_user_session_path)} to view the schedule in your preferred timezone.)
- if current_user
%p.text-center
Click the star next to each event to add or remove it from
diff --git a/app/views/schedules/_event.html.haml b/app/views/schedules/_event.html.haml
index 122d3e1e1..e3438bde0 100644
--- a/app/views/schedules/_event.html.haml
+++ b/app/views/schedules/_event.html.haml
@@ -14,7 +14,7 @@
%h3.event-panel-title
= link_to conference_program_proposal_path(@conference.short_title, event.id),
- style: color_style do
+ style: "#{color_style}; line-height: 1.7" do
= event.title
- if event.subtitle.present?
%br
diff --git a/app/views/schedules/_event_mini.html.haml b/app/views/schedules/_event_mini.html.haml
index b0e29509c..27acb4a77 100644
--- a/app/views/schedules/_event_mini.html.haml
+++ b/app/views/schedules/_event_mini.html.haml
@@ -25,8 +25,8 @@
%span= " at #{new_start_time.strftime('%l:%M %P')}"
= join_event_link(event, event_schedule, current_user, small: true)
%p
- = truncate(markdown(event.abstract), length: 250, escape: false)
- - if event.subevents.present?
+ = markdown(truncate(event.abstract, length: 256))
+ - if event.program_subevents.present?
%ul
- event.program_subevents.each do |subevent|
%li= link_to(subevent.title, conference_program_proposal_path(@conference.short_title, subevent.id))
diff --git a/app/views/schedules/events.html.haml b/app/views/schedules/events.html.haml
index 4c45a80b6..a6365171a 100644
--- a/app/views/schedules/events.html.haml
+++ b/app/views/schedules/events.html.haml
@@ -40,37 +40,34 @@
.row
/ scheduled events
:ruby
- date = nil
- time = nil
- tz_object = current_user&.timezone ? current_user : @conference
+ prev_date, prev_time = nil
+ display_tz = current_user&.timezone.presence || @conference.timezone
/ TODO-SNAPCON: Explore caching this.
- - @events_schedules.each do |event_schedule|
- - next if event_schedule.event.parent_event.present?
- - new_start_time = convert_timezone(event_schedule.start_time, @conference.timezone, tz_object.timezone)
- - start_ymd = new_start_time.strftime('%Y-%m-%d')
- - unless start_ymd.eql?(date)
- .col-xs-12.col-md-12
- .date-content
- %span.date-title{ id: start_ymd }
- = inyourtz(event_schedule.start_time, @conference.timezone) do
- = date = start_ymd
- %a.pull-right{ title: "Go up", href: "#program" }
- %i.fa-solid.fa-angles-up.fa-lg{ 'aria-hidden': true }
- .col-xs-12.col-md-1
- - if !event_schedule.start_time.strftime('%H:%M').eql?(time) || !start_ymd.eql?(date)
- - time = new_start_time.strftime('%H:%M')
- = inyourtz(event_schedule.start_time, @conference.timezone) do
- = time + ' ' + timezone_text(tz_object)
- .col-xs-12.col-md-11
- - cache [@program, event_schedule, event_schedule.event, current_user, event_schedule.happening_now?, '#scheduled#full#panel'] do
- = render 'event', event: event_schedule.event, event_schedule: event_schedule
+ - @events_schedules.select { |es| es.event.parent_event.nil? }.each do |event_schedule|
+ - event = event_schedule.event
+ - start_time = convert_timezone(event_schedule.start_time, @conference.timezone, display_tz)
+
+ - unless start_time.to_date.eql?(prev_date)
+ .date-content
+ %h2.date-title{ style: "margin: 0;" }
+ %a{ name: start_time.to_date }
+ = prev_date = start_time.to_date
+ %a.pull-right{ title: "Go up", href: "#program" }
+ %i.fa-solid.fa-angles-up.fa-lg{ 'aria-hidden': true }
+ - if !start_time.eql?(prev_time)
+ - prev_time = start_time
+ %h3= start_time.strftime('%H:%M %p %Z')
+ .col-12
+ - cache [@program, event_schedule, event, current_user, event_schedule.happening_now?, '#scheduled#full#panel'] do
+ .event-item{ data: { time: event_schedule.start_time.iso8601 }, id: dom_id(event) }
+ = render 'event', event: event, event_schedule: event_schedule
/ confirmed events that are not scheduled
- if @unscheduled_events.any?
- .col-xs-12.col-md-12
+ .col-12
.date-content
- %span.date-title#unscheduled
+ %h2.date-title#unscheduled{ style: "margin: 0;" }
Unscheduled events
%a.pull-right{ title: "Go up", href: "#program" }
%i.fa-solid.fa-angles-up.fa-lg{ 'aria-hidden': true }
@@ -79,6 +76,8 @@
.unscheduled-event
- cache [@program, event, current_user, '#unscheduled#full#panel'] do
= render 'event', event: event, event_schedule: nil
+ %button.btn.btn-primary#current-event-btn{ type: "button" }
+ Jump to Current Event
:javascript
$('.program-selector').on('click', function(e) {
diff --git a/app/views/shared/_media_item.html.haml b/app/views/shared/_media_item.html.haml
index 367d04a2f..8e36eb4ad 100644
--- a/app/views/shared/_media_item.html.haml
+++ b/app/views/shared/_media_item.html.haml
@@ -15,7 +15,7 @@
- elsif commercial.commercial_type == 'YouTube'
%iframe{width: '560', height: '315', frameborder: '0', allowfullscreen: 'true', src: "https://www.youtube.com/embed/#{commercial.commercial_id}?rel=0"}
- elsif commercial.url
- = Commercial.render_from_url(commercial.url)[:html]
+ = Commercial.render_from_url(commercial.url, commercial.title)[:html]
- if commercial.url
= link_to('Open in a new tab', commercial.url, target: '_blank', class: 'btn btn-info btn-xs')
%br
diff --git a/app/views/users/edit.html.haml b/app/views/users/edit.html.haml
index d1b967620..aff35bd69 100644
--- a/app/views/users/edit.html.haml
+++ b/app/views/users/edit.html.haml
@@ -23,6 +23,9 @@
The timezone setting will update the event schedules to show using the time you selected.
Your browser is current set to
%span.js-localTimezone
+ .form-group
+ = f.label :default_currency, 'Default Currency'
+ = f.select :default_currency, options_for_select(@currency_options, selected: @user.default_currency), {}, { class: 'form-control' }
.form-group
= f.label :avatar
%br
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
index ac033bf9d..90ac51ca2 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -11,6 +11,6 @@
# end
# These inflection rules are supported but not enabled by default:
-# ActiveSupport::Inflector.inflections(:en) do |inflect|
-# inflect.acronym 'RESTful'
-# end
+ActiveSupport::Inflector.inflections(:en) do |inflect|
+ inflect.acronym 'URL'
+end
diff --git a/config/puma.rb b/config/puma.rb
index 35d73834e..c401d6fc2 100644
--- a/config/puma.rb
+++ b/config/puma.rb
@@ -25,12 +25,13 @@
# processes).
#
workers ENV.fetch('WEB_CONCURRENCY') { 2 }
+# Set a 10 minute timeout in development for debugging.
+worker_timeout 60 * 60 * 10 if ENV.fetch('RAILS_ENV') == 'development'
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
-#
preload_app!
lowlevel_error_handler do |ex, env|
diff --git a/config/routes.rb b/config/routes.rb
index 6e49e0666..62ac3dfdd 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -40,6 +40,11 @@
resource :ticket_scanning, only: [:create]
resources :comments, only: [:index]
resources :conferences do
+ resources :schedules do
+ collection do
+ post :upload_csv
+ end
+ end
resources :surveys do
resources :survey_questions, except: :index
end
diff --git a/db/migrate/20240308190204_add_currency_to_payments.rb b/db/migrate/20230418211400_add_currency_to_payments.rb
similarity index 100%
rename from db/migrate/20240308190204_add_currency_to_payments.rb
rename to db/migrate/20230418211400_add_currency_to_payments.rb
diff --git a/db/migrate/20240315025823_add_currency_to_ticket_purchases.rb b/db/migrate/20230418211410_add_currency_to_ticket_purchases.rb
similarity index 61%
rename from db/migrate/20240315025823_add_currency_to_ticket_purchases.rb
rename to db/migrate/20230418211410_add_currency_to_ticket_purchases.rb
index 70e4f9d96..6fa9de448 100644
--- a/db/migrate/20240315025823_add_currency_to_ticket_purchases.rb
+++ b/db/migrate/20230418211410_add_currency_to_ticket_purchases.rb
@@ -1,5 +1,5 @@
class AddCurrencyToTicketPurchases < ActiveRecord::Migration[7.0]
def change
- add_column :ticket_purchases, :currency, :string
+ # add_column :ticket_purchases, :currency, :string
end
end
diff --git a/db/migrate/20230418211400_create_currency_conversions.rb b/db/migrate/20230418211420_create_currency_conversions.rb
similarity index 79%
rename from db/migrate/20230418211400_create_currency_conversions.rb
rename to db/migrate/20230418211420_create_currency_conversions.rb
index 9c5100085..3403e779c 100644
--- a/db/migrate/20230418211400_create_currency_conversions.rb
+++ b/db/migrate/20230418211420_create_currency_conversions.rb
@@ -1,6 +1,6 @@
class CreateCurrencyConversions < ActiveRecord::Migration[7.0]
def change
- create_table :currency_conversions do |t|
+ create_table :currency_conversions, if_not_exists: true do |t|
t.decimal :rate
t.string :from_currency
t.string :to_currency
diff --git a/db/migrate/20240318164346_add_enable_public_submission_to_event_types.rb b/db/migrate/20240318164346_add_enable_public_submission_to_event_types.rb
new file mode 100644
index 000000000..f653893c8
--- /dev/null
+++ b/db/migrate/20240318164346_add_enable_public_submission_to_event_types.rb
@@ -0,0 +1,5 @@
+class AddEnablePublicSubmissionToEventTypes < ActiveRecord::Migration[7.0]
+ def change
+ add_column :event_types, :enable_public_submission, :boolean, default: true, null: false
+ end
+end
diff --git a/db/migrate/20240414231910_add_include_committee_to_splashpage.rb b/db/migrate/20240414231910_add_include_committee_to_splashpage.rb
new file mode 100644
index 000000000..87b637c12
--- /dev/null
+++ b/db/migrate/20240414231910_add_include_committee_to_splashpage.rb
@@ -0,0 +1,5 @@
+class AddIncludeCommitteeToSplashpage < ActiveRecord::Migration[7.0]
+ def change
+ add_column :splashpages, :include_committee, :boolean
+ end
+end
diff --git a/db/migrate/20240417212948_set_currency_for_null_ticket_purchase_currency.rb b/db/migrate/20240417212948_set_currency_for_null_ticket_purchase_currency.rb
new file mode 100644
index 000000000..839d594c4
--- /dev/null
+++ b/db/migrate/20240417212948_set_currency_for_null_ticket_purchase_currency.rb
@@ -0,0 +1,5 @@
+class SetCurrencyForNullTicketPurchaseCurrency < ActiveRecord::Migration[7.0]
+ def up
+ # TicketPurchase.where(currency: nil).update_all(currency: 'USD')
+ end
+end
diff --git a/db/migrate/20240422200831_add_amount_paid_cents_to_ticket_purchases.rb b/db/migrate/20240422200831_add_amount_paid_cents_to_ticket_purchases.rb
new file mode 100644
index 000000000..b8176309f
--- /dev/null
+++ b/db/migrate/20240422200831_add_amount_paid_cents_to_ticket_purchases.rb
@@ -0,0 +1,12 @@
+class AddAmountPaidCentsToTicketPurchases < ActiveRecord::Migration[7.0]
+ def up
+ add_column :ticket_purchases, :amount_paid_cents, :integer, default: 0
+
+ TicketPurchase.reset_column_information
+ end
+
+ def down
+ # Remove the amount_paid_cents column if you roll back this migration
+ remove_column :ticket_purchases, :amount_paid_cents
+ end
+end
diff --git a/db/migrate/20240422230019_add_registered_attendees_message_to_conferences.rb b/db/migrate/20240422230019_add_registered_attendees_message_to_conferences.rb
new file mode 100644
index 000000000..579907263
--- /dev/null
+++ b/db/migrate/20240422230019_add_registered_attendees_message_to_conferences.rb
@@ -0,0 +1,5 @@
+class AddRegisteredAttendeesMessageToConferences < ActiveRecord::Migration[7.0]
+ def change
+ add_column :conferences, :registered_attendees_message, :text
+ end
+end
diff --git a/db/migrate/20240801042356_add_default_currency_to_users.rb b/db/migrate/20240801042356_add_default_currency_to_users.rb
new file mode 100644
index 000000000..9684e5a48
--- /dev/null
+++ b/db/migrate/20240801042356_add_default_currency_to_users.rb
@@ -0,0 +1,7 @@
+class AddDefaultCurrencyToUsers < ActiveRecord::Migration[7.0]
+ def change
+ unless ActiveRecord::Base.connection.column_exists?(:users, :default_currency)
+ add_column :users, :default_currency, :text
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c3879f928..18d82ff98 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[7.0].define(version: 2024_03_15_025823) do
+ActiveRecord::Schema[7.0].define(version: 2024_08_01_042356) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "plpgsql"
@@ -109,6 +109,7 @@
t.string "custom_domain"
t.integer "booth_limit", default: 0
t.text "custom_css"
+ t.text "registered_attendees_message"
t.index ["organization_id"], name: "index_conferences_on_organization_id"
end
@@ -234,6 +235,7 @@
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.text "submission_template"
+ t.boolean "enable_public_submission", default: true, null: false
end
create_table "event_users", force: :cascade do |t|
@@ -467,6 +469,7 @@
t.boolean "include_booths"
t.boolean "shuffle_highlights", default: false, null: false
t.boolean "include_happening_now"
+ t.boolean "include_committee"
end
create_table "sponsors", force: :cascade do |t|
@@ -545,6 +548,7 @@
t.integer "week"
t.float "amount_paid", default: 0.0
t.string "currency"
+ t.integer "amount_paid_cents", default: 0
end
create_table "ticket_scannings", force: :cascade do |t|
@@ -624,6 +628,7 @@
t.boolean "is_disabled", default: false
t.string "picture"
t.string "timezone"
+ t.string "default_currency"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
diff --git a/features/conferences/all_events.feature b/features/conferences/all_events.feature
index 4df1fe39b..409c91da1 100644
--- a/features/conferences/all_events.feature
+++ b/features/conferences/all_events.feature
@@ -9,7 +9,7 @@ Scenario: Display events using the conference's timezone
And I should see "All events are currently displayed in PDT (UTC -7)."
And I should see the following data: 8:00 am - 8:30 am PDT, 8:30 am - 9:00 am PDT
And I should see the following data in order: Dates, 2014-05-03, 2014-05-04, 2014-05-05, 2014-05-06, 2014-05-07, Unscheduled
- And I should see the following data in order: 2014-05-03, 08:00 PDT, first_scheduled_event, 8:00 am, first_scheduled_subevent, 08:30 PDT, multiple_speaker_event
+ And I should see the following data in order: 2014-05-03, 08:00 AM PDT, first_scheduled_event, 8:00 am, first_scheduled_subevent, 08:30 AM PDT, multiple_speaker_event
Scenario: Display events using the user's timezone
Given I sign in with username "admin" and password "password123"
@@ -18,4 +18,4 @@ Scenario: Display events using the user's timezone
And I should see "All events are currently displayed in AEST (UTC 10)."
And I should see the following data: 1:00 am - 1:30 am AEST, 1:30 am - 2:00 am AEST
And I should see the following data in order: Dates, 2014-05-04, 2014-05-05, 2014-05-06, 2014-05-07, 2014-05-08, Unscheduled
- And I should see the following data in order: 2014-05-04, 01:00 AEST, first_scheduled_event, 1:00 am, first_scheduled_subevent, 01:30 AEST, multiple_speaker_event
+ And I should see the following data in order: 2014-05-04, 01:00 AM AEST, first_scheduled_event, 1:00 am, first_scheduled_subevent, 01:30 AM AEST, multiple_speaker_event
diff --git a/lib/tasks/migrate_config.rake b/lib/tasks/migrate_config.rake
index 99081611d..7ee42cdc8 100644
--- a/lib/tasks/migrate_config.rake
+++ b/lib/tasks/migrate_config.rake
@@ -40,5 +40,18 @@ namespace :data do
puts "Migrated config/config.yml to .env.#{Rails.env}"
end
+
+ task :update_ticket_purchase_currency do
+ TicketPurchase.find_each do |purchase|
+ converted_amount = CurrencyConversion.convert_currency(
+ purchase.conference,
+ purchase.ticket.price_cents,
+ purchase.price_currency,
+ purchase.currency
+ )
+
+ purchase.update_column(:amount_paid_cents, converted_amount.fractional)
+ end
+ end
end
end
diff --git a/lib/tasks/migrate_currency.rake b/lib/tasks/migrate_currency.rake
new file mode 100644
index 000000000..b05304131
--- /dev/null
+++ b/lib/tasks/migrate_currency.rake
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+namespace :data do
+ namespace :migrate do
+ desc 'Update Currency of past ticket purchases'
+ task :update_ticket_purchase_currency do
+ TicketPurchase.where(currency: nil).update_all(currency: 'USD')
+ TicketPurchase.find_each do |purchase|
+ converted_amount = CurrencyConversion.convert_currency(
+ purchase.conference,
+ purchase.ticket.price_cents,
+ purchase.price_currency,
+ purchase.currency
+ )
+
+ purchase.update_column(:amount_paid_cents, converted_amount)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/conference_registration_controller_spec.rb b/spec/controllers/conference_registration_controller_spec.rb
index a32fe6dbd..dfe04003b 100644
--- a/spec/controllers/conference_registration_controller_spec.rb
+++ b/spec/controllers/conference_registration_controller_spec.rb
@@ -265,28 +265,41 @@
end
end
- context 'user has purchased a ticket' do
+ context 'user has not purchased any ticket' do
before do
- @ticket = create(:ticket, conference: conference)
- @purchased_ticket = create(:ticket_purchase, conference: conference,
- user: user,
- ticket: @ticket)
get :show, params: { conference_id: conference.short_title }
end
- it 'does not assign price of purchased tickets to total_price and purchased tickets to tickets without payment' do
- expect(assigns(:total_price)).to eq 0
+ it 'assigns an empty array to tickets variables' do
+ expect(assigns(:purchases)).to match_array []
end
end
- context 'user has not purchased any ticket' do
+ context 'when user has purchased tickets' do
+ let!(:purchase) { create(:ticket_purchase, user: user, conference: conference, quantity: 2, amount_paid_cents: 10_000, currency: 'USD', paid: true, ticket_id: 1) }
+ let!(:purchase_not_paid) { create(:ticket_purchase, user: user, conference: conference, quantity: 2, amount_paid_cents: 10_000, currency: 'USD') }
+ let!(:purchase_diff_id) { create(:ticket_purchase, user: user, conference: conference, quantity: 1, amount_paid_cents: 10_000, currency: 'USD', paid: true, ticket_id: 2) }
+ let!(:purchase_diff_curr) { create(:ticket_purchase, user: user, conference: conference, quantity: 1, amount_paid_cents: 10_000, currency: 'CAD', paid: true, ticket_id: 1) }
+
before do
+ sign_in user
get :show, params: { conference_id: conference.short_title }
end
- it 'assigns 0 dollars to total_price and empty array to tickets variables' do
- expect(assigns(:total_price)).to eq 0
- expect(assigns(:tickets)).to match_array []
+ it 'assigns @purchases correctly' do
+ expect(assigns(:purchases)).to contain_exactly(purchase, purchase_diff_id, purchase_diff_curr)
+ end
+
+ it 'assigns @total_price_per_ticket_per_currency correctly' do
+ expect(assigns(:total_price_per_ticket_per_currency)).to eq({ [purchase.ticket_id, 'USD'] => Money.new(20_000, 'USD'), [purchase_diff_id.ticket_id, 'USD'] => Money.new(10_000, 'USD'), [purchase_diff_curr.ticket_id, 'CAD'] => Money.new(10_000, 'CAD') })
+ end
+
+ it 'assigns @total_quantity correctly' do
+ expect(assigns(:total_quantity)).to eq({ [purchase.ticket_id, 'USD'] => 2, [purchase_diff_id.ticket_id, 'USD'] => 1, [purchase_diff_curr.ticket_id, 'CAD'] => 1 })
+ end
+
+ it 'assigns @total_price_per_currency correctly' do
+ expect(assigns(:total_price_per_currency)).to eq({ 'USD' => Money.new(30_000, 'USD'), 'CAD' => Money.new(10_000, 'CAD') })
end
end
end
diff --git a/spec/factories/conferences.rb b/spec/factories/conferences.rb
index 336082652..5c1f9377a 100644
--- a/spec/factories/conferences.rb
+++ b/spec/factories/conferences.rb
@@ -4,32 +4,33 @@
#
# Table name: conferences
#
-# id :bigint not null, primary key
-# booth_limit :integer default(0)
-# color :string
-# custom_css :text
-# custom_domain :string
-# description :text
-# end_date :date not null
-# end_hour :integer default(20)
-# events_per_week :text
-# guid :string not null
-# logo_file_name :string
-# picture :string
-# registration_limit :integer default(0)
-# revision :integer default(0), not null
-# short_title :string not null
-# start_date :date not null
-# start_hour :integer default(9)
-# ticket_layout :integer default("portrait")
-# timezone :string not null
-# title :string not null
-# use_vdays :boolean default(FALSE)
-# use_volunteers :boolean
-# use_vpositions :boolean default(FALSE)
-# created_at :datetime
-# updated_at :datetime
-# organization_id :integer
+# id :bigint not null, primary key
+# booth_limit :integer default(0)
+# color :string
+# custom_css :text
+# custom_domain :string
+# description :text
+# end_date :date not null
+# end_hour :integer default(20)
+# events_per_week :text
+# guid :string not null
+# logo_file_name :string
+# picture :string
+# registered_attendees_message :text
+# registration_limit :integer default(0)
+# revision :integer default(0), not null
+# short_title :string not null
+# start_date :date not null
+# start_hour :integer default(9)
+# ticket_layout :integer default("portrait")
+# timezone :string not null
+# title :string not null
+# use_vdays :boolean default(FALSE)
+# use_volunteers :boolean
+# use_vpositions :boolean default(FALSE)
+# created_at :datetime
+# updated_at :datetime
+# organization_id :integer
#
# Indexes
#
diff --git a/spec/factories/event_types.rb b/spec/factories/event_types.rb
index e3801ca14..d8e3ee758 100644
--- a/spec/factories/event_types.rb
+++ b/spec/factories/event_types.rb
@@ -4,17 +4,18 @@
#
# Table name: event_types
#
-# id :bigint not null, primary key
-# color :string
-# description :string
-# length :integer default(30)
-# maximum_abstract_length :integer default(500)
-# minimum_abstract_length :integer default(0)
-# submission_template :text
-# title :string not null
-# created_at :datetime
-# updated_at :datetime
-# program_id :integer
+# id :bigint not null, primary key
+# color :string
+# description :string
+# enable_public_submission :boolean default(TRUE), not null
+# length :integer default(30)
+# maximum_abstract_length :integer default(500)
+# minimum_abstract_length :integer default(0)
+# submission_template :text
+# title :string not null
+# created_at :datetime
+# updated_at :datetime
+# program_id :integer
#
FactoryBot.define do
@@ -22,6 +23,7 @@
title { 'Example Event Type' }
length { 30 }
description { 'Example Event Description\nThis event type is an example.' }
+ enable_public_submission { true }
minimum_abstract_length { 0 }
maximum_abstract_length { 500 }
submission_template { 'Example Event Template _with_ **markdown**' }
diff --git a/spec/factories/splashpages.rb b/spec/factories/splashpages.rb
index 6bb3e70ff..a095cffdb 100644
--- a/spec/factories/splashpages.rb
+++ b/spec/factories/splashpages.rb
@@ -11,6 +11,7 @@
# banner_photo_updated_at :datetime
# include_booths :boolean
# include_cfp :boolean default(FALSE)
+# include_committee :boolean
# include_happening_now :boolean
# include_lodgings :boolean
# include_program :boolean
diff --git a/spec/factories/ticket_purchases.rb b/spec/factories/ticket_purchases.rb
index 1eb1f347d..9f55a3ca7 100644
--- a/spec/factories/ticket_purchases.rb
+++ b/spec/factories/ticket_purchases.rb
@@ -4,17 +4,18 @@
#
# Table name: ticket_purchases
#
-# id :bigint not null, primary key
-# amount_paid :float default(0.0)
-# currency :string
-# paid :boolean default(FALSE)
-# quantity :integer default(1)
-# week :integer
-# created_at :datetime
-# conference_id :integer
-# payment_id :integer
-# ticket_id :integer
-# user_id :integer
+# id :bigint not null, primary key
+# amount_paid :float default(0.0)
+# amount_paid_cents :integer default(0)
+# currency :string
+# paid :boolean default(FALSE)
+# quantity :integer default(1)
+# week :integer
+# created_at :datetime
+# conference_id :integer
+# payment_id :integer
+# ticket_id :integer
+# user_id :integer
#
FactoryBot.define do
factory :ticket_purchase do
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 78784fa4f..f9135376c 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -16,6 +16,7 @@
# confirmed_at :datetime
# current_sign_in_at :datetime
# current_sign_in_ip :string
+# default_currency :string
# email :string default(""), not null
# email_public :boolean default(FALSE)
# encrypted_password :string default(""), not null
diff --git a/spec/features/conference_spec.rb b/spec/features/conference_spec.rb
index 9be0860a5..eb00a0799 100644
--- a/spec/features/conference_spec.rb
+++ b/spec/features/conference_spec.rb
@@ -84,4 +84,38 @@
it_behaves_like 'add and update conference'
end
+
+ describe 'user views' do
+ let!(:conference) { create(:full_conference, description: 'Welcome to this conference!', registered_attendees_message: 'This is an exclusive message!') }
+ let!(:registered_user) { create(:user) }
+ let!(:not_registered_user) { create(:user) }
+ let!(:registration) { create(:registration, user: registered_user, conference: conference) }
+
+ context 'when user is registered for conference' do
+ before do
+ sign_in registered_user
+ visit conference_path(conference.short_title)
+ end
+
+ it 'shows registered attendees message and description' do
+ expect(page).to have_content('Welcome to this conference!')
+ expect(page).to have_content('This is an exclusive message!')
+ end
+ end
+
+ context 'when user is not registered for conference' do
+ before do
+ sign_in not_registered_user
+ visit conference_path(conference)
+ end
+
+ it 'shows conference description' do
+ expect(page).to have_content('Welcome to this conference!')
+ end
+
+ it 'does not show registered attendees message' do
+ expect(page).to have_no_content('This is an exclusive message!')
+ end
+ end
+ end
end
diff --git a/spec/features/event_schedules_spec.rb b/spec/features/event_schedules_spec.rb
new file mode 100644
index 000000000..bbb3eaf20
--- /dev/null
+++ b/spec/features/event_schedules_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe EventSchedule, :js do
+ Timecop.return
+ let(:test_date) { Time.current }
+ let!(:conference) do
+ create(:full_conference, start_date: test_date - 1.hour, end_date: test_date + 5.days, start_hour: 0, end_hour: 24)
+ end
+ let!(:program) { conference.program }
+ let!(:selected_schedule) { create(:schedule, program: program) }
+ let!(:scheduled_event_early) do
+ program.update!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed', abstract: '`markdown`')
+ end
+ let!(:event_schedule_early) do
+ create(:event_schedule, event: scheduled_event_early, schedule: selected_schedule,
+ start_time: test_date - 1.hours)
+ end
+ let!(:scheduled_event_mid) do
+ program.update!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:event_schedule_mid) do
+ create(:event_schedule, event: scheduled_event_mid, schedule: selected_schedule,
+ start_time: test_date)
+ end
+ let!(:scheduled_event_late) do
+ program.update!(selected_schedule: selected_schedule)
+ create(:event, program: program, state: 'confirmed')
+ end
+ let!(:event_schedule_late) do
+ create(:event_schedule, event: scheduled_event_late, schedule: selected_schedule,
+ start_time: test_date + 1.hours)
+ end
+
+ before do
+ login_as(create(:user), scope: :user)
+ visit events_conference_schedule_path(conference_id: conference.short_title, favourites: false)
+ end
+
+ it 'jumps to the closest event' do
+ find('#current-event-btn').click
+ highlighted_element = page.find('.highlighted', visible: true, wait: 1)
+ expect(highlighted_element[:id]).to include("event_#{scheduled_event_mid.id}")
+ end
+end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 9e4108337..808a2827d 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -155,4 +155,22 @@
expect(conference_logo_url(conference2)).to include('2.png')
end
end
+
+ describe '#event_type_sentence' do
+ before do
+ create(:event_type, title: 'Keynote', program: conference.program, enable_public_submission: false)
+ end
+
+ context 'when a user is an admin' do
+ it 'returns a sentence with all event types' do
+ expect(helper.event_types_sentence(conference, true)).to eq 'Talks, Workshops, and Keynotes'
+ end
+ end
+
+ context 'when a user is not an admin' do
+ it 'returns a sentence only event types that allow public submission' do
+ expect(helper.event_types_sentence(conference, false)).to eq 'Talks and Workshops'
+ end
+ end
+ end
end
diff --git a/spec/models/conference_spec.rb b/spec/models/conference_spec.rb
index 0f81d8ae6..e96d69fac 100644
--- a/spec/models/conference_spec.rb
+++ b/spec/models/conference_spec.rb
@@ -4,32 +4,33 @@
#
# Table name: conferences
#
-# id :bigint not null, primary key
-# booth_limit :integer default(0)
-# color :string
-# custom_css :text
-# custom_domain :string
-# description :text
-# end_date :date not null
-# end_hour :integer default(20)
-# events_per_week :text
-# guid :string not null
-# logo_file_name :string
-# picture :string
-# registration_limit :integer default(0)
-# revision :integer default(0), not null
-# short_title :string not null
-# start_date :date not null
-# start_hour :integer default(9)
-# ticket_layout :integer default("portrait")
-# timezone :string not null
-# title :string not null
-# use_vdays :boolean default(FALSE)
-# use_volunteers :boolean
-# use_vpositions :boolean default(FALSE)
-# created_at :datetime
-# updated_at :datetime
-# organization_id :integer
+# id :bigint not null, primary key
+# booth_limit :integer default(0)
+# color :string
+# custom_css :text
+# custom_domain :string
+# description :text
+# end_date :date not null
+# end_hour :integer default(20)
+# events_per_week :text
+# guid :string not null
+# logo_file_name :string
+# picture :string
+# registered_attendees_message :text
+# registration_limit :integer default(0)
+# revision :integer default(0), not null
+# short_title :string not null
+# start_date :date not null
+# start_hour :integer default(9)
+# ticket_layout :integer default("portrait")
+# timezone :string not null
+# title :string not null
+# use_vdays :boolean default(FALSE)
+# use_volunteers :boolean
+# use_vpositions :boolean default(FALSE)
+# created_at :datetime
+# updated_at :datetime
+# organization_id :integer
#
# Indexes
#
diff --git a/spec/models/event_type_spec.rb b/spec/models/event_type_spec.rb
index 2c30203c2..840e84da8 100644
--- a/spec/models/event_type_spec.rb
+++ b/spec/models/event_type_spec.rb
@@ -4,17 +4,18 @@
#
# Table name: event_types
#
-# id :bigint not null, primary key
-# color :string
-# description :string
-# length :integer default(30)
-# maximum_abstract_length :integer default(500)
-# minimum_abstract_length :integer default(0)
-# submission_template :text
-# title :string not null
-# created_at :datetime
-# updated_at :datetime
-# program_id :integer
+# id :bigint not null, primary key
+# color :string
+# description :string
+# enable_public_submission :boolean default(TRUE), not null
+# length :integer default(30)
+# maximum_abstract_length :integer default(500)
+# minimum_abstract_length :integer default(0)
+# submission_template :text
+# title :string not null
+# created_at :datetime
+# updated_at :datetime
+# program_id :integer
#
require 'spec_helper'
@@ -61,5 +62,25 @@
expect(build(:event_type, program: conference.program, length: 37)).not_to be_valid
end
end
+
+ describe 'title' do
+ it 'removes leading and trailing whitespace from title' do
+ event_type = EventType.new(title: ' Movie ')
+ event_type.valid?
+ expect(event_type.title).to eq('Movie')
+ end
+ end
+ end
+
+ describe 'scope :available_for_public' do
+ it 'includes event types with enable_public_submission set to true' do
+ public_event_type = create(:event_type, program: conference.program, enable_public_submission: true)
+ expect(EventType.available_for_public).to include(public_event_type)
+ end
+
+ it 'excludes event types with enable_public_submission set to false' do
+ non_public_event_type = create(:event_type, program: conference.program, enable_public_submission: false)
+ expect(EventType.available_for_public).not_to include(non_public_event_type)
+ end
end
end
diff --git a/spec/models/ticket_purchase_spec.rb b/spec/models/ticket_purchase_spec.rb
index 6a705336d..e5c944a26 100644
--- a/spec/models/ticket_purchase_spec.rb
+++ b/spec/models/ticket_purchase_spec.rb
@@ -4,17 +4,18 @@
#
# Table name: ticket_purchases
#
-# id :bigint not null, primary key
-# amount_paid :float default(0.0)
-# currency :string
-# paid :boolean default(FALSE)
-# quantity :integer default(1)
-# week :integer
-# created_at :datetime
-# conference_id :integer
-# payment_id :integer
-# ticket_id :integer
-# user_id :integer
+# id :bigint not null, primary key
+# amount_paid :float default(0.0)
+# amount_paid_cents :integer default(0)
+# currency :string
+# paid :boolean default(FALSE)
+# quantity :integer default(1)
+# week :integer
+# created_at :datetime
+# conference_id :integer
+# payment_id :integer
+# ticket_id :integer
+# user_id :integer
#
require 'spec_helper'
@@ -67,6 +68,16 @@
end
end
+ describe 'monetization' do
+ let!(:purchase) { create(:ticket_purchase, amount_paid_cents: 1000, currency: 'USD') }
+
+ it 'correctly monetizes amount_paid_cents' do
+ expect(purchase.purchase_price).to be_a(Money)
+ expect(purchase.purchase_price.currency.iso_code).to eq('USD')
+ expect(purchase.purchase_price.fractional).to eq(1000)
+ end
+ end
+
describe 'self#purchase' do
let!(:participant) { create(:user) }
let!(:ticket_1) { create(:ticket) }
diff --git a/spec/models/ticket_spec.rb b/spec/models/ticket_spec.rb
index 36d074afd..9b295a039 100644
--- a/spec/models/ticket_spec.rb
+++ b/spec/models/ticket_spec.rb
@@ -21,7 +21,7 @@
describe Ticket do
let(:conference) { create(:conference) }
- let(:ticket) { create(:ticket, price: 50, price_currency: 'USD', conference: conference) }
+ let(:ticket) { create(:ticket, price: 50, price_currency: 'USD', conference: conference, price_cents: 5_000) }
let(:user) { create(:user) }
describe 'validation' do
@@ -83,13 +83,13 @@
subject { ticket.tickets_turnover_total ticket.id }
let!(:purchase1) do
- create(:ticket_purchase, ticket: ticket, amount_paid: 5_000, quantity: 1, paid: true, user: user)
+ create(:ticket_purchase, ticket: ticket, amount_paid: 50, amount_paid_cents: 5_000, quantity: 1, currency: 'USD', paid: true, user: user)
end
let!(:purchase2) do
- create(:ticket_purchase, ticket: ticket, amount_paid: 5_000, quantity: 2, paid: true, user: user)
+ create(:ticket_purchase, ticket: ticket, amount_paid: 50, amount_paid_cents: 5_000, quantity: 2, currency: 'USD', paid: true, user: user)
end
let!(:purchase3) do
- create(:ticket_purchase, ticket: ticket, amount_paid: 5_000, quantity: 10, paid: false, user: user)
+ create(:ticket_purchase, ticket: ticket, amount_paid: 50, amount_paid_cents: 5_000, quantity: 10, currency: 'USD', paid: false, user: user)
end
it 'returns turnover as Money with ticket\'s currency' do
diff --git a/spec/models/track_spec.rb b/spec/models/track_spec.rb
index 7c8df641a..a054de04b 100644
--- a/spec/models/track_spec.rb
+++ b/spec/models/track_spec.rb
@@ -61,8 +61,6 @@
withdrawn])
}
- it { is_expected.to validate_inclusion_of(:cfp_active).in_array([true, false]) }
-
context 'when self_organized_and_accepted_or_confirmed? returns true' do
before do
allow(subject).to receive(:self_organized_and_accepted_or_confirmed?).and_return(true)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a999ef03a..151acca47 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -16,6 +16,7 @@
# confirmed_at :datetime
# current_sign_in_at :datetime
# current_sign_in_ip :string
+# default_currency :string
# email :string default(""), not null
# email_public :boolean default(FALSE)
# encrypted_password :string default(""), not null
diff --git a/spec/serializers/conference_serializer_spec.rb b/spec/serializers/conference_serializer_spec.rb
index dd81619ff..9931bc72f 100644
--- a/spec/serializers/conference_serializer_spec.rb
+++ b/spec/serializers/conference_serializer_spec.rb
@@ -4,32 +4,33 @@
#
# Table name: conferences
#
-# id :bigint not null, primary key
-# booth_limit :integer default(0)
-# color :string
-# custom_css :text
-# custom_domain :string
-# description :text
-# end_date :date not null
-# end_hour :integer default(20)
-# events_per_week :text
-# guid :string not null
-# logo_file_name :string
-# picture :string
-# registration_limit :integer default(0)
-# revision :integer default(0), not null
-# short_title :string not null
-# start_date :date not null
-# start_hour :integer default(9)
-# ticket_layout :integer default("portrait")
-# timezone :string not null
-# title :string not null
-# use_vdays :boolean default(FALSE)
-# use_volunteers :boolean
-# use_vpositions :boolean default(FALSE)
-# created_at :datetime
-# updated_at :datetime
-# organization_id :integer
+# id :bigint not null, primary key
+# booth_limit :integer default(0)
+# color :string
+# custom_css :text
+# custom_domain :string
+# description :text
+# end_date :date not null
+# end_hour :integer default(20)
+# events_per_week :text
+# guid :string not null
+# logo_file_name :string
+# picture :string
+# registered_attendees_message :text
+# registration_limit :integer default(0)
+# revision :integer default(0), not null
+# short_title :string not null
+# start_date :date not null
+# start_hour :integer default(9)
+# ticket_layout :integer default("portrait")
+# timezone :string not null
+# title :string not null
+# use_vdays :boolean default(FALSE)
+# use_volunteers :boolean
+# use_vpositions :boolean default(FALSE)
+# created_at :datetime
+# updated_at :datetime
+# organization_id :integer
#
# Indexes
#
diff --git a/spec/services/embeddable_url_spec.rb b/spec/services/embeddable_url_spec.rb
new file mode 100644
index 000000000..b88a94a37
--- /dev/null
+++ b/spec/services/embeddable_url_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe EmbeddableURL do
+ describe '#iframe_url' do
+ it 'returns the original url if no transformations apply' do
+ url = 'https://example.com'
+ expect(EmbeddableURL.new(url, 'title').iframe_url).to eq url
+ end
+
+ it 'transforms a Google Drive URL' do
+ url = EmbeddableURL.new('https://docs.google.com/presentation/d/1eGbEQtcOPW2N2P5rKfBVfSo2zn4C307Sh6C7vpJsruE/edit#slide=id.g1088c029399_0_47', 'title').iframe_url
+ expect(url).to include '/embed'
+ expect(url).not_to include('/edit')
+ end
+
+ it 'transforms a Dropbox URL' do
+ url = EmbeddableURL.new('https://www.dropbox.com/scl/fi/49gkp6ghfnxgqex64zvzd/Guzdial-SnapCon23.pdf?rlkey=ecwvmcmfscqtwfq21l3kzqcul&dl=1', 'title').iframe_url
+ expect(url).to include('dl=0')
+ expect(url).not_to include('raw=')
+ end
+
+ it 'transforms a Snap! Project URL' do
+ url = EmbeddableURL.new('https://snap.berkeley.edu/project?username=jedi_force&projectname=Autograder%2dlite', 'title').iframe_url
+ expect(url).to include('/embed')
+ end
+ end
+
+ # it 'parses snap url' do
+ # url = 'https://snap.berkeley.edu/project?username=avi_shor&projectname=stamps'
+ # transformed_url = Commercial.generate_snap_embed(url)
+ # expected_url = 'https://snap.berkeley.edu/embed?projectname=stamps&username=avi_shor&showTitle=true&showAuthor=true&editButton=true&pauseButton=true'
+ # expect(transformed_url).to eq expected_url
+ # end
+ # TODO: Test ifram generation, snap-embedding
+end