diff --git a/app/abilities/ability.rb b/app/abilities/ability.rb
index 84d2e113e08..313e604e064 100644
--- a/app/abilities/ability.rb
+++ b/app/abilities/ability.rb
@@ -71,7 +71,7 @@ def initialize(user)
can [:create, :destroy], CommunityMember, :user_id => user.id
can [:destroy, :edit, :update], CommunityMember, :community => user_is_community_organizer
can [:destroy], CommunityMember, :user_id => user.id
- can [:new, :create], Event, :community => user_is_community_organizer
+ can [:create, :edit, :new, :update], Event, :community => user_is_community_organizer
if user.moderator?
can [:hide, :hidecomment], DiaryEntry
diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb
index e76e03c9e22..b0604836c5a 100644
--- a/app/controllers/events_controller.rb
+++ b/app/controllers/events_controller.rb
@@ -1,7 +1,7 @@
class EventsController < ApplicationController
layout "site"
before_action :authorize_web
- before_action :set_event, :only => [:edit, :show]
+ 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]
@@ -33,22 +33,29 @@ def new
# POST /events.json
def create
@event = Event.new(event_params)
+ @event_organizer = EventOrganizer.new(:event => @event, :user => current_user)
- respond_to do |format|
- if @event.save
- warn_if_event_in_past
- format.html { redirect_to @event, :notice => t(".success") }
- format.json { render :show, :status => :created, :location => @event }
- else
- format.html { render :new }
- format.json { render :json => @event.errors, :status => :unprocessable_entity }
- end
+ 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
# GET /events/1/edit
def edit; end
+ def update
+ if @event.update(event_params)
+ redirect_to @event, :notice => t(".success")
+ else
+ flash.now[:alert] = t(".failure")
+ render :edit
+ end
+ end
+
# GET /events/1
# GET /events/1.json
def show
diff --git a/app/models/event.rb b/app/models/event.rb
index 1d53f51e001..72ed286967e 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -17,6 +17,7 @@
class Event < ApplicationRecord
belongs_to :community
+ has_many :event_organizers
scope :future, -> { where("moment >= ?", Time.now.utc) }
scope :past, -> { where("moment < ?", Time.now.utc) }
@@ -47,6 +48,10 @@ class Event < ApplicationRecord
}
)
+ def organizers
+ EventOrganizer.where(:event_id => id)
+ end
+
def past?
moment < Time.now.utc
end
diff --git a/app/models/event_organizer.rb b/app/models/event_organizer.rb
new file mode 100644
index 00000000000..0982c1fb4c9
--- /dev/null
+++ b/app/models/event_organizer.rb
@@ -0,0 +1,4 @@
+class EventOrganizer < ApplicationRecord
+ belongs_to :event
+ belongs_to :user
+end
diff --git a/app/views/events/edit.html.erb b/app/views/events/edit.html.erb
new file mode 100644
index 00000000000..82a4785900d
--- /dev/null
+++ b/app/views/events/edit.html.erb
@@ -0,0 +1,3 @@
+
<%= t(".edit_event") %>
+
+<%= render "form", :event => @event %>
diff --git a/app/views/events/show.html.erb b/app/views/events/show.html.erb
index 6e646d8d20a..2d89e2c312e 100644
--- a/app/views/events/show.html.erb
+++ b/app/views/events/show.html.erb
@@ -32,7 +32,9 @@
<%= t(".hosted_by") %>: <%= link_to @event.community.name, community_path(@event.community) %>
- <%= t(".organized_by") %>: TBD
+ <%= t(".organized_by") %>: <% @event.organizers.each do |organizer| %>
+ <%= link_to organizer.user.display_name, user_path(organizer.user) %>
+ <% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index ceaf4bcba13..5a3a7ea5fca 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -702,6 +702,9 @@ en:
events:
create:
success: Event was created successfully.
+ failure: Event was not created.
+ edit:
+ edit_event: Edit Event
index:
description: "Description"
events: "Events"
@@ -726,6 +729,9 @@ en:
organized_by: "Organized by"
past: "Event is in the past."
when: "When"
+ update:
+ success: "The event was successfully updated."
+ failure: "The event was not updated."
friendships:
make_friend:
heading: "Add %{user} as a friend?"
diff --git a/db/migrate/20221008224134_create_event_organizers.rb b/db/migrate/20221008224134_create_event_organizers.rb
new file mode 100644
index 00000000000..3d37351631b
--- /dev/null
+++ b/db/migrate/20221008224134_create_event_organizers.rb
@@ -0,0 +1,10 @@
+class CreateEventOrganizers < ActiveRecord::Migration[7.0]
+ def change
+ create_table :event_organizers do |t|
+ t.references :event, :foreign_key => true, :null => false, :index => true
+ t.references :user, :foreign_key => true, :null => false, :index => true
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 8d7607bdd3b..c8d207ccd1f 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -777,6 +777,38 @@ CREATE TABLE public.diary_entry_subscriptions (
);
+--
+-- Name: event_organizers; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE public.event_organizers (
+ id bigint NOT NULL,
+ event_id bigint NOT NULL,
+ user_id bigint NOT NULL,
+ created_at timestamp(6) without time zone NOT NULL,
+ updated_at timestamp(6) without time zone NOT NULL
+);
+
+
+--
+-- Name: event_organizers_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE public.event_organizers_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: event_organizers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE public.event_organizers_id_seq OWNED BY public.event_organizers.id;
+
+
--
-- Name: events; Type: TABLE; Schema: public; Owner: -
--
@@ -1820,6 +1852,13 @@ ALTER TABLE ONLY public.diary_comments ALTER COLUMN id SET DEFAULT nextval('publ
ALTER TABLE ONLY public.diary_entries ALTER COLUMN id SET DEFAULT nextval('public.diary_entries_id_seq'::regclass);
+--
+-- Name: event_organizers id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.event_organizers ALTER COLUMN id SET DEFAULT nextval('public.event_organizers_id_seq'::regclass);
+
+
--
-- Name: events id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -2151,6 +2190,14 @@ ALTER TABLE ONLY public.diary_entry_subscriptions
ADD CONSTRAINT diary_entry_subscriptions_pkey PRIMARY KEY (user_id, diary_entry_id);
+--
+-- Name: event_organizers event_organizers_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.event_organizers
+ ADD CONSTRAINT event_organizers_pkey PRIMARY KEY (id);
+
+
--
-- Name: events events_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -2715,6 +2762,20 @@ CREATE INDEX index_community_members_on_user_id ON public.community_members USIN
CREATE INDEX index_diary_entry_subscriptions_on_diary_entry_id ON public.diary_entry_subscriptions USING btree (diary_entry_id);
+--
+-- Name: index_event_organizers_on_event_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_event_organizers_on_event_id ON public.event_organizers USING btree (event_id);
+
+
+--
+-- Name: index_event_organizers_on_user_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_event_organizers_on_user_id ON public.event_organizers USING btree (user_id);
+
+
--
-- Name: index_events_on_community_id; Type: INDEX; Schema: public; Owner: -
--
@@ -3346,6 +3407,14 @@ ALTER TABLE ONLY public.active_storage_variant_records
ADD CONSTRAINT fk_rails_993965df05 FOREIGN KEY (blob_id) REFERENCES public.active_storage_blobs(id);
+--
+-- Name: event_organizers fk_rails_b1c2c61554; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.event_organizers
+ ADD CONSTRAINT fk_rails_b1c2c61554 FOREIGN KEY (user_id) REFERENCES public.users(id);
+
+
--
-- Name: oauth_access_grants fk_rails_b4b53e07b8; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3354,6 +3423,14 @@ ALTER TABLE ONLY public.oauth_access_grants
ADD CONSTRAINT fk_rails_b4b53e07b8 FOREIGN KEY (application_id) REFERENCES public.oauth_applications(id) NOT VALID;
+--
+-- Name: event_organizers fk_rails_c1e082c91e; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.event_organizers
+ ADD CONSTRAINT fk_rails_c1e082c91e FOREIGN KEY (event_id) REFERENCES public.events(id);
+
+
--
-- Name: active_storage_attachments fk_rails_c3b3935057; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3764,6 +3841,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20220821143545'),
('20220925043305'),
('20221008144036'),
+('20221008224134'),
('21'),
('22'),
('23'),
diff --git a/test/controllers/events_controller_test.rb b/test/controllers/events_controller_test.rb
index e4ac9f5ded8..a44f8d32d22 100644
--- a/test/controllers/events_controller_test.rb
+++ b/test/controllers/events_controller_test.rb
@@ -207,6 +207,68 @@ def controller_mock.render(_partial)
# assert_equal I18n.t("events.create.failure"), flash[:alert]
end
+ def test_update_as_organizer
+ # arrange
+ cm = create(:community_member, :organizer)
+ session_for(cm.user)
+ e1 = create(:event, :community => cm.community) # original object
+ e2 = build(:event, :community => cm.community) # new data
+ # act
+ put event_url(e1), :params => { :event => e2.as_json }, :xhr => true
+ # assert
+ assert_redirected_to event_path(e1)
+ # TODO: Is it better to use t() to translate?
+ assert_equal "The event was successfully updated.", flash[:notice]
+ e1.reload
+ # Assign the id of e1 to e2, so we can do an equality test easily.
+ e2.id = e1.id
+ assert_equal(e2, e1)
+ end
+
+ def test_update_as_non_organizer
+ # arrange
+ cm = create(:community_member)
+ session_for(cm.user)
+ e1 = create(:event, :community => cm.community) # original object
+ e2 = build(:event, :community => cm.community) # new data
+ # act
+ put event_url(e1), :params => { :event => e2.as_json }, :xhr => true
+ # assert
+ assert_redirected_to :controller => :errors, :action => :forbidden
+ end
+
+ def test_update_put_failure
+ # arrange
+ cm = create(:community_member, :organizer)
+ session_for(cm.user)
+ ev = create(:event, :community => cm.community)
+ def ev.update(_params)
+ false
+ end
+
+ controller_mock = EventsController.new
+ def controller_mock.set_event
+ @event = Event.new
+ end
+
+ def controller_mock.render(_partial)
+ # Can't do assert_equal here.
+ # assert_equal :edit, partial
+ end
+
+ # act
+ EventsController.stub :new, controller_mock do
+ Event.stub :new, ev do
+ assert_difference "Event.count", 0 do
+ put event_url(ev), :params => { :event => ev.as_json }, :xhr => true
+ end
+ end
+ end
+
+ # assert
+ assert_equal I18n.t("events.update.failure"), flash[:alert]
+ end
+
def test_in_past_warns
# arrange
cm = create(:community_member, :organizer)
diff --git a/test/factories/event_organizers.rb b/test/factories/event_organizers.rb
new file mode 100644
index 00000000000..175c60eaf5b
--- /dev/null
+++ b/test/factories/event_organizers.rb
@@ -0,0 +1,6 @@
+FactoryBot.define do
+ factory :event_organizer do
+ event
+ user
+ end
+end
diff --git a/test/helpers/events_helper_test.rb b/test/helpers/events_helper_test.rb
new file mode 100644
index 00000000000..9a89dbcadd8
--- /dev/null
+++ b/test/helpers/events_helper_test.rb
@@ -0,0 +1,15 @@
+require "test_helper"
+
+class EventsHelperTest < ActionView::TestCase
+ def test_event_location_url
+ event = create(:event)
+ location = event_location(event)
+ assert_match %r{^
#{event.location}$}, location
+ end
+
+ def test_event_location_no_url
+ event = create(:event, :location_url => nil)
+ location = event_location(event)
+ assert_match(/^#{event.location}$/, location)
+ end
+end
diff --git a/test/models/event_organizer_test.rb b/test/models/event_organizer_test.rb
new file mode 100644
index 00000000000..0cd6db89b79
--- /dev/null
+++ b/test/models/event_organizer_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class EventOrganizerTest < ActiveSupport::TestCase
+ def test_eventorganizer_validations
+ validate({}, true)
+ end
+end