Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better Registrations Datatable + Media URL embedding #339

Merged
merged 19 commits into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .haml-lint_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 1 addition & 3 deletions app/assets/javascripts/osem-switch.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
function checkboxSwitch(selector){
$(selector).bootstrapSwitch(

);
$(selector).bootstrapSwitch();

$(selector).on('switchChange.bootstrapSwitch', function(event, state) {
var url = $(this).attr('url') + state;
Expand Down
18 changes: 16 additions & 2 deletions app/datatables/registration_datatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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 }
}
Expand All @@ -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|
{
Expand All @@ -42,15 +54,17 @@ 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)
}
end
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
Expand Down
12 changes: 6 additions & 6 deletions app/models/commercial.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,13 @@ def self.render_from_url(url)
resource = OEmbed::Providers.get(url, maxwidth: 560, maxheight: 315)
{ html: resource.html.html_safe }
rescue StandardError
{ html: iframe_fallback(url) }
{ html: EmbeddableURL.new(url).render_embed.html_safe }
# { error: exception.message }
end
end

def self.iframe_fallback(url)
"<iframe width=560 height=315 frameborder=0 allowfullscreen=true src=\"#{url}\"></iframe>".html_safe
end

def self.read_file(file)
require 'csv'
errors = {}
errors[:no_event] = []
errors[:validation_errors] = []
Expand All @@ -67,7 +64,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
Expand All @@ -76,6 +74,8 @@ def self.read_file(file)
private

def valid_url
return unless url

result = Commercial.render_from_url(url)
errors.add(:base, result[:error]) if result[:error]
end
Expand Down
74 changes: 74 additions & 0 deletions app/services/embeddable_url.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Transform a URL to a version that allows iframes

class EmbeddableURL
attr_accessor :url

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)
self.url = url
end

def render_embed
return render_dropbox if url.include?('dropbox.com')

# TODO-A11Y: Set an iframe title
"<iframe #{DEFAULT_FRAME_ATTRS} src='#{iframe_url}'></iframe>"
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
<div>
<script type="text/javascript" src="https://www.dropbox.com/static/api/2/dropins.js" id="dropboxjs" data-app-key="#{ENV.fetch('DROPBOX_APP_KEY', nil)}"></script>
<a href="#{dropbox(url)}"
class="dropbox-embed" data-height="315px" data-width="560px"></a>
</div>
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)
query = CGI.parse(uri.query)
username = query['username'][0] || query['user'][0]
project = query['projectname'][0] || query['project'][0]
"https://snap.berkeley.edu/embed?projectname=#{project}&username=#{username}&showTitle=true&showAuthor=true&editButton=true&pauseButton=true"
end
end
44 changes: 34 additions & 10 deletions app/views/admin/registrations/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -55,12 +57,12 @@
{
"data": "name",
"className": "truncate",
"render": function(data, type, row) {
var content = '<span data-toggle="tooltip" title="' + data + '">' + data + '</span><br/>';
$.each(row.roles, function(index, role){
content += ' <span class="label label-info">' + role + '</span>'
});
return content;
"render": (data, type, row) => {
return `
<span data-toggle="tooltip" title="${data}">${data}</span>
<br/>
${row.roles.map(role => ` <span class="label label-info">${role}</span>`)}
`;
}
},
{
Expand All @@ -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 `
<span class="sr-only">${data}</span>
<input
type="checkbox"
class="switch-checkbox"
${data ? "checked" : ""}
name="#{@conference.short_title}_${row.id}"
url="${js_url.replace('ROW_ID', row.id)}?attended="
>
`;
}
},
{
"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 '<div class="btn-group">'+
'<a class="btn-primary" href="'+row.edit_url+'">Edit</a>'+
'</div>';
"render": (data, type, row) => {
return `<div class="btn-group">
<a class="btn-primary" href="${row.edit_url}">Edit</a>
</div>`;
}
}
]
Expand Down
6 changes: 3 additions & 3 deletions config/initializers/inflections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion config/puma.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
2 changes: 0 additions & 2 deletions spec/models/track_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
29 changes: 29 additions & 0 deletions spec/services/embeddable_url_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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).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').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').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').iframe_url
expect(url).to include('/embed')
end
end
end
Loading