mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-27 13:47:58 +01:00
forum posting
This commit is contained in:
parent
1f95c04b44
commit
91517b756b
10 changed files with 191 additions and 23 deletions
|
@ -4,22 +4,12 @@ defmodule Philomena.Posts do
|
|||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias Ecto.Multi
|
||||
alias Philomena.Repo
|
||||
|
||||
alias Philomena.Topics.Topic
|
||||
alias Philomena.Posts.Post
|
||||
|
||||
@doc """
|
||||
Returns the list of posts.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_posts()
|
||||
[%Post{}, ...]
|
||||
|
||||
"""
|
||||
def list_posts do
|
||||
Repo.all(Post)
|
||||
end
|
||||
alias Philomena.Notifications
|
||||
|
||||
@doc """
|
||||
Gets a single post.
|
||||
|
@ -49,10 +39,60 @@ defmodule Philomena.Posts do
|
|||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_post(attrs \\ %{}) do
|
||||
%Post{}
|
||||
|> Post.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
def create_post(topic, user, attributes, params \\ %{}) do
|
||||
topic_query =
|
||||
Topic
|
||||
|> where(id: ^topic.id)
|
||||
|
||||
Multi.new
|
||||
|> Multi.run(:post, fn repo, _ ->
|
||||
last_position =
|
||||
Post
|
||||
|> where(topic_id: ^topic.id)
|
||||
|> order_by(desc: :topic_position)
|
||||
|> select([p], p.topic_position)
|
||||
|> limit(1)
|
||||
|> repo.one()
|
||||
|
||||
Ecto.build_assoc(topic, :posts, [topic_position: (last_position || -1) + 1] ++ attributes)
|
||||
|> Post.creation_changeset(user, params)
|
||||
|> repo.insert()
|
||||
end)
|
||||
|> Multi.run(:update_topic, fn repo, %{post: %{id: post_id}} ->
|
||||
{count, nil} =
|
||||
repo.update_all(topic_query, inc: [post_count: 1], set: [last_post_id: post_id])
|
||||
|
||||
{:ok, count}
|
||||
end)
|
||||
|> Repo.isolated_transaction(:serializable)
|
||||
end
|
||||
|
||||
def notify_post(post) do
|
||||
spawn fn ->
|
||||
topic =
|
||||
post
|
||||
|> Repo.preload(:topic)
|
||||
|> Map.fetch!(:topic)
|
||||
|
||||
subscriptions =
|
||||
topic
|
||||
|> Repo.preload(:subscriptions)
|
||||
|> Map.fetch!(:subscriptions)
|
||||
|
||||
Notifications.notify(
|
||||
post,
|
||||
subscriptions,
|
||||
%{
|
||||
actor_id: topic.id,
|
||||
actor_type: "Topic",
|
||||
actor_child_id: post.id,
|
||||
actor_child_type: "Post",
|
||||
action: "posted a new reply in"
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
post
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -101,4 +141,20 @@ defmodule Philomena.Posts do
|
|||
def change_post(%Post{} = post) do
|
||||
Post.changeset(post, %{})
|
||||
end
|
||||
|
||||
def reindex_post(%Post{} = post) do
|
||||
spawn fn ->
|
||||
Post
|
||||
|> preload(^indexing_preloads())
|
||||
|> where(id: ^post.id)
|
||||
|> Repo.one()
|
||||
|> Post.index_document()
|
||||
end
|
||||
|
||||
post
|
||||
end
|
||||
|
||||
def indexing_preloads do
|
||||
[:user, topic: :forum]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,4 +38,18 @@ defmodule Philomena.Posts.Post do
|
|||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
|
||||
@doc false
|
||||
def creation_changeset(post, user, attrs) do
|
||||
post
|
||||
|> cast(attrs, [:body, :anonymous])
|
||||
|> set_name_at_post_time(user)
|
||||
|> validate_required([:body])
|
||||
|> validate_length(:body, min: 1, max: 300_000, count: :bytes)
|
||||
end
|
||||
|
||||
def set_name_at_post_time(changeset, nil), do: changeset
|
||||
def set_name_at_post_time(changeset, %{name: name}) do
|
||||
change(changeset, name_at_post_time: name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,4 +4,24 @@ defmodule Philomena.Repo do
|
|||
adapter: Ecto.Adapters.Postgres
|
||||
|
||||
use Scrivener, page_size: 250
|
||||
|
||||
@levels %{
|
||||
read_committed: "READ COMMITTED",
|
||||
repeatable_read: "REPEATABLE READ",
|
||||
serializable: "SERIALIZABLE"
|
||||
}
|
||||
|
||||
def isolated_transaction(f, level) do
|
||||
Philomena.Repo.transaction(fn ->
|
||||
Philomena.Repo.query!("SET TRANSACTION ISOLATION LEVEL #{@levels[level]}")
|
||||
Philomena.Repo.transaction(f)
|
||||
end)
|
||||
|> case do
|
||||
{:ok, value} ->
|
||||
value
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Philomena.Topics.Topic do
|
|||
alias Philomena.Users.User
|
||||
alias Philomena.Polls.Poll
|
||||
alias Philomena.Posts.Post
|
||||
alias Philomena.Topics.Subscription
|
||||
|
||||
@derive {Phoenix.Param, key: :slug}
|
||||
schema "topics" do
|
||||
|
@ -15,6 +16,8 @@ defmodule Philomena.Topics.Topic do
|
|||
belongs_to :last_post, Post
|
||||
belongs_to :forum, Forum
|
||||
has_one :poll, Poll
|
||||
has_many :posts, Post
|
||||
has_many :subscriptions, Subscription
|
||||
|
||||
field :title, :string
|
||||
field :post_count, :integer, default: 0
|
||||
|
|
|
@ -1,10 +1,51 @@
|
|||
defmodule PhilomenaWeb.Topic.PostController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
#alias Philomena.{Forums.Forum}
|
||||
#alias Philomena.Posts
|
||||
alias Philomena.{Forums.Forum, Topics.Topic, Posts}
|
||||
alias Philomena.Repo
|
||||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CanaryMapPlug, create: :show, edit: :show, update: :show
|
||||
plug :load_and_authorize_resource, model: Forum, id_field: "short_name", id_name: "forum_id", persisted: true
|
||||
plug :load_topic
|
||||
|
||||
def create(conn, %{"post" => post_params}) do
|
||||
attributes = conn.assigns.attributes
|
||||
forum = conn.assigns.forum
|
||||
topic = conn.assigns.topic
|
||||
user = conn.assigns.current_user
|
||||
|
||||
case Posts.create_post(topic, user, attributes, post_params) do
|
||||
{:ok, %{post: post}} ->
|
||||
Posts.notify_post(post)
|
||||
Posts.reindex_post(post)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Post created successfully.")
|
||||
|> redirect(to: Routes.forum_topic_path(conn, :show, forum, topic, post_id: post.id) <> "#post_#{post.id}")
|
||||
|
||||
_error ->
|
||||
conn
|
||||
|> put_flash(:error, "There was an error creating the post")
|
||||
|> redirect(external: conn.assigns.referrer)
|
||||
end
|
||||
end
|
||||
|
||||
defp load_topic(%{params: %{"topic_id" => slug}} = conn, _args) do
|
||||
forum = conn.assigns.forum
|
||||
user = conn.assigns.current_user
|
||||
|
||||
with topic when not is_nil(topic) <- Repo.get_by(Topic, slug: slug, forum_id: forum.id),
|
||||
true <- Canada.Can.can?(user, :show, topic)
|
||||
do
|
||||
conn
|
||||
|> assign(:topic, topic)
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
|> put_flash(:error, "Couldn't access that topic")
|
||||
|> redirect(external: conn.assigns.referrer)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
defmodule PhilomenaWeb.TopicController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.{Forums.Forum, Topics, Topics.Topic, Posts.Post, Textile.Renderer}
|
||||
alias Philomena.{Forums.Forum, Topics, Topics.Topic, Posts, Posts.Post, Textile.Renderer}
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query
|
||||
|
||||
|
@ -54,6 +54,10 @@ defmodule PhilomenaWeb.TopicController do
|
|||
watching =
|
||||
Topics.subscribed?(topic, conn.assigns.current_user)
|
||||
|
||||
render(conn, "show.html", posts: posts, watching: watching)
|
||||
changeset =
|
||||
%Post{}
|
||||
|> Posts.change_post()
|
||||
|
||||
render(conn, "show.html", posts: posts, changeset: changeset, watching: watching)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,7 +86,9 @@ defmodule PhilomenaWeb.Router do
|
|||
resources "/tags", TagController, only: [:index, :show]
|
||||
resources "/search", SearchController, only: [:index]
|
||||
resources "/forums", ForumController, only: [:index, :show] do
|
||||
resources "/topics", TopicController, only: [:show]
|
||||
resources "/topics", TopicController, only: [:show] do
|
||||
resources "/posts", Topic.PostController, only: [:create], singleton: true
|
||||
end
|
||||
end
|
||||
resources "/comments", CommentController, only: [:index]
|
||||
|
||||
|
|
25
lib/philomena_web/templates/topic/post/_form.html.slime
Normal file
25
lib/philomena_web/templates/topic/post/_form.html.slime
Normal file
|
@ -0,0 +1,25 @@
|
|||
= form_for @changeset, Routes.forum_topic_post_path(@conn, :create, @forum, @topic), fn f ->
|
||||
= if @changeset.action do
|
||||
.alert.alert-danger
|
||||
p Oops, something went wrong! Please check the errors below.
|
||||
|
||||
.block
|
||||
.block__header.block__header--js-tabbed
|
||||
a.selected href="#" data-click-tab="write"
|
||||
i.fa.fa-pencil>
|
||||
' Edit
|
||||
|
||||
a href="#" data-click-tab="preview"
|
||||
i.fa.fa-eye>
|
||||
' Preview
|
||||
|
||||
.block__tab.communication-edit__tab.selected data-tab="write"
|
||||
.field
|
||||
= textarea f, :body, class: "input input--wide input--text js-preview-input js-toolbar-input", placeholder: "Please read the site rules before posting and use [spoiler][/spoiler] for NSFW stuff in SFW forums.", required: true
|
||||
= error_tag f, :body
|
||||
|
||||
.block__tab.communication-edit__tab.hidden data-tab="preview"
|
||||
' [Loading preview...]
|
||||
|
||||
.block__content.communication-edit__actions
|
||||
= submit "Post", class: "button"
|
|
@ -47,7 +47,7 @@ h1 = @topic.title
|
|||
|
||||
/ Post form
|
||||
= if @topic.post_count < 200_000 do
|
||||
/= render partial: 'topics/posts/form'
|
||||
= render PhilomenaWeb.Topic.PostView, "_form.html", conn: @conn, forum: @forum, topic: @topic, changeset: @changeset
|
||||
- else
|
||||
/h3 Okay, we're impressed
|
||||
/p You're looking at a thread with over 200,000 posts in it!
|
||||
|
|
3
lib/philomena_web/views/topic/post_view.ex
Normal file
3
lib/philomena_web/views/topic/post_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule PhilomenaWeb.Topic.PostView do
|
||||
use PhilomenaWeb, :view
|
||||
end
|
Loading…
Reference in a new issue