Skip to content

Commit

Permalink
Add events and event organizers including tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
openbrian committed Aug 7, 2024
1 parent ad362bc commit c7c6bfc
Show file tree
Hide file tree
Showing 26 changed files with 1,027 additions and 3 deletions.
8 changes: 5 additions & 3 deletions app/abilities/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def initialize(user)
can [:confirm, :confirm_resend, :confirm_email], :confirmation
can [:index, :rss, :show], DiaryEntry
can :index, DiaryComment
can [:index, :show], Event
can [:index], Note
can [:new, :create, :edit, :update], :password
can [:index, :show], Redaction
Expand Down Expand Up @@ -58,9 +59,10 @@ def initialize(user)
}
can [:create, :new, :step_up], Community
can [:edit, :update], Community, user_is_community_organizer
can [:edit, :create, :destroy, :new, :update], CommunityLink, { :community => user_is_community_organizer }
can [:create], CommunityMember, { :user_id => user.id }
can [:destroy, :edit, :update], CommunityMember, { :community => user_is_community_organizer }
can [:edit, :create, :destroy, :new, :update], CommunityLink, :community => user_is_community_organizer
can [:create, :destroy], CommunityMember, :user_id => user.id
can [:destroy, :edit, :update], CommunityMember, :community => user_is_community_organizer
can [:create, :edit, :new, :update], Event, :community => user_is_community_organizer
can [:close, :reopen], Note
can [:show, :edit, :update], :preference
can [:edit, :update], :profile
Expand Down
9 changes: 9 additions & 0 deletions app/assets/javascripts/event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*global showMap,formMapInput*/

$(document).ready(function () {
if ($("#event_map_form").length) {
formMapInput("event_map_form", "event");
} else if ($("#event_map_show").length) {
showMap("event_map_show");
}
});
92 changes: 92 additions & 0 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
class EventsController < ApplicationController
layout "site"
before_action :authorize_web
before_action :set_event, :only => [:edit, :show, :update]
# This needs to be one before load_and_authorize_resource, so cancancan will be handed
# an event that contains a community, based on the input parameter community_id.
before_action :set_params_for_new, :only => [:new]

load_and_authorize_resource

# GET /events
# GET /events.json
def index
if params[:community_id]
@community = Community.friendly.find(params[:community_id])
@events = @community.events
else
@community = nil
@events = Event.all
end
rescue ActiveRecord::RecordNotFound
@not_found_community = params[:community_id]
render :template => "communities/no_such_community", :status => :not_found
end

# GET /events/1
# GET /events/1.json
def show
@community = Community.friendly.find(params[:community_id]) if params[:community_id]
rescue ActiveRecord::RecordNotFound
@not_found_community = params[:community_id]
render :template => "communities/no_such_community", :status => :not_found
end

# GET /events/new
def new
@title = t ".new"
@event = Event.new(event_params_new)
end

# GET /events/1/edit
def edit; end

# POST /events
# POST /events.json
def create
@event = Event.new(event_params)
@event_organizer = EventOrganizer.new(:event => @event, :user => current_user)

if @event.save && @event_organizer.save
warn_if_event_in_past
redirect_to @event, :notice => t(".success")
else
flash.now[:alert] = t(".failure")
render :new
end
end

def update
if @event.update(event_params)
redirect_to @event, :notice => t(".success")
else
flash.now[:alert] = t(".failure")
render :edit
end
end

private

def warn_if_event_in_past
flash[:warning] = t "events.show.past" if @event.past?
end

def set_event
@event = Event.find(params[:id])
end

def set_params_for_new
@params = event_params_new
end

def event_params
params.require(:event).permit(
:title, :moment, :location, :location_url,
:latitude, :longitude, :description, :community_id
)
end

def event_params_new
params.require(:event).permit(:community_id)
end
end
9 changes: 9 additions & 0 deletions app/helpers/events_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module EventsHelper
def event_location(event)
if event.location_url.present?
link_to event.location, event.location_url
else
event.location
end
end
end
1 change: 1 addition & 0 deletions app/models/community.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Community < ApplicationRecord

belongs_to :leader, :class_name => "User"
has_many :community_links
has_many :events, -> { order(:moment) }, :inverse_of => :community

validates :name, :presence => true, :length => 1..255, :characters => true
validates :description, :presence => true, :length => 1..1023, :characters => true
Expand Down
66 changes: 66 additions & 0 deletions app/models/event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# == Schema Information
#
# Table name: events
#
# id :bigint(8) not null, primary key
# title :string not null
# moment :datetime not null
# location :string not null
# location_url :string
# latitude :float
# longitude :float
# description :text not null
# community_id :bigint(8) not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_events_on_community_id (community_id)
#
# Foreign Keys
#
# fk_rails_... (community_id => communities.id)
#

class Event < ApplicationRecord
belongs_to :community
has_many :event_organizers

scope :future, -> { where(:moment => Time.now.utc..) }
scope :past, -> { where(:moment => ...Time.now.utc) }

validates :moment, :datetime_format => true
validates :location, :length => { :maximum => 255 }, :presence => true
# While latitude and longitude below will implicitly convert blanks to nil,
# the string/url here will not and I don't know why.
validates(
:location_url,
:allow_nil => true, :length => { :maximum => 255 },
:url => { :allow_nil => true, :allow_blank => true, :schemes => ["https"] }
)
validates(
:latitude,
:allow_nil => true,
:numericality => {
:greater_than_or_equal_to => -90,
:less_than_or_equal_to => 90
}
)
validates(
:longitude,
:allow_nil => true,
:numericality => {
:greater_than_or_equal_to => -180,
:less_than_or_equal_to => 180
}
)

def organizers
EventOrganizer.where(:event_id => id)
end

def past?
moment < Time.now.utc
end
end
24 changes: 24 additions & 0 deletions app/models/event_organizer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# == Schema Information
#
# Table name: event_organizers
#
# id :bigint(8) not null, primary key
# event_id :bigint(8) not null
# user_id :bigint(8) not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_event_organizers_on_event_id (event_id)
# index_event_organizers_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (event_id => events.id)
# fk_rails_... (user_id => users.id)
#
class EventOrganizer < ApplicationRecord
belongs_to :event
belongs_to :user
end
16 changes: 16 additions & 0 deletions app/validators/datetime_format_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require "date"

class DatetimeFormatValidator < ActiveModel::EachValidator
# No need to pull in validates_timeless for just a simple validation.
def validate_each(record, attribute, _value)
# By this point in time, rails has already converted an invalid _value to
# Nil. With built in rails validation, there's no good way to say the
# input is not a valid date. Validate the user input.
before_value = record.read_attribute_before_type_cast(attribute)
return if before_value.is_a? Time

Date.iso8601(before_value)
rescue ArgumentError
record.errors.add(attribute, options[:message] || I18n.t("validations.invalid_datetime_range"))
end
end
48 changes: 48 additions & 0 deletions app/views/communities/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,58 @@
</div>
<% end %>
</div>
<div>
<label><%= t(".links") %></label>
<ul>
<% @community.community_links.each do |link| %>
<li>
<%= link_to membership.user.display_name, user_path(membership.user) %>
</li>
<% end %>
</ul>
<% if @community.organizers.empty? %>
<%= link_to t(".step_up"), step_up_path(@community.id), :method => :post %>
<% end %>
</div>
<div>
<% if current_user %>
<% if ! @community.member?(current_user) %>
<%= bootstrap_form_for @current_user_membership do |form| %>
<%= form.hidden_field(:community_id) %>
<%= form.hidden_field(:user_id) %>
<%= form.primary t(".join.action") %>
<% end %>
<% else %>
<%= bootstrap_form_for @current_user_membership, :method => :delete do |form| %>
<%= form.primary t(".leave.action"), :data => { :confirm => t(".leave.confirm", :name => @community.name) } %>
<% end %>
<% end %>
<% else %>
<div>
<%# Just a link here. application_controller will redirect to login. That will be posted. %>
<%= link_to t(".join.action"), login_to_join_path(:community_member => { :community_id => @community.id }), :class => "btn btn-primary btn-sm" %>
</div>
<% end %>
</div>
</div>
</div>
<div class="row">
<div class="col-sm">
<h2>
<%= link_to t(".events"), community_community_events_path(@community) %>
</h2>
<p>
<% if current_user && @community.organizer?(current_user) %>
<%= link_to t(".new_event"), new_event_path(:community_id => @community.id) %>
<% end %>
</p>
<ul>
<% @community.events.future.each do |event| %>
<li>
<%= link_to "#{l(event.moment, :format => '%e %B')} - #{event.title}", community_event_path(@community, event) %>
</li>
<% end %>
</ul>
<h2>
<%= t(".recent_changes") %>
</h2>
Expand Down
48 changes: 48 additions & 0 deletions app/views/events/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<%= javascript_include_tag "event" %>
<%= bootstrap_form_for(@event) do |form| %>
<% if event.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(event.errors.count, "error") %> prohibited this event from being saved:</h2>
<ul>
<% event.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row">
<%= form.text_field :title, :id => :event_title %>
</div>
<div class="row">
<%= form.datetime_local_field :moment, :pattern => "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}" %>
</div>
<div class="row">
<%= form.text_field :location, :id => :event_location %>
</div>
<div class="row">
<%= form.text_field :location_url, :id => :event_location_url %>
</div>
<div class="row">
<%= form.text_area :description, :id => :event_description %>
</div>
<fieldset>
<div class='row'>
<%= form.text_field :latitude, :id => "event_latitude" %>
</div>
<div class='row'>
<%= form.text_field :longitude, :id => "event_longitude" %>
</div>
<div class="event_set_location">
<div id="event_map_form" class="content_map"></div>
</div>
</fieldset>
<% if @event&.community_id %>
<%= form.hidden_field(:community_id, :value => @event.community_id) %>
<% else %>
<div class="row">
<%= collection_select(:event, :community_id, Community.all, :id, :name, :prompt => true) %>
</div>
<% end %>
<%= form.primary %>
<% end %>
25 changes: 25 additions & 0 deletions app/views/events/_index_list.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<% if !events.empty? %>
<h2>
<%= header %>
</h2>
<table class="table table-borderless table-striped">
<thead>
<tr>
<th><%= t(".moment") %></th>
<th><%= t(".title") %></th>
<th><%= t(".location") %></th>
<th><%= t(".community") %></th>
</tr>
</thead>
<tbody>
<% events.each do |event| %>
<tr>
<td><%= l(event.moment, :format => :blog) %></td>
<td><%= link_to event.title, event %></td>
<td><%= event.location %></td>
<td><%= link_to event.community.name, community_path(event.community) %></td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
3 changes: 3 additions & 0 deletions app/views/events/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<h1><%= t(".edit_event") %></h1>

<%= render "form", :event => @event %>
Loading

0 comments on commit c7c6bfc

Please sign in to comment.