sticky threads
This commit is contained in:
parent
21f499426b
commit
d456e7b161
|
@ -1641,11 +1641,6 @@
|
|||
"resolve-dir": "0.1.1"
|
||||
}
|
||||
},
|
||||
"font-awesome": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
||||
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^3.3.7",
|
||||
"font-awesome": "^4.7.0",
|
||||
"jquery": "^3.3.1",
|
||||
"phoenix": "file:../deps/phoenix",
|
||||
"phoenix_html": "file:../deps/phoenix_html"
|
||||
|
|
|
@ -15,12 +15,15 @@ use Mix.Config
|
|||
# which you typically run after static files are built.
|
||||
config :forum, ForumWeb.Endpoint,
|
||||
load_from_system_env: true,
|
||||
url: [host: "forum.tilde.team", port: 4002],
|
||||
# url: [host: "forum.tilde.team", port: 4002],
|
||||
http: [port: {:system, "PORT"}],
|
||||
cache_static_manifest: "priv/static/cache_manifest.json"
|
||||
|
||||
# Do not print debug messages in production
|
||||
config :logger, level: :info
|
||||
|
||||
config :phoenix, :serve_endpoints, true
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
# To get SSL working, you will need to add the `https` key
|
||||
|
|
|
@ -39,6 +39,15 @@ defmodule Forum do
|
|||
Repo.get_by!(User, name: name)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets user id by name
|
||||
"""
|
||||
def get_user_id_by_name(name) do
|
||||
from(u in User, where: u.name == ^name, select: [u.id])
|
||||
|> Repo.one()
|
||||
|> hd
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a user.
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ defmodule Forum.Thread do
|
|||
|
||||
schema "threads" do
|
||||
field(:name, :string)
|
||||
field(:sticky, :boolean)
|
||||
|
||||
has_many(:posts, Forum.Post)
|
||||
belongs_to(:user, Forum.User)
|
||||
|
@ -15,7 +16,7 @@ defmodule Forum.Thread do
|
|||
@doc false
|
||||
def changeset(%Thread{} = thread, attrs) do
|
||||
thread
|
||||
|> cast(attrs, [:name, :user_id])
|
||||
|> cast(attrs, [:name, :user_id, :sticky])
|
||||
|> validate_required([:name, :user_id])
|
||||
|> foreign_key_constraint(:user_id)
|
||||
end
|
||||
|
|
|
@ -22,6 +22,9 @@ defmodule Forum.User do
|
|||
|> unique_constraint(:name)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets contents of tagline.txt for user
|
||||
"""
|
||||
def tagline(user) do
|
||||
resp = get("~#{user.name}/tagline.txt")
|
||||
|
||||
|
@ -36,4 +39,18 @@ defmodule Forum.User do
|
|||
"something went wrong..."
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets tilde url for user
|
||||
"""
|
||||
def profile_url(user) do
|
||||
"https://tilde.team/~#{user.name}/"
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets path to avatar for user
|
||||
"""
|
||||
def pfp(user) do
|
||||
profile_url(user) <> "avatar.png"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,31 +1,43 @@
|
|||
defmodule Forum.Util do
|
||||
@moduledoc """
|
||||
General utilities for the Forum
|
||||
"""
|
||||
|
||||
import Plug.Conn
|
||||
|
||||
alias Forum.Repo
|
||||
alias Forum.User
|
||||
|
||||
@doc """
|
||||
Converts Markdown to HTML
|
||||
"""
|
||||
def md(text) do
|
||||
text
|
||||
|> Earmark.as_html!()
|
||||
|> Phoenix.HTML.raw()
|
||||
end
|
||||
|
||||
def get_session_user(conn) do
|
||||
Repo.get_by(User, name: current_username(conn))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the logged-in username for a conn
|
||||
"""
|
||||
def current_username(conn) do
|
||||
get_session(conn, :current_user)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Can current user sudo?
|
||||
"""
|
||||
def is_sudoer?(conn) do
|
||||
get_session(conn, :sudoer)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
"""
|
||||
def can_edit?(conn, user) do
|
||||
user.name == current_username(conn) or is_sudoer?(conn)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
"""
|
||||
def pp_time(time) do
|
||||
time
|
||||
|> Timex.local()
|
||||
|
@ -33,17 +45,19 @@ defmodule Forum.Util do
|
|||
|> String.downcase()
|
||||
end
|
||||
|
||||
def pfp(user) do
|
||||
profile_url(user) <> "avatar.png"
|
||||
end
|
||||
|
||||
def profile_url(user) do
|
||||
"https://tilde.team/~#{user.name}/"
|
||||
end
|
||||
@doc """
|
||||
|
||||
"""
|
||||
def paginate(conn, kerosene) do
|
||||
if kerosene.total_pages > 1 do
|
||||
Kerosene.HTML.paginate(conn, kerosene, next_label: ">>", previous_label: "<<")
|
||||
Kerosene.HTML.paginate(
|
||||
conn,
|
||||
kerosene,
|
||||
next_label: ">>",
|
||||
previous_label: "<<",
|
||||
first_label: "first",
|
||||
last_label: "last"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,6 +43,7 @@ defmodule ForumWeb do
|
|||
import ForumWeb.ErrorHelpers
|
||||
import ForumWeb.Gettext
|
||||
|
||||
alias Forum.User
|
||||
alias Forum.Util
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,20 +4,16 @@ defmodule ForumWeb.PostController do
|
|||
def create(conn, %{"post" => post_params}) do
|
||||
case Forum.create_post(post_params) do
|
||||
{:ok, post} ->
|
||||
post = Forum.Repo.preload(post, :thread)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "reply created successfully.")
|
||||
|> redirect(to: thread_path(conn, :show, post.thread.id))
|
||||
|> redirect(to: thread_path(conn, :show, post.thread_id))
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
user = Util.get_session_user(conn)
|
||||
|
||||
render(
|
||||
conn,
|
||||
"new.html",
|
||||
changeset: changeset,
|
||||
user_id: user.id,
|
||||
user_id: Forum.get_user_id_by_name(Util.current_username(conn)),
|
||||
thread_id: post_params["thread_id"]
|
||||
)
|
||||
end
|
||||
|
|
|
@ -9,20 +9,39 @@ defmodule ForumWeb.ThreadController do
|
|||
alias Forum.User
|
||||
|
||||
def index(conn, params) do
|
||||
stickies =
|
||||
Thread
|
||||
|> order_by(desc: :updated_at)
|
||||
|> where(sticky: true)
|
||||
|> preload(:user)
|
||||
|> Repo.all()
|
||||
|
||||
{threads, kerosene} =
|
||||
Thread
|
||||
|> order_by(desc: :id)
|
||||
|> where(sticky: false)
|
||||
|> preload(:user)
|
||||
|> Repo.paginate(params)
|
||||
|
||||
tagline = Tesla.get("https://tilde.team/~ben/api/").body
|
||||
render(conn, "index.html", threads: threads, kerosene: kerosene, tagline: tagline)
|
||||
|
||||
render(
|
||||
conn,
|
||||
"index.html",
|
||||
stickies: stickies,
|
||||
threads: threads,
|
||||
kerosene: kerosene,
|
||||
tagline: tagline
|
||||
)
|
||||
end
|
||||
|
||||
def new(conn, _params) do
|
||||
changeset = Forum.change_thread(%Thread{})
|
||||
user = Util.get_session_user(conn)
|
||||
render(conn, "new.html", changeset: changeset, user_id: user.id)
|
||||
changeset =
|
||||
Forum.change_thread(%Thread{
|
||||
user_id: conn |> Util.current_username() |> Forum.get_user_id_by_name()
|
||||
})
|
||||
|
||||
render(conn, "new.html", changeset: changeset)
|
||||
end
|
||||
|
||||
def create(conn, %{"thread" => thread_params}) do
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="panel-body">
|
||||
<div class="pull-right">
|
||||
<a href="<%= user_path(@conn, :show, @post.user.name) %>">
|
||||
<img src="<%= Util.pfp(@post.user) %>" alt="<%= @post.user.name %> avatar" width="96px" height="96px">
|
||||
<img src="<%= User.pfp(@post.user) %>" alt="<%= @post.user.name %> avatar" width="96px" height="96px">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -6,16 +6,22 @@
|
|||
<% IO.inspect @changeset %>
|
||||
<% end %>
|
||||
|
||||
<%= if assigns[:user_id] do
|
||||
hidden_input f, :user_id, value: assigns[:user_id]
|
||||
end %>
|
||||
<%= hidden_input f, :user_id %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= text_input f, :name, placeholder: "my cool new thread", class: "form-control" %>
|
||||
<%= label f, :name, "thread title" %>
|
||||
<%= text_input f, :name, placeholder: "my cool new thread", class: "form-control", autofocus: true %>
|
||||
<%= error_tag f, :name %>
|
||||
</div>
|
||||
|
||||
<%= if Util.is_sudoer?(@conn) do %>
|
||||
<div class="form-group">
|
||||
<%= label f, :sticky, "sticky thread?" %>
|
||||
<%= checkbox f, :sticky %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= submit "create", class: "btn btn-primary" %>
|
||||
<%= submit "save", class: "btn btn-primary" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
<h1>tilde~forum</h1>
|
||||
<small><em><%= @tagline %></em></small>
|
||||
|
||||
<%= if Enum.any?(@stickies) do %>
|
||||
<hr>
|
||||
<h4>sticky threads</h4>
|
||||
<% end %>
|
||||
|
||||
<%= for sticky <- @stickies do %>
|
||||
<%= render "thread.html", conn: @conn, thread: sticky %>
|
||||
<% end %>
|
||||
|
||||
<hr>
|
||||
|
||||
<%= for thread <- @threads do %>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<h4><a href="<%= thread_path(@conn, :show, thread) %>"><%= thread.name %></a></h4>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<%= if Util.can_edit?(@conn, thread.user) do %>
|
||||
<div class="pull-right">
|
||||
<%= link "edit", to: thread_path(@conn, :edit, thread), class: "btn btn-info btn-xs" %>
|
||||
<%= link "delete", to: thread_path(@conn, :delete, thread), method: :delete, data: [confirm: "are you sure?"], class: "btn btn-danger btn-xs" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
by: <a href="<%= user_path(@conn, :show, thread.user.name) %>"><em>~<%= thread.user.name %></em></a> // <small>created <%= thread.inserted_at |> Util.pp_time %></small>
|
||||
</div>
|
||||
</div>
|
||||
<%= render "thread.html", conn: @conn, thread: thread %>
|
||||
<% end %>
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<h2>start new thread</h2>
|
||||
<div class="page-header">
|
||||
<h1>start new thread</h1>
|
||||
</div>
|
||||
|
||||
<%= render "form.html", Map.put(assigns, :action, thread_path(@conn, :create)) %>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<%= if Util.can_edit?(@conn, @thread.user) do %>
|
||||
<div class="pull-right">
|
||||
<span><%= link "edit thread title", to: thread_path(@conn, :edit, @thread), class: "btn btn-info btn-xs" %></span>
|
||||
<span><%= link "edit thread", to: thread_path(@conn, :edit, @thread), class: "btn btn-info btn-xs" %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<h4><a href="<%= thread_path(@conn, :show, @thread) %>"><%= @thread.name %></a></h4>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<%= if Util.can_edit?(@conn, @thread.user) do %>
|
||||
<div class="pull-right">
|
||||
<%= link "edit", to: thread_path(@conn, :edit, @thread), class: "btn btn-info btn-xs" %>
|
||||
<%= link "delete", to: thread_path(@conn, :delete, @thread), method: :delete, data: [confirm: "are you sure?"], class: "btn btn-danger btn-xs" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
by: <a href="<%= user_path(@conn, :show, @thread.user.name) %>"><em>~<%= @thread.user.name %></em></a> // <small>created <%= @thread.inserted_at |> Util.pp_time %></small>
|
||||
</div>
|
||||
</div>
|
|
@ -1,9 +1,9 @@
|
|||
<div class="page-header clearfix">
|
||||
<div class="pull-right">
|
||||
<img src="<%= Util.pfp(@user) %>" alt="<%= @user.name %> avatar" width="128px" height="128px">
|
||||
<img src="<%= @user |> User.pfp %>" alt="<%= @user.name %> avatar" width="128px" height="128px">
|
||||
</div>
|
||||
<h1>
|
||||
<a href="<%= Util.profile_url(@user) %>">~<%= @user.name %></a>
|
||||
<a href="<%= @user |> User.profile_url %>">~<%= @user.name %></a>
|
||||
</h1>
|
||||
<%= if @user.sudoer do %>
|
||||
<span class="label label-info">sudoer</span>
|
||||
|
|
8
mix.exs
8
mix.exs
|
@ -4,7 +4,7 @@ defmodule Forum.Mixfile do
|
|||
def project do
|
||||
[
|
||||
app: :forum,
|
||||
version: "0.0.1",
|
||||
version: "0.0.#{committed_at()}",
|
||||
elixir: "~> 1.4",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||
|
@ -14,6 +14,11 @@ defmodule Forum.Mixfile do
|
|||
]
|
||||
end
|
||||
|
||||
@doc "unix timestamp of last commit"
|
||||
def committed_at() do
|
||||
System.cmd("git", ~w[log -1 --date=short --pretty=format:%ct]) |> elem(0)
|
||||
end
|
||||
|
||||
# Configuration for the OTP application.
|
||||
#
|
||||
# Type `mix help compile.app` for more information.
|
||||
|
@ -34,6 +39,7 @@ defmodule Forum.Mixfile do
|
|||
defp deps do
|
||||
[
|
||||
{:cowboy, "~> 1.0"},
|
||||
{:distillery, "~> 1.5", runtime: false},
|
||||
{:earmark, ">= 1.2.4"},
|
||||
{:gettext, "~> 0.11"},
|
||||
{:kerosene, "~> 0.7.0"},
|
||||
|
|
1
mix.lock
1
mix.lock
|
@ -6,6 +6,7 @@
|
|||
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"},
|
||||
"db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"decimal": {:hex, :decimal, "1.4.1", "ad9e501edf7322f122f7fc151cce7c2a0c9ada96f2b0155b8a09a795c2029770", [:mix], [], "hexpm"},
|
||||
"distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "2.2.8", "a4463c0928b970f2cee722cd29aaac154e866a15882c5737e0038bbfcf03ec2c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"file_system": {:hex, :file_system, "0.2.4", "f0bdda195c0e46e987333e986452ec523aed21d784189144f647c43eaf307064", [:mix], [], "hexpm"},
|
||||
|
|
|
@ -3,10 +3,10 @@ defmodule Forum.Repo.Migrations.CreateThreads do
|
|||
|
||||
def change do
|
||||
create table(:threads) do
|
||||
add :name, :string
|
||||
add(:name, :string)
|
||||
add(:sticky, :boolean, default: false)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,3 +9,24 @@
|
|||
#
|
||||
# We recommend using the bang functions (`insert!`, `update!`
|
||||
# and so on) as they will fail if something goes wrong.
|
||||
|
||||
Forum.Repo.insert!(%Forum.User{name: "ben", sudoer: true, bio: "dev/sysadmin"})
|
||||
|
||||
Forum.Repo.insert!(%Forum.Thread{name: "forum faq", sticky: true, user_id: 1})
|
||||
|
||||
for i <- 1..100 do
|
||||
Forum.Repo.insert!(%Forum.Post{
|
||||
content: "welcome! lots of posts #{i}",
|
||||
thread_id: 1,
|
||||
user_id: 1
|
||||
})
|
||||
end
|
||||
|
||||
Forum.Repo.insert!(%Forum.Thread{name: "what are you working on?", user_id: 1})
|
||||
|
||||
Forum.Repo.insert!(%Forum.Post{
|
||||
content:
|
||||
"learning elixir by making this site! check it out on [github](https://github.com/tilde-team/forum/tree/elixir)",
|
||||
thread_id: 2,
|
||||
user_id: 1
|
||||
})
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
# Import all plugins from `rel/plugins`
|
||||
# They can then be used by adding `plugin MyPlugin` to
|
||||
# either an environment, or release definition, where
|
||||
# `MyPlugin` is the name of the plugin module.
|
||||
Path.join(["rel", "plugins", "*.exs"])
|
||||
|> Path.wildcard()
|
||||
|> Enum.map(&Code.eval_file(&1))
|
||||
|
||||
use Mix.Releases.Config,
|
||||
# This sets the default release built by `mix release`
|
||||
default_release: :default,
|
||||
# This sets the default environment used by `mix release`
|
||||
default_environment: Mix.env()
|
||||
|
||||
# For a full list of config options for both releases
|
||||
# and environments, visit https://hexdocs.pm/distillery/configuration.html
|
||||
|
||||
|
||||
# You may define one or more environments in this file,
|
||||
# an environment's settings will override those of a release
|
||||
# when building in that environment, this combination of release
|
||||
# and environment configuration is called a profile
|
||||
|
||||
environment :dev do
|
||||
# If you are running Phoenix, you should make sure that
|
||||
# server: true is set and the code reloader is disabled,
|
||||
# even in dev mode.
|
||||
# It is recommended that you build with MIX_ENV=prod and pass
|
||||
# the --env flag to Distillery explicitly if you want to use
|
||||
# dev mode.
|
||||
set dev_mode: true
|
||||
set include_erts: false
|
||||
set cookie: :"syId2a?^I4@,}pMpVTb&YJvp~mfgl3oW3MN(3qVVz[m>u3tz^@$4kv*41XmY.6,0"
|
||||
end
|
||||
|
||||
environment :prod do
|
||||
set include_erts: true
|
||||
set include_src: false
|
||||
set cookie: :"`SKJ$KS/7GL&d.O||qanMD=R&:MZx:Fwd8Fq]qB0pT3Nqy!nG]</(2Xoz!z]sL>7"
|
||||
end
|
||||
|
||||
# You may define one or more releases in this file.
|
||||
# If you have not set a default release, or selected one
|
||||
# when running `mix release`, the first release in the file
|
||||
# will be used by default
|
||||
|
||||
release :forum do
|
||||
set version: current_version(:forum)
|
||||
set applications: [
|
||||
:runtime_tools
|
||||
]
|
||||
end
|
||||
|
Reference in New Issue