Add event-scoped retrieval functions for embeds, forms, polls, posts, and quizzes

This commit is contained in:
Alex Lion
2026-04-06 15:08:20 +02:00
parent efe6d6de51
commit c4303d0de2
17 changed files with 516 additions and 154 deletions

View File

@@ -9,6 +9,7 @@
- Fix stored XSS vulnerability in custom embed iframes via input sanitization with attribute whitelisting - Fix stored XSS vulnerability in custom embed iframes via input sanitization with attribute whitelisting
- Fix XSS vulnerability in URL link formatting by escaping user-submitted URLs - Fix XSS vulnerability in URL link formatting by escaping user-submitted URLs
- Fix IDOR on form export endpoint by adding authorization check - Fix IDOR on form export endpoint by adding authorization check
- Fix cross-event IDOR on polls, quizzes, forms, embeds, and posts by enforcing event-scoped resource access in context layer
- Fix atom exhaustion DoS by replacing `String.to_atom/1` on user input with explicit whitelists (8 locations) - Fix atom exhaustion DoS by replacing `String.to_atom/1` on user input with explicit whitelists (8 locations)
- Add rate limiting on authentication endpoints using Hammer 7.0 - Add rate limiting on authentication endpoints using Hammer 7.0

View File

@@ -59,6 +59,23 @@ defmodule Claper.Embeds do
def get_embed!(id, preload \\ []), def get_embed!(id, preload \\ []),
do: Repo.get!(Embed, id) |> Repo.preload(preload) do: Repo.get!(Embed, id) |> Repo.preload(preload)
@doc """
Gets a single embed scoped to the given event.
Returns `nil` if the embed does not exist or does not belong to the event.
"""
def get_embed_for_event(id, event_id, preload \\ []) do
from(e in Embed,
join: pf in assoc(e, :presentation_file),
where: e.id == ^id and pf.event_id == ^event_id
)
|> Repo.one()
|> case do
nil -> nil
embed -> Repo.preload(embed, preload)
end
end
@doc """ @doc """
Gets a single embed for a given position. Gets a single embed for a given position.

View File

@@ -61,6 +61,23 @@ defmodule Claper.Forms do
def get_form!(id, preload \\ []), def get_form!(id, preload \\ []),
do: Repo.get!(Form, id) |> Repo.preload(preload) do: Repo.get!(Form, id) |> Repo.preload(preload)
@doc """
Gets a single form scoped to the given event.
Returns `nil` if the form does not exist or does not belong to the event.
"""
def get_form_for_event(id, event_id, preload \\ []) do
from(f in Form,
join: pf in assoc(f, :presentation_file),
where: f.id == ^id and pf.event_id == ^event_id
)
|> Repo.one()
|> case do
nil -> nil
form -> Repo.preload(form, preload)
end
end
@doc """ @doc """
Gets a single form for a given position. Gets a single form for a given position.
@@ -280,6 +297,20 @@ defmodule Claper.Forms do
def get_form_submit_by_id!(id, preload \\ []), def get_form_submit_by_id!(id, preload \\ []),
do: Repo.get_by!(FormSubmit, id: id) |> Repo.preload(preload) do: Repo.get_by!(FormSubmit, id: id) |> Repo.preload(preload)
@doc """
Gets a single FormSubmit scoped to the given event.
Returns `nil` if the FormSubmit does not exist or does not belong to the event.
"""
def get_form_submit_for_event(id, event_id) do
from(fs in FormSubmit,
join: f in assoc(fs, :form),
join: pf in assoc(f, :presentation_file),
where: fs.id == ^id and pf.event_id == ^event_id
)
|> Repo.one()
end
@doc """ @doc """
Creates or update a FormSubmit. Creates or update a FormSubmit.

View File

@@ -72,6 +72,34 @@ defmodule Claper.Polls do
) )
|> set_percentages() |> set_percentages()
@doc """
Gets a single poll scoped to the given event.
Returns `nil` if the poll does not exist or does not belong to the event.
"""
def get_poll_for_event(id, event_id) do
from(p in Poll,
join: pf in assoc(p, :presentation_file),
where: p.id == ^id and pf.event_id == ^event_id
)
|> Repo.one()
|> case do
nil ->
nil
poll ->
poll
|> Repo.preload(
poll_opts:
from(
o in PollOpt,
order_by: [asc: o.id]
)
)
|> set_percentages()
end
end
@doc """ @doc """
Gets a single poll for a given position. Gets a single poll for a given position.

View File

@@ -120,6 +120,22 @@ defmodule Claper.Posts do
""" """
def get_post!(id, preload \\ []), do: Repo.get_by!(Post, uuid: id) |> Repo.preload(preload) def get_post!(id, preload \\ []), do: Repo.get_by!(Post, uuid: id) |> Repo.preload(preload)
@doc """
Gets a single post scoped to the given event.
Returns `nil` if the post does not exist or does not belong to the event.
"""
def get_post_for_event(uuid, event_id, preload \\ []) do
from(p in Post,
where: p.uuid == ^uuid and p.event_id == ^event_id
)
|> Repo.one()
|> case do
nil -> nil
post -> Repo.preload(post, preload)
end
end
@doc """ @doc """
Creates a post. Creates a post.

View File

@@ -70,6 +70,23 @@ defmodule Claper.Quizzes do
|> set_percentages() |> set_percentages()
end end
@doc """
Gets a single quiz scoped to the given event.
Returns `nil` if the quiz does not exist or does not belong to the event.
"""
def get_quiz_for_event(id, event_id, preload \\ []) do
from(q in Quiz,
join: pf in assoc(q, :presentation_file),
where: q.id == ^id and pf.event_id == ^event_id
)
|> Repo.one()
|> case do
nil -> nil
quiz -> quiz |> Repo.preload(preload) |> set_percentages()
end
end
@doc """ @doc """
Gets a single quiz for a given position. Gets a single quiz for a given position.

View File

@@ -24,10 +24,14 @@ defmodule ClaperWeb.EmbedLive.FormComponent do
@impl true @impl true
def handle_event("delete", %{"id" => id}, socket) do def handle_event("delete", %{"id" => id}, socket) do
embed = Embeds.get_embed!(id) case Embeds.get_embed_for_event(id, socket.assigns.presentation_file.event_id) do
{:ok, _} = Embeds.delete_embed(socket.assigns.event_uuid, embed) nil ->
{:noreply, socket}
{:noreply, socket |> push_navigate(to: socket.assigns.return_to)} embed ->
{:ok, _} = Embeds.delete_embed(socket.assigns.event_uuid, embed)
{:noreply, socket |> push_navigate(to: socket.assigns.return_to)}
end
end end
@impl true @impl true

View File

@@ -76,6 +76,8 @@ defmodule ClaperWeb.EventLive.Manage do
defp leader?(_socket, _event), do: false defp leader?(_socket, _event), do: false
defp event_id(%{assigns: %{event: event}}), do: event.id
@impl true @impl true
def handle_info(%{event: "presence_diff"}, %{assigns: %{event: event}} = socket) do def handle_info(%{event: "presence_diff"}, %{assigns: %{event: event}} = socket) do
attendees = Presence.list("event:#{event.uuid}") attendees = Presence.list("event:#{event.uuid}")
@@ -329,127 +331,175 @@ defmodule ClaperWeb.EventLive.Manage do
end end
def handle_event("poll-set-active", %{"id" => id}, socket) do def handle_event("poll-set-active", %{"id" => id}, socket) do
with poll <- Polls.get_poll!(id), :ok <- Claper.Interactions.enable_interaction(poll) do case Polls.get_poll_for_event(id, event_id(socket)) do
Phoenix.PubSub.broadcast( nil ->
Claper.PubSub, {:noreply, socket}
"event:#{socket.assigns.event.uuid}",
{:current_interaction, poll}
)
{:noreply, poll ->
socket with :ok <- Claper.Interactions.enable_interaction(poll) do
|> assign(:current_interaction, poll) Phoenix.PubSub.broadcast(
|> interactions_at_position(socket.assigns.state.position)} Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, poll}
)
{:noreply,
socket
|> assign(:current_interaction, poll)
|> interactions_at_position(socket.assigns.state.position)}
end
end end
end end
def handle_event("form-set-active", %{"id" => id}, socket) do def handle_event("form-set-active", %{"id" => id}, socket) do
with form <- Forms.get_form!(id), :ok <- Claper.Interactions.enable_interaction(form) do case Forms.get_form_for_event(id, event_id(socket)) do
Phoenix.PubSub.broadcast( nil ->
Claper.PubSub, {:noreply, socket}
"event:#{socket.assigns.event.uuid}",
{:current_interaction, form}
)
{:noreply, form ->
socket with :ok <- Claper.Interactions.enable_interaction(form) do
|> assign(:current_interaction, form) Phoenix.PubSub.broadcast(
|> interactions_at_position(socket.assigns.state.position)} Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, form}
)
{:noreply,
socket
|> assign(:current_interaction, form)
|> interactions_at_position(socket.assigns.state.position)}
end
end end
end end
def handle_event("embed-set-active", %{"id" => id}, socket) do def handle_event("embed-set-active", %{"id" => id}, socket) do
with embed <- Embeds.get_embed!(id), :ok <- Claper.Interactions.enable_interaction(embed) do case Embeds.get_embed_for_event(id, event_id(socket)) do
Phoenix.PubSub.broadcast( nil ->
Claper.PubSub, {:noreply, socket}
"event:#{socket.assigns.event.uuid}",
{:current_interaction, embed}
)
{:noreply, embed ->
socket with :ok <- Claper.Interactions.enable_interaction(embed) do
|> assign(:current_interaction, embed) Phoenix.PubSub.broadcast(
|> interactions_at_position(socket.assigns.state.position)} Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, embed}
)
{:noreply,
socket
|> assign(:current_interaction, embed)
|> interactions_at_position(socket.assigns.state.position)}
end
end end
end end
def handle_event("poll-set-inactive", %{"id" => id}, socket) do def handle_event("poll-set-inactive", %{"id" => id}, socket) do
with poll <- Polls.get_poll!(id), {:ok, _} <- Claper.Interactions.disable_interaction(poll) do case Polls.get_poll_for_event(id, event_id(socket)) do
Phoenix.PubSub.broadcast( nil ->
Claper.PubSub, {:noreply, socket}
"event:#{socket.assigns.event.uuid}",
{:current_interaction, nil}
)
end
{:noreply, poll ->
socket with {:ok, _} <- Claper.Interactions.disable_interaction(poll) do
|> assign(:current_interaction, nil) Phoenix.PubSub.broadcast(
|> interactions_at_position(socket.assigns.state.position)} Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, nil}
)
end
{:noreply,
socket
|> assign(:current_interaction, nil)
|> interactions_at_position(socket.assigns.state.position)}
end
end end
def handle_event("form-set-inactive", %{"id" => id}, socket) do def handle_event("form-set-inactive", %{"id" => id}, socket) do
with form <- Forms.get_form!(id), {:ok, _} <- Claper.Interactions.disable_interaction(form) do case Forms.get_form_for_event(id, event_id(socket)) do
Phoenix.PubSub.broadcast( nil ->
Claper.PubSub, {:noreply, socket}
"event:#{socket.assigns.event.uuid}",
{:current_interaction, nil}
)
end
{:noreply, form ->
socket with {:ok, _} <- Claper.Interactions.disable_interaction(form) do
|> assign(:current_interaction, nil) Phoenix.PubSub.broadcast(
|> interactions_at_position(socket.assigns.state.position)} Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, nil}
)
end
{:noreply,
socket
|> assign(:current_interaction, nil)
|> interactions_at_position(socket.assigns.state.position)}
end
end end
def handle_event("embed-set-inactive", %{"id" => id}, socket) do def handle_event("embed-set-inactive", %{"id" => id}, socket) do
with embed <- Embeds.get_embed!(id), case Embeds.get_embed_for_event(id, event_id(socket)) do
{:ok, _} <- Claper.Interactions.disable_interaction(embed) do nil ->
Phoenix.PubSub.broadcast( {:noreply, socket}
Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, nil}
)
end
{:noreply, embed ->
socket with {:ok, _} <- Claper.Interactions.disable_interaction(embed) do
|> assign(:current_interaction, nil) Phoenix.PubSub.broadcast(
|> interactions_at_position(socket.assigns.state.position)} Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, nil}
)
end
{:noreply,
socket
|> assign(:current_interaction, nil)
|> interactions_at_position(socket.assigns.state.position)}
end
end end
@impl true @impl true
def handle_event("quiz-set-active", %{"id" => id}, socket) do def handle_event("quiz-set-active", %{"id" => id}, socket) do
with quiz <- Quizzes.get_quiz!(id, [:quiz_questions, quiz_questions: :quiz_question_opts]), case Quizzes.get_quiz_for_event(id, event_id(socket), [
:ok <- Claper.Interactions.enable_interaction(quiz) do :quiz_questions,
Phoenix.PubSub.broadcast( quiz_questions: :quiz_question_opts
Claper.PubSub, ]) do
"event:#{socket.assigns.event.uuid}", nil ->
{:current_interaction, quiz} {:noreply, socket}
)
{:noreply, quiz ->
socket with :ok <- Claper.Interactions.enable_interaction(quiz) do
|> assign(:current_interaction, quiz) Phoenix.PubSub.broadcast(
|> interactions_at_position(socket.assigns.state.position)} Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, quiz}
)
{:noreply,
socket
|> assign(:current_interaction, quiz)
|> interactions_at_position(socket.assigns.state.position)}
end
end end
end end
def handle_event("quiz-set-inactive", %{"id" => id}, socket) do def handle_event("quiz-set-inactive", %{"id" => id}, socket) do
with quiz <- Quizzes.get_quiz!(id), case Quizzes.get_quiz_for_event(id, event_id(socket)) do
{:ok, _} <- Claper.Interactions.disable_interaction(quiz) do nil ->
Phoenix.PubSub.broadcast( {:noreply, socket}
Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, nil}
)
end
{:noreply, quiz ->
socket with {:ok, _} <- Claper.Interactions.disable_interaction(quiz) do
|> assign(:current_interaction, nil) Phoenix.PubSub.broadcast(
|> interactions_at_position(socket.assigns.state.position)} Claper.PubSub,
"event:#{socket.assigns.event.uuid}",
{:current_interaction, nil}
)
end
{:noreply,
socket
|> assign(:current_interaction, nil)
|> interactions_at_position(socket.assigns.state.position)}
end
end end
@impl true @impl true
@@ -465,8 +515,10 @@ defmodule ClaperWeb.EventLive.Manage do
@impl true @impl true
def handle_event("pin", %{"id" => id}, socket) do def handle_event("pin", %{"id" => id}, socket) do
post = Claper.Posts.get_post!(id, [:event]) case Claper.Posts.get_post_for_event(id, event_id(socket), [:event]) do
pin(post, socket) nil -> {:noreply, socket}
post -> pin(post, socket)
end
end end
@impl true @impl true
@@ -669,21 +721,26 @@ defmodule ClaperWeb.EventLive.Manage do
@impl true @impl true
def handle_event("delete", %{"id" => id}, socket) do def handle_event("delete", %{"id" => id}, socket) do
post = Claper.Posts.get_post!(id, [:event]) case Claper.Posts.get_post_for_event(id, event_id(socket), [:event]) do
{:ok, _} = Claper.Posts.delete_post(post) nil ->
{:noreply, socket}
updated_socket = post ->
if post.pinned do {:ok, _} = Claper.Posts.delete_post(post)
stream(socket, :pinned_posts, list_pinned_posts(socket, socket.assigns.event.uuid),
reset: true
)
stream(socket, :posts, list_all_posts(socket, socket.assigns.event.uuid), reset: true) updated_socket =
else if post.pinned do
stream(socket, :posts, list_all_posts(socket, socket.assigns.event.uuid), reset: true) stream(socket, :pinned_posts, list_pinned_posts(socket, socket.assigns.event.uuid),
end reset: true
)
{:noreply, updated_socket} stream(socket, :posts, list_all_posts(socket, socket.assigns.event.uuid), reset: true)
else
stream(socket, :posts, list_all_posts(socket, socket.assigns.event.uuid), reset: true)
end
{:noreply, updated_socket}
end
end end
@impl true @impl true
@@ -697,16 +754,21 @@ defmodule ClaperWeb.EventLive.Manage do
end end
@impl true @impl true
def handle_event("delete-form-submit", %{"event-id" => event_id, "id" => id}, socket) do def handle_event("delete-form-submit", %{"id" => id}, socket) do
form = Claper.Forms.get_form_submit_by_id!(id) case Claper.Forms.get_form_submit_for_event(id, event_id(socket)) do
{:ok, _} = Claper.Forms.delete_form_submit(event_id, form) nil ->
{:noreply, socket}
{:noreply, form_submit ->
assign( {:ok, _} = Claper.Forms.delete_form_submit(socket.assigns.event.uuid, form_submit)
socket,
:form_submits, {:noreply,
list_form_submits(socket, socket.assigns.event.presentation_file.id) assign(
)} socket,
:form_submits,
list_form_submits(socket, socket.assigns.event.presentation_file.id)
)}
end
end end
@impl true @impl true
@@ -762,18 +824,26 @@ defmodule ClaperWeb.EventLive.Manage do
@impl true @impl true
def handle_event("delete-poll", %{"id" => id}, socket) do def handle_event("delete-poll", %{"id" => id}, socket) do
poll = Polls.get_poll!(id) case Polls.get_poll_for_event(id, event_id(socket)) do
{:ok, _} = Polls.delete_poll(socket.assigns.event.uuid, poll) nil ->
{:noreply, socket}
{:noreply, socket} poll ->
{:ok, _} = Polls.delete_poll(socket.assigns.event.uuid, poll)
{:noreply, socket}
end
end end
@impl true @impl true
def handle_event("delete-quiz", %{"id" => id}, socket) do def handle_event("delete-quiz", %{"id" => id}, socket) do
quiz = Quizzes.get_quiz!(id) case Quizzes.get_quiz_for_event(id, event_id(socket)) do
{:ok, _} = Quizzes.delete_quiz(socket.assigns.event.uuid, quiz) nil ->
{:noreply, socket}
{:noreply, socket} quiz ->
{:ok, _} = Quizzes.delete_quiz(socket.assigns.event.uuid, quiz)
{:noreply, socket}
end
end end
@impl true @impl true
@@ -826,13 +896,19 @@ defmodule ClaperWeb.EventLive.Manage do
end end
defp apply_action(socket, :edit_poll, %{"id" => id}) do defp apply_action(socket, :edit_poll, %{"id" => id}) do
poll = Polls.get_poll!(id) case Polls.get_poll_for_event(id, event_id(socket)) do
nil ->
socket
|> put_flash(:error, gettext("Resource not found"))
|> push_navigate(to: ~p"/e/#{socket.assigns.event.code}/manage")
socket poll ->
|> assign(:create, "poll") socket
|> assign(:interaction_modal, true) |> assign(:create, "poll")
|> assign(:create_action, :edit) |> assign(:interaction_modal, true)
|> assign(:poll, poll) |> assign(:create_action, :edit)
|> assign(:poll, poll)
end
end end
defp apply_action(socket, :add_form, _params) do defp apply_action(socket, :add_form, _params) do
@@ -859,23 +935,35 @@ defmodule ClaperWeb.EventLive.Manage do
end end
defp apply_action(socket, :edit_form, %{"id" => id}) do defp apply_action(socket, :edit_form, %{"id" => id}) do
form = Forms.get_form!(id) case Forms.get_form_for_event(id, event_id(socket)) do
nil ->
socket
|> put_flash(:error, gettext("Resource not found"))
|> push_navigate(to: ~p"/e/#{socket.assigns.event.code}/manage")
socket form ->
|> assign(:create, "form") socket
|> assign(:interaction_modal, true) |> assign(:create, "form")
|> assign(:create_action, :edit) |> assign(:interaction_modal, true)
|> assign(:form, form) |> assign(:create_action, :edit)
|> assign(:form, form)
end
end end
defp apply_action(socket, :edit_embed, %{"id" => id}) do defp apply_action(socket, :edit_embed, %{"id" => id}) do
embed = Embeds.get_embed!(id) case Embeds.get_embed_for_event(id, event_id(socket)) do
nil ->
socket
|> put_flash(:error, gettext("Resource not found"))
|> push_navigate(to: ~p"/e/#{socket.assigns.event.code}/manage")
socket embed ->
|> assign(:create, "embed") socket
|> assign(:interaction_modal, true) |> assign(:create, "embed")
|> assign(:create_action, :edit) |> assign(:interaction_modal, true)
|> assign(:embed, embed) |> assign(:create_action, :edit)
|> assign(:embed, embed)
end
end end
defp apply_action(socket, :add_quiz, _params) do defp apply_action(socket, :add_quiz, _params) do
@@ -900,13 +988,22 @@ defmodule ClaperWeb.EventLive.Manage do
end end
defp apply_action(socket, :edit_quiz, %{"id" => id}) do defp apply_action(socket, :edit_quiz, %{"id" => id}) do
quiz = Quizzes.get_quiz!(id, [:quiz_questions, quiz_questions: :quiz_question_opts]) case Quizzes.get_quiz_for_event(id, event_id(socket), [
:quiz_questions,
quiz_questions: :quiz_question_opts
]) do
nil ->
socket
|> put_flash(:error, gettext("Resource not found"))
|> push_navigate(to: ~p"/e/#{socket.assigns.event.code}/manage")
socket quiz ->
|> assign(:create, "quiz") socket
|> assign(:interaction_modal, true) |> assign(:create, "quiz")
|> assign(:create_action, :edit) |> assign(:interaction_modal, true)
|> assign(:quiz, quiz) |> assign(:create_action, :edit)
|> assign(:quiz, quiz)
end
end end
defp pin(post, socket) do defp pin(post, socket) do

View File

@@ -1398,7 +1398,6 @@
to: "#", to: "#",
phx_click: "delete-form-submit", phx_click: "delete-form-submit",
phx_value_id: submission.id, phx_value_id: submission.id,
phx_value_event_id: @event.uuid,
data: [confirm: gettext("This cannot be undone, confirm ?")] data: [confirm: gettext("This cannot be undone, confirm ?")]
)} )}
</span> </span>

View File

@@ -17,10 +17,14 @@ defmodule ClaperWeb.FormLive.FormComponent do
@impl true @impl true
def handle_event("delete", %{"id" => id}, socket) do def handle_event("delete", %{"id" => id}, socket) do
form = Forms.get_form!(id) case Forms.get_form_for_event(id, socket.assigns.presentation_file.event_id) do
{:ok, _} = Forms.delete_form(socket.assigns.event_uuid, form) nil ->
{:noreply, socket}
{:noreply, socket |> push_navigate(to: socket.assigns.return_to)} form ->
{:ok, _} = Forms.delete_form(socket.assigns.event_uuid, form)
{:noreply, socket |> push_navigate(to: socket.assigns.return_to)}
end
end end
@impl true @impl true

View File

@@ -17,10 +17,14 @@ defmodule ClaperWeb.PollLive.FormComponent do
@impl true @impl true
def handle_event("delete", %{"id" => id}, socket) do def handle_event("delete", %{"id" => id}, socket) do
poll = Polls.get_poll!(id) case Polls.get_poll_for_event(id, socket.assigns.presentation_file.event_id) do
{:ok, _} = Polls.delete_poll(socket.assigns.event_uuid, poll) nil ->
{:noreply, socket}
{:noreply, socket |> push_navigate(to: socket.assigns.return_to)} poll ->
{:ok, _} = Polls.delete_poll(socket.assigns.event_uuid, poll)
{:noreply, socket |> push_navigate(to: socket.assigns.return_to)}
end
end end
@impl true @impl true

View File

@@ -17,10 +17,17 @@ defmodule ClaperWeb.QuizLive.QuizComponent do
@impl true @impl true
def handle_event("delete", %{"id" => id}, socket) do def handle_event("delete", %{"id" => id}, socket) do
quiz = Quizzes.get_quiz!(id, [:quiz_questions, quiz_questions: :quiz_question_opts]) case Quizzes.get_quiz_for_event(id, socket.assigns.event.id, [
{:ok, _} = Quizzes.delete_quiz(socket.assigns.event.uuid, quiz) :quiz_questions,
quiz_questions: :quiz_question_opts
]) do
nil ->
{:noreply, socket}
{:noreply, socket |> push_navigate(to: socket.assigns.return_to)} quiz ->
{:ok, _} = Quizzes.delete_quiz(socket.assigns.event.uuid, quiz)
{:noreply, socket |> push_navigate(to: socket.assigns.return_to)}
end
end end
@impl true @impl true

View File

@@ -138,5 +138,26 @@ defmodule Claper.EmbedsTest do
embed = embed_fixture(%{presentation_file_id: presentation_file.id}) embed = embed_fixture(%{presentation_file_id: presentation_file.id})
assert %Ecto.Changeset{} = Embeds.change_embed(embed) assert %Ecto.Changeset{} = Embeds.change_embed(embed)
end end
test "get_embed_for_event/3 returns embed when it belongs to the event" do
presentation_file = presentation_file_fixture()
embed = embed_fixture(%{presentation_file_id: presentation_file.id})
fetched_embed = Embeds.get_embed_for_event(embed.id, presentation_file.event_id)
assert fetched_embed.id == embed.id
end
test "get_embed_for_event/3 returns nil when embed belongs to a different event" do
presentation_file_a = presentation_file_fixture()
presentation_file_b = presentation_file_fixture()
embed = embed_fixture(%{presentation_file_id: presentation_file_a.id})
assert is_nil(Embeds.get_embed_for_event(embed.id, presentation_file_b.event_id))
end
test "get_embed_for_event/3 returns nil for nonexistent embed id" do
presentation_file = presentation_file_fixture()
assert is_nil(Embeds.get_embed_for_event(-1, presentation_file.event_id))
end
end end
end end

View File

@@ -102,10 +102,63 @@ defmodule Claper.FormsTest do
form = form_fixture(%{presentation_file_id: presentation_file.id}) form = form_fixture(%{presentation_file_id: presentation_file.id})
assert %Ecto.Changeset{} = Forms.change_form(form) assert %Ecto.Changeset{} = Forms.change_form(form)
end end
test "get_form_for_event/3 returns form when it belongs to the event" do
presentation_file = presentation_file_fixture()
form = form_fixture(%{presentation_file_id: presentation_file.id})
fetched_form = Forms.get_form_for_event(form.id, presentation_file.event_id)
assert fetched_form.id == form.id
end
test "get_form_for_event/3 returns nil when form belongs to a different event" do
presentation_file_a = presentation_file_fixture()
presentation_file_b = presentation_file_fixture()
form = form_fixture(%{presentation_file_id: presentation_file_a.id})
assert is_nil(Forms.get_form_for_event(form.id, presentation_file_b.event_id))
end
test "get_form_for_event/3 returns nil for nonexistent form id" do
presentation_file = presentation_file_fixture()
assert is_nil(Forms.get_form_for_event(-1, presentation_file.event_id))
end
end end
describe "form_submits" do describe "form_submits" do
import Claper.{FormsFixtures, PresentationsFixtures} import Claper.{FormsFixtures, PresentationsFixtures, AccountsFixtures}
test "get_form_submit_for_event/2 returns form submit when it belongs to the event" do
presentation_file = presentation_file_fixture()
form = form_fixture(%{presentation_file_id: presentation_file.id})
user = user_fixture()
{:ok, form_submit} =
Forms.create_form_submit(%{
form_id: form.id,
user_id: user.id,
response: %{"Name" => "Test"}
})
fetched = Forms.get_form_submit_for_event(form_submit.id, presentation_file.event_id)
assert fetched.id == form_submit.id
end
test "get_form_submit_for_event/2 returns nil when form submit belongs to a different event" do
presentation_file_a = presentation_file_fixture()
presentation_file_b = presentation_file_fixture()
form = form_fixture(%{presentation_file_id: presentation_file_a.id})
user = user_fixture()
{:ok, form_submit} =
Forms.create_form_submit(%{
form_id: form.id,
user_id: user.id,
response: %{"Name" => "Test"}
})
assert is_nil(Forms.get_form_submit_for_event(form_submit.id, presentation_file_b.event_id))
end
test "get_form_submit/2 returns the form_submit with given id and user id" do test "get_form_submit/2 returns the form_submit with given id and user id" do
form_submit = form_submit_fixture() form_submit = form_submit_fixture()

View File

@@ -106,6 +106,27 @@ defmodule Claper.PollsTest do
poll = poll_fixture(%{presentation_file_id: presentation_file.id}) poll = poll_fixture(%{presentation_file_id: presentation_file.id})
assert %Ecto.Changeset{} = Polls.change_poll(poll) assert %Ecto.Changeset{} = Polls.change_poll(poll)
end end
test "get_poll_for_event/2 returns poll when it belongs to the event" do
presentation_file = presentation_file_fixture()
poll = poll_fixture(%{presentation_file_id: presentation_file.id})
fetched_poll = Polls.get_poll_for_event(poll.id, presentation_file.event_id)
assert fetched_poll.id == poll.id
end
test "get_poll_for_event/2 returns nil when poll belongs to a different event" do
presentation_file_a = presentation_file_fixture()
presentation_file_b = presentation_file_fixture()
poll = poll_fixture(%{presentation_file_id: presentation_file_a.id})
assert is_nil(Polls.get_poll_for_event(poll.id, presentation_file_b.event_id))
end
test "get_poll_for_event/2 returns nil for nonexistent poll id" do
presentation_file = presentation_file_fixture()
assert is_nil(Polls.get_poll_for_event(-1, presentation_file.event_id))
end
end end
describe "poll_opts" do describe "poll_opts" do

View File

@@ -52,6 +52,27 @@ defmodule Claper.PostsTest do
assert {:ok, %Post{}} = Posts.delete_post(post) assert {:ok, %Post{}} = Posts.delete_post(post)
assert_raise Ecto.NoResultsError, fn -> Posts.get_post!(post.uuid) end assert_raise Ecto.NoResultsError, fn -> Posts.get_post!(post.uuid) end
end end
test "get_post_for_event/3 returns post when it belongs to the event" do
event = event_fixture()
post = post_fixture(%{event: event}, [:event])
fetched_post = Posts.get_post_for_event(post.uuid, event.id, [:event])
assert fetched_post.id == post.id
end
test "get_post_for_event/3 returns nil when post belongs to a different event" do
event_a = event_fixture()
event_b = event_fixture()
post = post_fixture(%{event: event_a})
assert is_nil(Posts.get_post_for_event(post.uuid, event_b.id))
end
test "get_post_for_event/3 returns nil for nonexistent post uuid" do
event = event_fixture()
assert is_nil(Posts.get_post_for_event(Ecto.UUID.generate(), event.id))
end
end end
describe "reactions" do describe "reactions" do

View File

@@ -196,6 +196,27 @@ defmodule Claper.QuizzesTest do
assert length(responses) == 1 assert length(responses) == 1
end end
test "get_quiz_for_event/3 returns quiz when it belongs to the event" do
presentation_file = presentation_file_fixture()
quiz = quiz_fixture(%{presentation_file: presentation_file})
fetched_quiz = Quizzes.get_quiz_for_event(quiz.id, presentation_file.event_id)
assert fetched_quiz.id == quiz.id
end
test "get_quiz_for_event/3 returns nil when quiz belongs to a different event" do
presentation_file_a = presentation_file_fixture()
presentation_file_b = presentation_file_fixture()
quiz = quiz_fixture(%{presentation_file: presentation_file_a})
assert is_nil(Quizzes.get_quiz_for_event(quiz.id, presentation_file_b.event_id))
end
test "get_quiz_for_event/3 returns nil for nonexistent quiz id" do
presentation_file = presentation_file_fixture()
assert is_nil(Quizzes.get_quiz_for_event(-1, presentation_file.event_id))
end
test "submit_quiz/4 with user and duplicate opts deduplicates by id" do test "submit_quiz/4 with user and duplicate opts deduplicates by id" do
quiz = quiz_fixture() quiz = quiz_fixture()
user = user_fixture() user = user_fixture()