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

Communities rsvp #206

Open
wants to merge 1 commit into
base: communities-events
Choose a base branch
from
Open
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
10 changes: 7 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,12 @@ 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 [:create], EventAttendance
can [:update], EventAttendance, :user_id => user.id
can [:close, :reopen], Note
can [:show, :edit, :update], :preference
can [:edit, :update], :profile
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ window.formMapInput = function (id, type) {
if (lat_field.value) {
map.setView([lat_field.value, lon_field.value], 12);
} else {
map.setView([0, 0], 0);
map.setView([0, 0], 2);
}

L.Control.Watermark = L.Control.extend({
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");
}
});
40 changes: 40 additions & 0 deletions app/controllers/event_attendances_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class EventAttendancesController < ApplicationController
layout "site"
before_action :authorize_web
before_action :set_event_attendance, :only => [:update]

authorize_resource

def create
attendance = EventAttendance.new(event_attendance_params)
if attendance.save
redirect_to event_path(attendance.event), :notice => t(".success")
else
redirect_to event_path(attendance.event), :alert => t(".failure")
end
end

def update
respond_to do |format|
if @event_attendance.update(update_params)
format.html { redirect_to @event_attendance.event, :notice => t(".success") }
else
format.html { redirect_to :edit, :alert => t(".failure") }
end
end
end

private

def set_event_attendance
@event_attendance = EventAttendance.find(params[:id])
end

def event_attendance_params
params.require(:event_attendance).permit(:event_id, :user_id, :intention)
end

def update_params
params.require(:event_attendance).permit(:intention)
end
end
99 changes: 99 additions & 0 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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]
@my_attendance = EventAttendance.find_or_initialize_by(:event_id => @event.id, :user_id => current_user&.id)
@yes_check = @my_attendance.intention == EventAttendance::Intentions::YES ? "✓" : ""
@no_check = @my_attendance.intention == EventAttendance::Intentions::NO ? "✓" : ""
@maybe_check = @my_attendance.intention == EventAttendance::Intentions::MAYBE ? "✓" : ""
@yes_disabled = @my_attendance.intention == EventAttendance::Intentions::YES
@no_disabled = @my_attendance.intention == EventAttendance::Intentions::NO
@maybe_disabled = @my_attendance.intention == EventAttendance::Intentions::MAYBE
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
83 changes: 83 additions & 0 deletions app/models/event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# == 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
has_many :event_attendances

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

def attendees(intention)
EventAttendance.where(:event_id => id, :intention => intention)
end

def yes_attendees
attendees(EventAttendance::Intentions::YES)
end

def no_attendees
attendees(EventAttendance::Intentions::NO)
end

def maybe_attendees
attendees(EventAttendance::Intentions::MAYBE)
end
end
35 changes: 35 additions & 0 deletions app/models/event_attendance.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# == Schema Information
#
# Table name: event_attendances
#
# id :bigint(8) not null, primary key
# user_id :bigint(8) not null
# event_id :bigint(8) not null
# intention :enum not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_event_attendances_on_event_id (event_id)
# index_event_attendances_on_user_id (user_id)
# index_event_attendances_on_user_id_and_event_id (user_id,event_id) UNIQUE
#
# Foreign Keys
#
# fk_rails_... (event_id => events.id)
# fk_rails_... (user_id => users.id)
#

class EventAttendance < ApplicationRecord
module Intentions
YES = "Yes".freeze
NO = "No".freeze
MAYBE = "Maybe".freeze
ALL_INTENTIONS = [YES, NO, MAYBE].freeze
end
validates :intention, :inclusion => { :in => Intentions::ALL_INTENTIONS }

belongs_to :event
belongs_to :user
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
15 changes: 15 additions & 0 deletions app/views/communities/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,21 @@
</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
4 changes: 4 additions & 0 deletions app/views/events/_event_property.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<tr>
<th class='py-1 border-secondary-subtle table-secondary fw-normal' dir='auto'><%= name %></th>
<td class='py-1 border-secondary-subtle border-start' dir='auto'><%= value %></td>
</tr>
Loading
Loading