Godwin
9 years ago
82 changed files with 1671 additions and 270 deletions
@ -0,0 +1,146 @@ |
|||||
|
|
||||
|
module ScheduleHelper |
||||
|
def get_schedule_data |
||||
|
schedule = Hash.new |
||||
|
workshop_errors = Hash.new |
||||
|
workshop_warnings = Hash.new |
||||
|
|
||||
|
all_events = (@workshops + @events) |
||||
|
conflicts = 0 |
||||
|
errors = 0 |
||||
|
|
||||
|
(0...all_events.count).each do |i| |
||||
|
w = all_events[i] |
||||
|
|
||||
|
if w.start_time.present? && w.end_time.present? && w.event_location_id.present? |
||||
|
type = w.is_a?(Workshop) ? :workshop : :event |
||||
|
|
||||
|
hour = w.start_time.strftime('%H').to_i |
||||
|
hour += 0.5 if w.start_time.strftime('%M').to_i > 15 |
||||
|
|
||||
|
end_hour = w.end_time.strftime('%H').to_i |
||||
|
end_hour += 0.5 if w.end_time.strftime('%M').to_i > 15 |
||||
|
|
||||
|
day = w.conference_day |
||||
|
|
||||
|
schedule[day] ||= Hash.new |
||||
|
schedule[day][:locations] ||= Hash.new |
||||
|
schedule[day][:locations][w.event_location_id] ||= Hash.new |
||||
|
|
||||
|
conflict = nil |
||||
|
(hour...end_hour).step(0.5).each do |h| |
||||
|
h = h.to_i if h == h.to_i |
||||
|
if !conflict && schedule[day][:locations][w.event_location_id][h].present? |
||||
|
conflict = schedule[day][:locations][w.event_location_id][h] |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
if conflict.present? |
||||
|
w_conflict = get_workshop(conflict, @workshops, @events) |
||||
|
workshop_errors[(w_conflict.is_a?(Workshop) ? 'w' : 'e') + w_conflict.id.to_s] = "Time conflict with “<strong>#{w_conflict.title}</strong>”".html_safe |
||||
|
workshop_errors[(w.is_a?(Workshop) ? 'w' : 'e') + w.id.to_s] = "Time conflict with “<strong>#{w.title}</strong>”".html_safe |
||||
|
errors += 1 if workshop_errors[(w_conflict.is_a?(Workshop) ? 'w' : 'e') + w_conflict.id.to_s].nil? |
||||
|
else |
||||
|
schedule[day][:start_time] = hour if schedule[day][:start_time].nil? || hour < schedule[day][:start_time] |
||||
|
schedule[day][:end_time] = end_hour if schedule[day][:end_time].nil? || end_hour > schedule[day][:end_time] |
||||
|
|
||||
|
schedule[day][:locations][w.event_location_id][hour] = { |
||||
|
:span => w.duration / 60.0, |
||||
|
:type => w.is_a?(Event) ? w.event_type : :workshop |
||||
|
} |
||||
|
schedule[day][:locations][w.event_location_id][hour][:workshop] = i if type == :workshop |
||||
|
schedule[day][:locations][w.event_location_id][hour][:event] = (i - @workshops.count) if type == :event |
||||
|
end |
||||
|
else |
||||
|
workshop_warnings["w#{w.id}"] ||= Array.new |
||||
|
workshop_warnings["w#{w.id}"] << (w.is_a?(Workshop) ? "This workshop is not scheduled" : "This event is not scheduled") |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
schedule.each do |day, day_data| |
||||
|
day_data[:locations].each do |location1, location_data1| |
||||
|
location_data1.each do |time1, data1| |
||||
|
day_data[:locations].each do |location2, location_data2| |
||||
|
location_data2.each do |time2, data2| |
||||
|
if data1[:workshop].present? |
||||
|
if data2[:workshop].present? |
||||
|
unless location1 == location2 && time1 == time2 |
||||
|
if workshop_errors[data1[:workshop]].nil? |
||||
|
w1 = @workshops[data1[:workshop]] |
||||
|
w2 = @workshops[data2[:workshop]] |
||||
|
if time1 == time2 |
||||
|
w1.workshop_facilitators.each do |f| |
||||
|
u = User.find(f.user_id) |
||||
|
if w2.active_facilitator?(u) |
||||
|
errors += 1 if workshop_errors[(w2.is_a?(Workshop) ? 'w' : 'e') + w2.id.to_s].nil? |
||||
|
workshop_errors["w#{w1.id}"] = "This workshop shares facilitators with “<strong>#{w2.title}</strong>”".html_safe |
||||
|
end |
||||
|
end |
||||
|
connection ||= ActiveRecord::Base.connection |
||||
|
common_interest_count = connection.select_value("SELECT COUNT(w1.user_id) FROM workshop_interests AS w1 JOIN workshop_interests AS w2 ON w2.user_id=w1.user_id WHERE w1.workshop_id=#{w1.id} AND w2.workshop_id=#{w2.id}") |
||||
|
common_interest_count = common_interest_count ? common_interest_count.to_i : 0 |
||||
|
if common_interest_count > 0 |
||||
|
conflicts += common_interest_count |
||||
|
workshop_warnings["w#{w1.id}"] ||= Array.new |
||||
|
workshop_warnings["w#{w1.id}"] << "<strong>#{common_interest_count}</strong> people are also interested in “<strong>#{w2.title}</strong>”".html_safe |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
{ |
||||
|
:schedule => schedule.sort, |
||||
|
:errors => workshop_errors, |
||||
|
:warnings => workshop_warnings, |
||||
|
:conflict_score => conflicts, |
||||
|
:error_count => errors |
||||
|
} |
||||
|
end |
||||
|
|
||||
|
def get_workshop(workshop, workshops, events) |
||||
|
workshop[:workshop] ? workshops[workshop[:workshop]] : (events[workshop[:event]] || :event) |
||||
|
end |
||||
|
|
||||
|
def workshop_classes(workshop, show_interest) |
||||
|
classes = [workshop.is_a?(Workshop) ? :workshop : workshop.event_type] |
||||
|
if show_interest && workshop.is_a?(Workshop) && current_user && WorkshopInterest.where(:user_id => current_user.id) |
||||
|
if workshop.interested?(current_user) || workshop.facilitator?(current_user) |
||||
|
classes << 'interested' |
||||
|
else |
||||
|
classes << 'not-interested' |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
return classes |
||||
|
end |
||||
|
|
||||
|
def schedule_start_and_end_times(day_part, day_parts, day_schedule) |
||||
|
start_time = [day_parts[day_parts.keys[day_part]], day_schedule[:start_time]].max |
||||
|
end_time = [day_parts[day_parts.keys[day_part + 1]] || 24, day_schedule[:end_time]].min |
||||
|
|
||||
|
min_time = nil |
||||
|
max_time = nil |
||||
|
|
||||
|
day_schedule[:locations].each do |location, location_schedule| |
||||
|
location_schedule.each do |hour, workshop| |
||||
|
t_start = hour |
||||
|
t_end = hour + workshop[:span] |
||||
|
if t_start >= start_time && t_end <= end_time |
||||
|
min_time = [min_time || 24, t_start].min |
||||
|
max_time = [max_time || 0, t_end].max |
||||
|
end |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
return nil unless min_time.present? && max_time.present? |
||||
|
|
||||
|
[min_time || 0, max_time || 24] |
||||
|
end |
||||
|
end |
@ -1,2 +1,17 @@ |
|||||
class Event < ActiveRecord::Base |
class Event < ActiveRecord::Base |
||||
|
belongs_to :conference |
||||
|
|
||||
|
def conference_day |
||||
|
return nil unless start_time.present? && end_time.present? |
||||
|
|
||||
|
start_day = conference.start_date.change(hour: 0, minute: 0, second: 0) |
||||
|
w_start_day = start_time.change(hour: 0, minute: 0, second: 0) |
||||
|
return (((w_start_day - start_day) / 86400) + 1).to_i |
||||
|
end |
||||
|
|
||||
|
def duration |
||||
|
return nil unless start_time.present? && end_time.present? |
||||
|
((end_time - start_time) / 60).to_i |
||||
|
end |
||||
|
|
||||
end |
end |
||||
|
@ -0,0 +1,2 @@ |
|||||
|
class EventLocation < ActiveRecord::Base |
||||
|
end |
@ -0,0 +1,23 @@ |
|||||
|
= render 'conferences/page_header', :page_key => (@location ? 'Edit_Location' : 'New_Location') |
||||
|
%article |
||||
|
= form_tag save_location_path(@this_conference.slug), class: 'composition' do |
||||
|
= row do |
||||
|
= columns(medium: 12) do |
||||
|
= (hidden_field_tag :location_id, @location.id) if @location |
||||
|
.text-field.input-field.big |
||||
|
= label_tag :title |
||||
|
= text_field_tag :title, @location ? @location.title : nil, :required => true |
||||
|
= row do |
||||
|
= columns(medium: 6) do |
||||
|
.text-area-field.input-field |
||||
|
= label_tag :address |
||||
|
= text_area_tag :address, @location ? @location.address : nil, :required => true |
||||
|
= columns(medium: 6) do |
||||
|
%h3=_'articles.locations.headings.amenities' |
||||
|
- [:sound, :projector, :tools].each do |need| |
||||
|
.single-check-box-field.input-field |
||||
|
= label_tag "needs_#{need}" do |
||||
|
= check_box_tag "needs[#{need}]", 1, @location && @amenities.include?(need) |
||||
|
= _"workshop.options.needs.#{need}" |
||||
|
.actions |
||||
|
= button_tag :save, :value => :save |
@ -1,7 +1,22 @@ |
|||||
%h1 Editing event |
= render 'conferences/page_header', :page_key => (@event ? 'Edit_Event' : 'New_Event') |
||||
|
%article |
||||
= render 'form' |
= form_tag save_event_path(@this_conference.slug), class: 'composition' do |
||||
|
= row do |
||||
= link_to 'Show', @event |
= columns(medium: 9) do |
||||
\| |
= (hidden_field_tag :event_id, @event.id) if @event |
||||
= link_to 'Back', events_path |
.text-field.input-field.big |
||||
|
= label_tag :title |
||||
|
= text_field_tag :title, @event ? @event.title : nil, :required => true |
||||
|
= columns(medium: 3) do |
||||
|
.select-field.input-field |
||||
|
= label_tag :event_type do |
||||
|
= select_tag :event_type, options_for_select([:event, :meal], @event ? @event.event_type : nil), :include_blank => true |
||||
|
=_'forms.labels.generic.event_type' |
||||
|
= row do |
||||
|
= columns(medium: 12) do |
||||
|
.text-area-field.input-field |
||||
|
= label_tag :info |
||||
|
= text_area_tag :info, @event ? @event.info : nil, :required => true |
||||
|
= columns(medium: 12) do |
||||
|
.actions.right |
||||
|
= button_tag :save, :value => :save |
||||
|
@ -0,0 +1,36 @@ |
|||||
|
.programme |
||||
|
- schedule.each do |day, day_schedule| |
||||
|
.programme-day |
||||
|
%h2=I18n.l(conference.start_date + (day - 1).days, :format => "%A") |
||||
|
- (0...day_parts.length).each do |day_part| |
||||
|
.programme-day-part |
||||
|
- times = schedule_start_and_end_times(day_part, day_parts, day_schedule) |
||||
|
- if times.present? |
||||
|
%h3=_"articles.headings.schedule.day_parts.#{day_parts.keys[day_part].to_s}" if day_parts.length > 1 |
||||
|
%table.schedule |
||||
|
%tr |
||||
|
%th |
||||
|
- (times.first...times.last).step(0.5).each do |t| |
||||
|
- t = t.to_i if t == t.to_i |
||||
|
%th=I18n.l(Date.today + t.hours, :format => :short) |
||||
|
- day_schedule[:locations].each do |location, location_schedule| |
||||
|
%tr |
||||
|
%th=locations[location.to_s].title |
||||
|
- skip = 0 |
||||
|
- (times.first...times.last).step(0.5).each do |t| |
||||
|
- t = t.to_i if t == t.to_i |
||||
|
- if location_schedule[t].present? |
||||
|
- workshop = location_schedule[t] |
||||
|
- w = get_workshop(workshop, workshops, events) |
||||
|
%td{:class => workshop_classes(w, show_interest) + [show_previews && workshop[:type] == :workshop ? 'previewable' : nil], :colspan => (workshop[:span] * 2), :id => "workshop-#{w.id}"} |
||||
|
.title= w.title |
||||
|
- if show_previews && workshop[:type] == :workshop |
||||
|
.info |
||||
|
%a.close{href: "#!"} |
||||
|
= render 'workshops/show', :workshop => w, :preview => true |
||||
|
%a{class: 'preview', href: "#workshop-#{w.id}"} |
||||
|
- skip = workshop[:span] - 0.5 |
||||
|
- elsif skip > 0 |
||||
|
- skip -= 0.5 |
||||
|
- else |
||||
|
%td.empty |
@ -0,0 +1,77 @@ |
|||||
|
= render 'conferences/page_header', :page_key => 'Edit_Schedule' |
||||
|
%article |
||||
|
= form_tag save_schedule_path(@this_conference.slug), class: 'composition' do |
||||
|
= row do |
||||
|
= columns(medium: 12) do |
||||
|
= render 'schedule/programme', :schedule => @schedule, :conference => @this_conference, :workshops => @workshops, :events => @events, :locations => @location_hash, :show_interest => false, :day_parts => @day_parts, :show_previews => false |
||||
|
= row do |
||||
|
= columns(medium: 12) do |
||||
|
- if @error_count && @error_count > 0 |
||||
|
%h3.errors |
||||
|
=_'errors.schedule.errors',"Errors:" |
||||
|
= @error_count |
||||
|
- if @conflict_score && @conflict_score > 0 |
||||
|
%h3.conflict-score |
||||
|
=_'errors.schedule.conflict_score',"Interest Conflicts:" |
||||
|
= @conflict_score |
||||
|
= row do |
||||
|
= columns(medium: 12) do |
||||
|
= (hidden_field_tag :location_id, @location.id) if @location |
||||
|
.actions |
||||
|
- if @this_conference.workshop_schedule_published |
||||
|
= button_tag :Unpublish, :value => :unpublish, :class => 'delete' |
||||
|
- elsif @error_count < 1 |
||||
|
= button_tag :Publish, :value => :publish |
||||
|
= button_tag :Preview, :value => :preview, :class => 'secondary' |
||||
|
- unless @this_conference.workshop_schedule_published && @error_count > 0 |
||||
|
= button_tag :save, :value => :save |
||||
|
- unless @saved |
||||
|
.unsaved=_'errors.schedule.unsaved','Your changes will not be saved until you press Save or Publish' |
||||
|
= row do |
||||
|
= columns(medium: 6) do |
||||
|
%h2=_"articles.headings.schedule.day_parts.Workshops" |
||||
|
%ul.all-workshops |
||||
|
- @workshops.each do |i| |
||||
|
- error = @errors["w#{i.id}"] |
||||
|
- warnings = @warnings["w#{i.id}"] |
||||
|
%li{class: error.present? ? :error : nil} |
||||
|
%h3=i.title |
||||
|
.workshop-interest=_'articles.workshops.info.interested_count', "#{i.interested_count} people are interested in this workshop", :vars => {:count => i.interested_count} |
||||
|
.time |
||||
|
= select_tag "workshop_day[#{i.id}]", options_for_select(@days, i.conference_day), :include_blank => true |
||||
|
= select_tag "workshop_hour[#{i.id}]", options_for_select(@hours, i.start_time ? i.start_time.strftime('%R') : nil), :include_blank => true |
||||
|
= select_tag "workshop_duration[#{i.id}]", options_for_select(@workshop_durations, i.duration || 60) |
||||
|
.location |
||||
|
= select_tag "workshop_location[#{i.id}]", options_from_collection_for_select(@locations, :id, :title, i.event_location_id), :include_blank => true |
||||
|
- if warnings |
||||
|
%ul.warnings |
||||
|
- warnings.each do |warning| |
||||
|
%li=warning |
||||
|
- if error |
||||
|
.error-description=error |
||||
|
= columns(medium: 6) do |
||||
|
%h2=_"articles.headings.schedule.day_parts.Events" |
||||
|
%ul.all-events |
||||
|
- @events.each do |i| |
||||
|
- error = @errors["e#{i.id}"] |
||||
|
%li{:class => [i.event_type, error.present? ? :error : nil]} |
||||
|
%h3=i.title |
||||
|
.time |
||||
|
= select_tag "event_day[#{i.id}]", options_for_select(@days, i.conference_day) |
||||
|
= select_tag "event_hour[#{i.id}]", options_for_select(@hours, i.start_time ? i.start_time.strftime('%R') : '12:00') |
||||
|
= select_tag "event_duration[#{i.id}]", options_for_select(@event_durations, i.duration || 60) |
||||
|
.location |
||||
|
= select_tag "event_location[#{i.id}]", options_from_collection_for_select(@locations, :id, :title, i.event_location_id), :include_blank => true |
||||
|
- if error |
||||
|
.error-description=error |
||||
|
%h2=_"articles.headings.schedule.day_parts.Day_Parts" |
||||
|
%ul.day_parts |
||||
|
- [:morning, :afternoon, :evening].each do |day_part| |
||||
|
%li |
||||
|
%h4 |
||||
|
=_"articles.headings.schedule.day_parts.#{day_part.to_s}" |
||||
|
- h = (Date.today + @day_parts[day_part.to_s].to_f.hours).strftime('%R') |
||||
|
- if day_part == :morning |
||||
|
.select=h |
||||
|
- else |
||||
|
= select_tag "day_parts[#{day_part.to_s}]", options_for_select(@hours, h) |
@ -0,0 +1,28 @@ |
|||||
|
= render 'conferences/page_header', :page_key => 'Schedule' |
||||
|
%article |
||||
|
- if @this_conference.host?(current_user) |
||||
|
= row do |
||||
|
= columns(medium: 12) do |
||||
|
.actions.left |
||||
|
= link_to (_'actions.schedule.edit','Edit Schedule'), edit_schedule_path(@this_conference.slug), class: [:button] |
||||
|
= row do |
||||
|
= columns(medium: 6) do |
||||
|
%h2=_'articles.schedule.headings.Events' |
||||
|
- if @events |
||||
|
%ul |
||||
|
- @events.each do |event| |
||||
|
%li |
||||
|
%h3=_!event.title |
||||
|
= link_to (_'actions.events.edit','Edit'), edit_event_path(@this_conference.slug, event.id), class: [:button, :modify] |
||||
|
.actions.left |
||||
|
= link_to (_'actions.events.create','Add Event'), add_event_path(@this_conference.slug), class: [:button, :modify] |
||||
|
= columns(medium: 6) do |
||||
|
%h2=_'articles.schedule.headings.Locations' |
||||
|
- if @locations |
||||
|
%ul |
||||
|
- @locations.each do |location| |
||||
|
%li |
||||
|
%h3=_!location.title |
||||
|
= link_to (_'actions.locations.edit','Edit'), edit_location_path(@this_conference.slug, location.id), class: [:button, :modify] |
||||
|
.actions.left |
||||
|
= link_to (_'actions.schedule.edit','Add Location'), add_location_path(@this_conference.slug), class: [:button, :modify] |
@ -0,0 +1,58 @@ |
|||||
|
= row do |
||||
|
= columns(medium: 12) do |
||||
|
%h2=_!workshop.title |
||||
|
.workshop-interest |
||||
|
- if workshop.interested?(current_user) |
||||
|
=_'articles.workshops.info.you_are_interested_count', "You and #{workshop.interested_count - 1} others are interested in this workshop", :vars => {:count => (workshop.interested_count - 1)} |
||||
|
- else |
||||
|
=_'articles.workshops.info.interested_count', "#{workshop.interested_count} people are interested in this workshop", :vars => {:count => workshop.interested_count} |
||||
|
- if !preview && workshop.can_show_interest?(current_user) |
||||
|
= form_tag toggle_workshop_interest_path(workshop.conference.slug, workshop.id) do |
||||
|
= button_tag (workshop.interested?(current_user) ? :remove_interest : :show_interest), :value => :toggle_interest, :class => (workshop.interested?(current_user) ? 'delete' : 'add') |
||||
|
=markdown _!(workshop.info) || '' |
||||
|
- if !preview && logged_in? && current_user.can_translate? |
||||
|
.actions |
||||
|
- I18n.backend.enabled_locales.each do |locale| |
||||
|
= (link_to (_'actions.workshops.Translate', "Translate into #{language_name(locale)}", :vars => {:language => language_name(locale)}), edit_workshop_url(workshop.conference.slug, workshop.id, url_params(locale)), :class => 'button translate') if workshop.can_translate?(current_user, locale) |
||||
|
= columns(medium: 6) do |
||||
|
%h3=_'articles.workshops.headings.facilitators' |
||||
|
.facilitators |
||||
|
- workshop.workshop_facilitators.each do |f| |
||||
|
- u = User.find(f.user_id) |
||||
|
- if logged_in? && (workshop.public_facilitator?(u) || f.user_id == current_user.id || workshop.active_facilitator?(current_user)) |
||||
|
.facilitator |
||||
|
.name=_!(u.firstname || u.username || u.email) |
||||
|
.role |
||||
|
=_"roles.workshops.facilitator.#{workshop.role(u).to_s}" |
||||
|
- if !preview && f.role.to_sym == :requested && workshop.active_facilitator?(current_user) |
||||
|
=(link_to (_'actions.workshops.Approve'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'approve'), :class => 'button modify') |
||||
|
=(link_to (_'actions.workshops.Deny'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'deny'), :class => 'button delete') |
||||
|
- elsif !preview && (f.user_id == current_user.id && f.role.to_sym != :creator) || (!workshop.conference.registered?(u) && workshop.active_facilitator?(current_user)) |
||||
|
=(link_to (_'actions.workshops.Remove'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'remove'), :class => 'button delete') |
||||
|
- if !preview |
||||
|
.actions |
||||
|
=(link_to (_'actions.workshops.Facilitate'), facilitate_workshop_path(workshop.conference.slug, workshop.id), :class => 'button modify') if !workshop.facilitator?(current_user) |
||||
|
- if workshop.active_facilitator?(current_user) |
||||
|
= form_tag workshop_add_facilitator_path(workshop.conference.slug, workshop.id), :class => 'add-facilitator' do |
||||
|
%h4='Add a facilitator' |
||||
|
.email-field.input-field |
||||
|
= email_field_tag :email, nil, required: true |
||||
|
= label_tag :email |
||||
|
= button_tag :add |
||||
|
- if workshop.languages |
||||
|
= columns(medium: 6) do |
||||
|
%h3=_'articles.workshops.headings.languages','Workshop Language' |
||||
|
%p= _!((JSON.parse(workshop.languages || '[]').map { |x| _"languages.#{x}" }).join(', ').to_s.html_safe) |
||||
|
- if workshop.theme |
||||
|
= columns(medium: 6) do |
||||
|
%h3=_'articles.workshops.headings.theme','Theme' |
||||
|
%p= [:race_gender, :mechanics, :funding, :organization, :community].include?((workshop.theme || '').to_sym) ? (_"workshop.options.theme.#{workshop.theme}") : workshop.theme |
||||
|
- if workshop.active_facilitator?(current_user) || workshop.conference.host?(current_user) |
||||
|
- if workshop.needs |
||||
|
= columns(medium: 6) do |
||||
|
%h3=_'articles.workshops.headings.needs','What do you need?' |
||||
|
%p= _!((JSON.parse(workshop.needs || '[]').map { |x| _"workshop.options.needs.#{x}" }).join(', ').to_s.html_safe) |
||||
|
- if workshop.notes |
||||
|
= columns(medium: 12) do |
||||
|
%h3=_'articles.workshops.headings.notes','Notes' |
||||
|
=markdown _!(workshop.notes) |
@ -0,0 +1,248 @@ |
|||||
|
<!DOCTYPE html><html lang="en"><head> |
||||
|
<meta charset="utf-8"> |
||||
|
<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" name="viewport"> |
||||
|
<title>Bike!Bike!</title> |
||||
|
<meta content="Bike!Bike! A conference for bike collectives, co-ops, non-profit DIY bike shops" name="description"> |
||||
|
|
||||
|
<link href="/assets/application/safari-7.css" rel="stylesheet" media="all" type="text/css"><link href="/assets/web-fonts/safari-7.css" rel="stylesheet" media="all" type="text/css"> |
||||
|
<link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon"> |
||||
|
<link href="/assets/favicon.ico" rel="icon" type="image/x-icon"> |
||||
|
<link href="/apple-touch-icon.png" rel="apple-touch-icon"> |
||||
|
<link href="/apple-touch-icon-72x72.png" rel="apple-touch-icon" sizes="72x72"> |
||||
|
<link href="/apple-touch-icon-114x114.png" rel="apple-touch-icon" sizes="114x114"> |
||||
|
<link href="/apple-touch-icon-144x144.png" rel="apple-touch-icon" sizes="144x144"> |
||||
|
|
||||
|
</head> |
||||
|
<body class="home"> |
||||
|
<nav> |
||||
|
<div id="main-nav"> |
||||
|
<div class="inner-nav"> |
||||
|
<a class="logo" href="/"><img src="/assets/bb-icon-logo.png" class="sprite icons bb-icon-logo"> |
||||
|
<img src="/assets/bb-icon-logo-text.png" class="sprite icons bb-icon-logo-text"> |
||||
|
</a><div class="nav"> |
||||
|
<a class="current" href="/"><span>My Bike!Bike!</span></a> |
||||
|
<a href="/about/"><span><span class="translated-content" data-i18n-key="page_titles.About_BikeBike" data-i18n-needs-translation="0">About Bike!Bike!</span></span></a> |
||||
|
<a href="/policy/"><span><span class="translated-content" data-i18n-key="page_titles.Safe_Space_Policy" data-i18n-needs-translation="0">Safer Space Agreement</span></span></a> |
||||
|
</div> |
||||
|
<div class="actions"> |
||||
|
<a class="button register" href="/conferences/MyBikeBike/register/"><span class="translated-content" data-i18n-key="conference.actions.Register" data-i18n-needs-translation="0">Register</span> |
||||
|
</a></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</nav> |
||||
|
|
||||
|
<main id="main"> |
||||
|
<header id="banner"> |
||||
|
<div id="header-title"> |
||||
|
|
||||
|
<div class="cover" style="background-image: url(/assets/default_cover.jpg)"></div> |
||||
|
<div class="title"> |
||||
|
<h2 class="background">2015!</h2> |
||||
|
<div class="details"> |
||||
|
<h3 class="primary">Boise, Idaho</h3> |
||||
|
<div class="secondary"> |
||||
|
August 27 – 31, 2015 |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<figure> |
||||
|
<img src="/assets/default_poster.jpg"> |
||||
|
</figure> |
||||
|
|
||||
|
</header> |
||||
|
<div id="content"> |
||||
|
<article> |
||||
|
<h2>My Bike!Bike!</h2> |
||||
|
Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. |
||||
|
<h3><span class="translated-content" data-i18n-key="articles.workshops.headings.Schedule" data-i18n-needs-translation="1">Schedule</span></h3> |
||||
|
<div class="programme"> |
||||
|
<div class="programme-day"> |
||||
|
<h2>Friday</h2> |
||||
|
<div class="programme-day-part"> |
||||
|
<h3><span class="translated-content" data-i18n-key="articles.headings.schedule.day_parts.morning" data-i18n-needs-translation="1">morning</span></h3> |
||||
|
<table class="schedule"> |
||||
|
<tbody><tr> |
||||
|
<th></th> |
||||
|
<th> 9:00am</th> |
||||
|
<th> 9:30am</th> |
||||
|
<th>10:00am</th> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>The Shop</th> |
||||
|
<td class="workshop previewable" colspan="3.0" id="workshop-1"> |
||||
|
<div class="title">Bikes and Beers</div> |
||||
|
<div class="info"> |
||||
|
<a class="close" href="#!"></a> |
||||
|
<div class="row"><div class="columns medium-12"><h2>Bikes and Beers</h2> |
||||
|
<div class="workshop-interest"> |
||||
|
<span class="translated-content" data-i18n-key="articles.workshops.info.interested_count" data-i18n-needs-translation="0">No one is interested in this workshop yet</span> |
||||
|
</div> |
||||
|
<p>Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.</p> |
||||
|
|
||||
|
</div><div class="columns medium-6"><h3><span class="translated-content" data-i18n-key="articles.workshops.headings.facilitators" data-i18n-needs-translation="0">Facilitators</span></h3> |
||||
|
<div class="facilitators"> |
||||
|
</div> |
||||
|
</div></div> |
||||
|
</div> |
||||
|
<a class="preview" href="#workshop-1"></a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</tbody></table> |
||||
|
</div> |
||||
|
<div class="programme-day-part"> |
||||
|
<h3><span class="translated-content" data-i18n-key="articles.headings.schedule.day_parts.afternoon" data-i18n-needs-translation="1">afternoon</span></h3> |
||||
|
<table class="schedule"> |
||||
|
<tbody><tr> |
||||
|
<th></th> |
||||
|
<th> 2:00pm</th> |
||||
|
<th> 2:30pm</th> |
||||
|
<th> 3:00pm</th> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>The Shop</th> |
||||
|
<td class="workshop previewable" colspan="3.0" id="workshop-3"> |
||||
|
<div class="title">Advocacy Now!</div> |
||||
|
<div class="info"> |
||||
|
<a class="close" href="#!"></a> |
||||
|
<div class="row"><div class="columns medium-12"><h2>Advocacy Now!</h2> |
||||
|
<div class="workshop-interest"> |
||||
|
<span class="translated-content" data-i18n-key="articles.workshops.info.interested_count" data-i18n-needs-translation="0">No one is interested in this workshop yet</span> |
||||
|
</div> |
||||
|
<p>In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.</p> |
||||
|
|
||||
|
</div><div class="columns medium-6"><h3><span class="translated-content" data-i18n-key="articles.workshops.headings.facilitators" data-i18n-needs-translation="0">Facilitators</span></h3> |
||||
|
<div class="facilitators"> |
||||
|
</div> |
||||
|
</div></div> |
||||
|
</div> |
||||
|
<a class="preview" href="#workshop-3"></a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</tbody></table> |
||||
|
</div> |
||||
|
<div class="programme-day-part"> |
||||
|
<h3><span class="translated-content" data-i18n-key="articles.headings.schedule.day_parts.evening" data-i18n-needs-translation="1">evening</span></h3> |
||||
|
<table class="schedule"> |
||||
|
<tbody><tr> |
||||
|
<th></th> |
||||
|
<th> 9:00pm</th> |
||||
|
<th> 9:30pm</th> |
||||
|
<th>10:00pm</th> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>The Shop</th> |
||||
|
<td class="workshop previewable" colspan="3.0" id="workshop-4"> |
||||
|
<div class="title">Public Outreach</div> |
||||
|
<div class="info"> |
||||
|
<a class="close" href="#!"></a> |
||||
|
<div class="row"><div class="columns medium-12"><h2>Public Outreach</h2> |
||||
|
<div class="workshop-interest"> |
||||
|
<span class="translated-content" data-i18n-key="articles.workshops.info.interested_count" data-i18n-needs-translation="0">No one is interested in this workshop yet</span> |
||||
|
</div> |
||||
|
<p>Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.</p> |
||||
|
|
||||
|
</div><div class="columns medium-6"><h3><span class="translated-content" data-i18n-key="articles.workshops.headings.facilitators" data-i18n-needs-translation="0">Facilitators</span></h3> |
||||
|
<div class="facilitators"> |
||||
|
</div> |
||||
|
</div></div> |
||||
|
</div> |
||||
|
<a class="preview" href="#workshop-4"></a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</tbody></table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</article> |
||||
|
</div> |
||||
|
</main> |
||||
|
|
||||
|
<div id="footer"> |
||||
|
<footer><div class="github"> |
||||
|
<img src="/assets/bb-icon-github.png" class="sprite icons bb-icon-github"> |
||||
|
<a href="https://github.com/bikebike/BikeBike/issues" target="_blank"> |
||||
|
<span class="translated-content" data-i18n-key="links.footer.text.File_an_Issue" data-i18n-needs-translation="0">File an issue</span> |
||||
|
</a> |
||||
|
| |
||||
|
<a href="https://github.com/bikebike/BikeBike/contributing.md" target="_blank"> |
||||
|
<span class="translated-content" data-i18n-key="links.footer.text.Help_contribute" data-i18n-needs-translation="0">Help contribute</span> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="facebook"> |
||||
|
<span class="translated-contentblock" data-i18n-key="links.footer.help_text.facebook" data-i18n-needs-translation="0"><a href="https://www.facebook.com/groups/648758205249998/" target="_blank" title="Join our Facebook group"> |
||||
|
<img src="/assets/bb-icon-fb.png" class="sprite icons bb-icon-fb"> |
||||
|
</a> |
||||
|
</span></div> |
||||
|
<div class="locale"> |
||||
|
<span class="translated-contentblock" data-i18n-key="links.footer.help_text.select_language" data-i18n-needs-translation="0"><input id="select-language" type="checkbox"> |
||||
|
<label class="launch" for="select-language" title="Change your language"> |
||||
|
EN |
||||
|
</label> |
||||
|
<div class="selector"> |
||||
|
<ul> |
||||
|
<li> |
||||
|
<a href="/?lang=en"> |
||||
|
EN |
||||
|
</a> |
||||
|
<span>English</span> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="/?lang=es"> |
||||
|
ES |
||||
|
</a> |
||||
|
<span>español</span> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="/?lang=fr"> |
||||
|
FR |
||||
|
</a> |
||||
|
<span>français</span> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</span></div> |
||||
|
<div class="copy"> |
||||
|
<span class="translated-contentblock" data-i18n-key="links.footer.help_text.contributors" data-i18n-needs-translation="0"><a title="Contributors" href="/humans.txt">©2015 Bike!Bike! |
||||
|
</a></span></div> |
||||
|
</footer> |
||||
|
</div> |
||||
|
<script> |
||||
|
(function() { |
||||
|
if (!String.prototype.trim) { |
||||
|
(function() { |
||||
|
var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; |
||||
|
String.prototype.trim = function() { |
||||
|
return this.replace(rtrim, ''); |
||||
|
}; |
||||
|
})(); |
||||
|
} |
||||
|
|
||||
|
function classExists(elem,className){var p = new RegExp('(^| )'+className+'( |$)');return (elem.className && elem.className.match(p));} |
||||
|
function addClass(elem,className){if(classExists(elem,className)){return true;}elem.className += ' '+className;} |
||||
|
function removeClass(elem,className){var c = elem.className;var p = new RegExp('(^| )'+className+'( |$)');c = c.replace(p,' ').replace(/ /g,' ');elem.className = c.trim();} |
||||
|
|
||||
|
[].slice.call(document.querySelectorAll('.input-field input')).forEach(function(inputEl) { |
||||
|
if (inputEl.value.trim() === '') { |
||||
|
inputEl.parentNode.className = inputEl.parentNode.className + ' empty'; |
||||
|
} |
||||
|
|
||||
|
inputEl.addEventListener('focus', onInputFocus); |
||||
|
inputEl.addEventListener('blur', onInputBlur); |
||||
|
}); |
||||
|
|
||||
|
function onInputFocus(ev) { |
||||
|
removeClass(ev.target.parentNode, 'empty') |
||||
|
} |
||||
|
|
||||
|
function onInputBlur(ev) { |
||||
|
if (ev.target.value.trim() === '') { |
||||
|
addClass(ev.target.parentNode, 'empty'); |
||||
|
} |
||||
|
} |
||||
|
})(); |
||||
|
</script> |
||||
|
|
||||
|
|
||||
|
</body></html> |
@ -1,38 +1,54 @@ |
|||||
BikeBike::Application.routes.draw do |
BikeBike::Application.routes.draw do |
||||
|
|
||||
get '/organizations/json' => 'organizations#json', :as => :organizations_json |
get '/organizations/json' => 'organizations#json', :as => :organizations_json |
||||
|
|
||||
|
get '/conferences/:slug/edit' => 'conferences#edit', :as => :edit_conference |
||||
|
post '/conferences/:slug/save' => 'conferences#save', :as => :save_conference |
||||
|
|
||||
match '/conferences/:slug/register' => 'conferences#register', :as => :register, via: [:get, :post] |
match '/conferences/:slug/register' => 'conferences#register', :as => :register, via: [:get, :post] |
||||
match '/conferences/:slug/broadcast' => 'conferences#broadcast', :as => :broadcast, via: [:get, :post] |
match '/conferences/:slug/broadcast' => 'conferences#broadcast', :as => :broadcast, via: [:get, :post] |
||||
get '/conferences/:slug/stats' => 'conferences#stats', :as => :stats |
get '/conferences/:slug/stats' => 'conferences#stats', :as => :stats |
||||
get '/conferences/:slug/register/:button/:confirmation_token' => 'conferences#register', :as => :register_paypal_confirm |
get '/conferences/:slug/register/:button/:confirmation_token' => 'conferences#register', :as => :register_paypal_confirm |
||||
get '/conferences/:slug/workshops' => 'conferences#workshops', :as => :workshops |
|
||||
|
get '/conferences/:slug/schedule' => 'conferences#schedule', :as => :schedule |
||||
|
get '/conferences/:slug/schedule/edit' => 'conferences#edit_schedule', :as => :edit_schedule |
||||
|
post '/conferences/:slug/schedule/save' => 'conferences#save_schedule', :as => :save_schedule |
||||
|
|
||||
|
get '/conferences/:slug/schedule/location/add' => 'conferences#add_location', :as => :add_location |
||||
|
post '/conferences/:slug/schedule/location/save' => 'conferences#save_location', :as => :save_location |
||||
|
get '/conferences/:slug/schedule/location/:id' => 'conferences#view_location', :as => :view_location |
||||
|
get '/conferences/:slug/schedule/location/:id/edit' => 'conferences#edit_location', :as => :edit_location |
||||
|
|
||||
|
get '/conferences/:slug/schedule/event/add' => 'conferences#add_event', :as => :add_event |
||||
|
post '/conferences/:slug/schedule/event/save' => 'conferences#save_event', :as => :save_event |
||||
|
get '/conferences/:slug/schedule/event/:id' => 'conferences#view_event', :as => :view_event |
||||
|
get '/conferences/:slug/schedule/event/:id/edit' => 'conferences#edit_event', :as => :edit_event |
||||
|
|
||||
|
get '/conferences/:slug/workshops' => 'conferences#workshops', :as => :workshops |
||||
match '/conferences/:slug/workshops/create' => 'conferences#create_workshop', :as => :create_workshop, via: [:get, :post] |
match '/conferences/:slug/workshops/create' => 'conferences#create_workshop', :as => :create_workshop, via: [:get, :post] |
||||
post '/conferences/:slug/workshops/save' => 'conferences#save_workshop', :as => :save_workshop |
post '/conferences/:slug/workshops/save' => 'conferences#save_workshop', :as => :save_workshop |
||||
get '/conferences/:slug/workshops/:workshop_id' => 'conferences#view_workshop', :as => :view_workshop |
get '/conferences/:slug/workshops/:workshop_id' => 'conferences#view_workshop', :as => :view_workshop |
||||
post '/conferences/:slug/workshops/:workshop_id/toggle-interest' => 'conferences#toggle_workshop_interest', :as => :toggle_workshop_interest |
post '/conferences/:slug/workshops/:workshop_id/toggle-interest' => 'conferences#toggle_workshop_interest', :as => :toggle_workshop_interest |
||||
match '/conferences/:slug/workshops/:workshop_id/edit' => 'conferences#edit_workshop', :as => :edit_workshop, via: [:get, :post] |
match '/conferences/:slug/workshops/:workshop_id/edit' => 'conferences#edit_workshop', :as => :edit_workshop, via: [:get, :post] |
||||
match '/conferences/:slug/workshops/:workshop_id/delete' => 'conferences#delete_workshop', :as => :delete_workshop, via: [:get, :post] |
match '/conferences/:slug/workshops/:workshop_id/delete' => 'conferences#delete_workshop', :as => :delete_workshop, via: [:get, :post] |
||||
get '/conferences/:slug/workshops/:workshop_id/facilitate' => 'conferences#facilitate_workshop', :as => :facilitate_workshop |
get '/conferences/:slug/workshops/:workshop_id/facilitate' => 'conferences#facilitate_workshop', :as => :facilitate_workshop |
||||
post '/conferences/:slug/workshops/:workshop_id/facilitate_request' => 'conferences#facilitate_request', :as => :facilitate_workshop_request |
post '/conferences/:slug/workshops/:workshop_id/facilitate_request' => 'conferences#facilitate_request', :as => :facilitate_workshop_request |
||||
get '/conferences/:slug/workshops/:workshop_id/facilitate_request/:user_id/:approve_or_deny' => 'conferences#approve_facilitate_request', :as => :approve_facilitate_workshop_request |
get '/conferences/:slug/workshops/:workshop_id/facilitate_request/:user_id/:approve_or_deny' => 'conferences#approve_facilitate_request', :as => :approve_facilitate_workshop_request |
||||
get '/conferences/:slug/workshops/:workshop_id/facilitate/sent' => 'conferences#sent_facilitate_request', :as => :sent_facilitate_workshop |
get '/conferences/:slug/workshops/:workshop_id/facilitate/sent' => 'conferences#sent_facilitate_request', :as => :sent_facilitate_workshop |
||||
post '/conferences/:slug/workshops/:workshop_id/add_facilitator' => 'conferences#add_workshop_facilitator', :as => :workshop_add_facilitator |
post '/conferences/:slug/workshops/:workshop_id/add_facilitator' => 'conferences#add_workshop_facilitator', :as => :workshop_add_facilitator |
||||
get '/conferences/:slug/edit' => 'conferences#edit', :as => :edit_conference |
|
||||
post '/conferences/:slug/save' => 'conferences#save', :as => :save_conference |
get '/robots.txt' => 'application#robots', :as => :robots_txt |
||||
|
get '/humans.txt' => 'application#humans', :as => :humans_txt |
||||
get '/robots.txt' => 'application#robots', :as => :robots_txt |
|
||||
get '/humans.txt' => 'application#humans', :as => :humans_txt |
get '/confirm/:token' => 'application#confirm', :as => :confirm |
||||
|
|
||||
get '/confirm/:token' => 'application#confirm', :as => :confirm |
|
||||
match '/doconfirm' => 'application#do_confirm', :as => :do_confirm, via: [:get, :post] |
match '/doconfirm' => 'application#do_confirm', :as => :do_confirm, via: [:get, :post] |
||||
#post '/doconfirm' => 'application#do_confirm', :as => :do_confirm |
#post '/doconfirm' => 'application#do_confirm', :as => :do_confirm |
||||
post '/logout' => 'application#user_logout', :as => :logout |
post '/logout' => 'application#user_logout', :as => :logout |
||||
post '/translator-request' => 'application#translator_request', :as => :translator_request |
post '/translator-request' => 'application#translator_request', :as => :translator_request |
||||
|
|
||||
get '/error_404' => 'application#error_404' |
get '/error_404' => 'application#error_404' |
||||
get '/about' => 'application#about', :as => :about |
get '/about' => 'application#about', :as => :about |
||||
get '/policy' => 'application#policy', :as => :policy |
get '/policy' => 'application#policy', :as => :policy |
||||
root 'application#home', :as => :home |
root 'application#home', :as => :home |
||||
|
|
||||
end |
end |
||||
|
@ -0,0 +1,14 @@ |
|||||
|
class CreateEventLocations < ActiveRecord::Migration |
||||
|
def change |
||||
|
create_table :event_locations do |t| |
||||
|
t.string :title |
||||
|
t.integer :conference_id |
||||
|
t.float :latitude |
||||
|
t.float :longitude |
||||
|
t.string :address |
||||
|
t.string :amenities |
||||
|
|
||||
|
t.timestamps null: false |
||||
|
end |
||||
|
end |
||||
|
end |
@ -0,0 +1,5 @@ |
|||||
|
class AddEventLocationIdToWorkshops < ActiveRecord::Migration |
||||
|
def change |
||||
|
add_column :workshops, :event_location_id, :integer |
||||
|
end |
||||
|
end |
@ -0,0 +1,5 @@ |
|||||
|
class AddEventLocationIdToEvents < ActiveRecord::Migration |
||||
|
def change |
||||
|
add_column :events, :event_location_id, :integer |
||||
|
end |
||||
|
end |
@ -0,0 +1,5 @@ |
|||||
|
class AddTypeToEvents < ActiveRecord::Migration |
||||
|
def change |
||||
|
add_column :events, :event_type, :string |
||||
|
end |
||||
|
end |
@ -0,0 +1,5 @@ |
|||||
|
class AddDayPartsToConferences < ActiveRecord::Migration |
||||
|
def change |
||||
|
add_column :conferences, :day_parts, :string |
||||
|
end |
||||
|
end |
@ -0,0 +1,26 @@ |
|||||
|
Feature: Workshop Page |
||||
|
In order to facilitate and attend workshops |
||||
|
As a visitor |
||||
|
|
||||
|
Scenario: View published schedule |
||||
|
Given There is an upcoming conference in Boise ID |
||||
|
And Registration is open |
||||
|
|
||||
|
And a location named The Shop exists |
||||
|
|
||||
|
And a workshop titled Bikes and Beers exists |
||||
|
And the workshop is scheduled for day 2 at 9:00 at The Shop |
||||
|
|
||||
|
And a workshop titled Bike Art exists |
||||
|
And the workshop is scheduled for day 2 at 12:00 at The Shop |
||||
|
|
||||
|
And a workshop titled Advocacy Now! exists |
||||
|
And the workshop is scheduled for day 2 at 14:00 at The Shop |
||||
|
|
||||
|
And a workshop titled Public Outreach exists |
||||
|
And the workshop is scheduled for day 2 at 21:00 at The Shop |
||||
|
|
||||
|
And the workshop schedule is published |
||||
|
|
||||
|
And I am on the landing page |
||||
|
Then I see the Bike!Bike! logo |
Loading…
Reference in new issue