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
|
import Ecto.Query, warn: false
|
||||||
|
alias Ecto.Multi
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
|
|
||||||
|
alias Philomena.Topics.Topic
|
||||||
alias Philomena.Posts.Post
|
alias Philomena.Posts.Post
|
||||||
|
alias Philomena.Notifications
|
||||||
@doc """
|
|
||||||
Returns the list of posts.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> list_posts()
|
|
||||||
[%Post{}, ...]
|
|
||||||
|
|
||||||
"""
|
|
||||||
def list_posts do
|
|
||||||
Repo.all(Post)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets a single post.
|
Gets a single post.
|
||||||
|
@ -49,10 +39,60 @@ defmodule Philomena.Posts do
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_post(attrs \\ %{}) do
|
def create_post(topic, user, attributes, params \\ %{}) do
|
||||||
%Post{}
|
topic_query =
|
||||||
|> Post.changeset(attrs)
|
Topic
|
||||||
|> Repo.insert()
|
|> 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
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -101,4 +141,20 @@ defmodule Philomena.Posts do
|
||||||
def change_post(%Post{} = post) do
|
def change_post(%Post{} = post) do
|
||||||
Post.changeset(post, %{})
|
Post.changeset(post, %{})
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -38,4 +38,18 @@ defmodule Philomena.Posts.Post do
|
||||||
|> cast(attrs, [])
|
|> cast(attrs, [])
|
||||||
|> validate_required([])
|
|> validate_required([])
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -4,4 +4,24 @@ defmodule Philomena.Repo do
|
||||||
adapter: Ecto.Adapters.Postgres
|
adapter: Ecto.Adapters.Postgres
|
||||||
|
|
||||||
use Scrivener, page_size: 250
|
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
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Philomena.Topics.Topic do
|
||||||
alias Philomena.Users.User
|
alias Philomena.Users.User
|
||||||
alias Philomena.Polls.Poll
|
alias Philomena.Polls.Poll
|
||||||
alias Philomena.Posts.Post
|
alias Philomena.Posts.Post
|
||||||
|
alias Philomena.Topics.Subscription
|
||||||
|
|
||||||
@derive {Phoenix.Param, key: :slug}
|
@derive {Phoenix.Param, key: :slug}
|
||||||
schema "topics" do
|
schema "topics" do
|
||||||
|
@ -15,6 +16,8 @@ defmodule Philomena.Topics.Topic do
|
||||||
belongs_to :last_post, Post
|
belongs_to :last_post, Post
|
||||||
belongs_to :forum, Forum
|
belongs_to :forum, Forum
|
||||||
has_one :poll, Poll
|
has_one :poll, Poll
|
||||||
|
has_many :posts, Post
|
||||||
|
has_many :subscriptions, Subscription
|
||||||
|
|
||||||
field :title, :string
|
field :title, :string
|
||||||
field :post_count, :integer, default: 0
|
field :post_count, :integer, default: 0
|
||||||
|
|
|
@ -1,10 +1,51 @@
|
||||||
defmodule PhilomenaWeb.Topic.PostController do
|
defmodule PhilomenaWeb.Topic.PostController do
|
||||||
use PhilomenaWeb, :controller
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
#alias Philomena.{Forums.Forum}
|
alias Philomena.{Forums.Forum, Topics.Topic, Posts}
|
||||||
#alias Philomena.Posts
|
alias Philomena.Repo
|
||||||
|
|
||||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||||
|
plug PhilomenaWeb.UserAttributionPlug
|
||||||
plug PhilomenaWeb.CanaryMapPlug, create: :show, edit: :show, update: :show
|
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_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
|
end
|
|
@ -1,7 +1,7 @@
|
||||||
defmodule PhilomenaWeb.TopicController do
|
defmodule PhilomenaWeb.TopicController do
|
||||||
use PhilomenaWeb, :controller
|
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
|
alias Philomena.Repo
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
@ -54,6 +54,10 @@ defmodule PhilomenaWeb.TopicController do
|
||||||
watching =
|
watching =
|
||||||
Topics.subscribed?(topic, conn.assigns.current_user)
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -86,7 +86,9 @@ defmodule PhilomenaWeb.Router do
|
||||||
resources "/tags", TagController, only: [:index, :show]
|
resources "/tags", TagController, only: [:index, :show]
|
||||||
resources "/search", SearchController, only: [:index]
|
resources "/search", SearchController, only: [:index]
|
||||||
resources "/forums", ForumController, only: [:index, :show] do
|
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
|
end
|
||||||
resources "/comments", CommentController, only: [:index]
|
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
|
/ Post form
|
||||||
= if @topic.post_count < 200_000 do
|
= 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
|
- else
|
||||||
/h3 Okay, we're impressed
|
/h3 Okay, we're impressed
|
||||||
/p You're looking at a thread with over 200,000 posts in it!
|
/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