mirror of
https://github.com/philomena-dev/philomena.git
synced 2025-01-19 22:27:59 +01:00
user links
This commit is contained in:
parent
50759774f6
commit
846c99e563
11 changed files with 318 additions and 5 deletions
|
@ -7,6 +7,7 @@ defmodule Philomena.UserLinks do
|
|||
alias Philomena.Repo
|
||||
|
||||
alias Philomena.UserLinks.UserLink
|
||||
alias Philomena.Tags.Tag
|
||||
|
||||
@doc """
|
||||
Returns the list of user_links.
|
||||
|
@ -49,9 +50,11 @@ defmodule Philomena.UserLinks do
|
|||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_user_link(attrs \\ %{}) do
|
||||
def create_user_link(user, attrs \\ %{}) do
|
||||
tag = Repo.get_by(Tag, name: attrs["tag_name"])
|
||||
|
||||
%UserLink{}
|
||||
|> UserLink.changeset(attrs)
|
||||
|> UserLink.creation_changeset(attrs, user, tag)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@ defmodule Philomena.UserLinks.UserLink do
|
|||
belongs_to :contacted_by_user, User
|
||||
belongs_to :tag, Tag
|
||||
|
||||
field :aasm_state, :string
|
||||
field :aasm_state, :string, default: "unverified"
|
||||
field :uri, :string
|
||||
field :hostname, :string
|
||||
field :path, :string
|
||||
field :verification_code, :string
|
||||
field :public, :boolean, default: true
|
||||
field :next_check_at, :naive_datetime
|
||||
field :contacted_at, :naive_datetime
|
||||
field :next_check_at, :utc_datetime
|
||||
field :contacted_at, :utc_datetime
|
||||
|
||||
timestamps(inserted_at: :created_at)
|
||||
end
|
||||
|
@ -29,4 +29,37 @@ defmodule Philomena.UserLinks.UserLink do
|
|||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
|
||||
def creation_changeset(user_link, attrs, user, tag) do
|
||||
user_link
|
||||
|> cast(attrs, [:uri])
|
||||
|> put_assoc(:tag, tag)
|
||||
|> put_assoc(:user, user)
|
||||
|> validate_required([:user, :uri])
|
||||
|> parse_uri()
|
||||
|> put_verification_code()
|
||||
|> put_next_check_at()
|
||||
end
|
||||
|
||||
defp parse_uri(changeset) do
|
||||
string_uri = get_field(changeset, :uri) |> to_string()
|
||||
uri = URI.parse(string_uri)
|
||||
|
||||
changeset
|
||||
|> change(hostname: uri.host, path: uri.path)
|
||||
end
|
||||
|
||||
defp put_verification_code(changeset) do
|
||||
code = :crypto.strong_rand_bytes(5) |> Base.encode16()
|
||||
change(changeset, verification_code: "DERPI-LINKVALIDATION-#{code}")
|
||||
end
|
||||
|
||||
defp put_next_check_at(changeset) do
|
||||
time =
|
||||
DateTime.utc_now()
|
||||
|> DateTime.add(60 * 2, :second)
|
||||
|> DateTime.truncate(:second)
|
||||
|
||||
change(changeset, next_check_at: time)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
|||
alias Philomena.Posts.Post
|
||||
alias Philomena.Filters.Filter
|
||||
alias Philomena.DnpEntries.DnpEntry
|
||||
alias Philomena.UserLinks.UserLink
|
||||
|
||||
# Admins can do anything
|
||||
def can?(%User{role: "admin"}, _action, _model), do: true
|
||||
|
@ -60,6 +61,9 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do
|
|||
# Edit filters they own
|
||||
def can?(%User{id: id}, action, %Filter{user_id: id}) when action in [:edit, :update], do: true
|
||||
|
||||
# View user links they've created
|
||||
def can?(%User{id: id}, :show, %UserLink{user_id: id}), do: true
|
||||
|
||||
# View non-deleted images
|
||||
def can?(_user, action, Image)
|
||||
when action in [:new, :create, :index],
|
||||
|
|
45
lib/philomena_web/controllers/user_link_controller.ex
Normal file
45
lib/philomena_web/controllers/user_link_controller.ex
Normal file
|
@ -0,0 +1,45 @@
|
|||
defmodule PhilomenaWeb.UserLinkController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.UserLinks
|
||||
alias Philomena.UserLinks.UserLink
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query
|
||||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create]
|
||||
plug :load_and_authorize_resource, model: UserLink, only: [:show], preload: [:user, :tag, :contacted_by_user]
|
||||
|
||||
def index(conn, _params) do
|
||||
user = conn.assigns.current_user
|
||||
user_links =
|
||||
UserLink
|
||||
|> where(user_id: ^user.id)
|
||||
|> Repo.all()
|
||||
|
||||
render(conn, "index.html", user_links: user_links)
|
||||
end
|
||||
|
||||
def new(conn, _params) do
|
||||
changeset = UserLinks.change_user_link(%UserLink{})
|
||||
render(conn, "new.html", changeset: changeset)
|
||||
end
|
||||
|
||||
def create(conn, %{"user_link" => user_link_params}) do
|
||||
user = conn.assigns.current_user
|
||||
|
||||
case UserLinks.create_user_link(user, user_link_params) do
|
||||
{:ok, user_link} ->
|
||||
conn
|
||||
|> put_flash(:info, "Link submitted! Please put '#{user_link.verification_code}' on your linked webpage now.")
|
||||
|> redirect(to: Routes.user_link_path(conn, :show, user_link))
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, "new.html", changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
def show(conn, _params) do
|
||||
user_link = conn.assigns.user_link
|
||||
render(conn, "show.html", user_link: user_link)
|
||||
end
|
||||
end
|
|
@ -117,6 +117,7 @@ defmodule PhilomenaWeb.Router do
|
|||
end
|
||||
|
||||
resources "/reports", ReportController, only: [:index]
|
||||
resources "/user_links", UserLinkController, only: [:index, :new, :create, :show]
|
||||
end
|
||||
|
||||
scope "/", PhilomenaWeb do
|
||||
|
|
38
lib/philomena_web/templates/user_link/_form.html.slime
Normal file
38
lib/philomena_web/templates/user_link/_form.html.slime
Normal file
|
@ -0,0 +1,38 @@
|
|||
= form_for @changeset, @action, fn f ->
|
||||
= if @changeset.action do
|
||||
.alert.alert-danger
|
||||
p Oops, something went wrong! Please check the errors below.
|
||||
|
||||
.field
|
||||
.field
|
||||
p
|
||||
label for="tag_name"
|
||||
' The tag,
|
||||
em> specific
|
||||
' to you, usually
|
||||
code>
|
||||
| artist:
|
||||
em artist name here
|
||||
' or a series name
|
||||
p Should be blank only if your content isn't on the site, generally
|
||||
= text_input f, :tag_name, class: "input", autocomplete: "off", placeholder: "artist:name", data: [ac: "true", ac_min_length: "3", ac_source: "/tags/autocomplete?term="]
|
||||
.field
|
||||
label for="uri"
|
||||
' URL of your art webpage
|
||||
= url_input f, :uri, class: "input input--wide", placeholder: "https://www.deviantart.com/your-name"#, required: true
|
||||
= error_tag f, :uri
|
||||
.field
|
||||
=> radio_button f, :public, "true"
|
||||
=> label f, :public, "Visible to everyone"
|
||||
.field
|
||||
=> radio_button f, :public, "false"
|
||||
=> label f, :public, "Visible only to site staff"
|
||||
|
||||
h4 Instructions
|
||||
p
|
||||
strong Review details carefully as only site staff can edit later.
|
||||
p
|
||||
strong> For quick results, put the LINKVALIDATION code on your linked webpage after submission.
|
||||
| We'll message you there otherwise.
|
||||
.actions
|
||||
= submit "Submit", class: "button"
|
23
lib/philomena_web/templates/user_link/index.html.slime
Normal file
23
lib/philomena_web/templates/user_link/index.html.slime
Normal file
|
@ -0,0 +1,23 @@
|
|||
h1 Your Links
|
||||
p
|
||||
a.button href=Routes.user_link_path(@conn, :new)
|
||||
' Create a link
|
||||
p
|
||||
' User links associate your account on Derpibooru with tags about content you create and with accounts on sites elsewhere. This allows users to easily identify artists and admins to act more rapidly on takedown requests.
|
||||
|
||||
table.table
|
||||
thead
|
||||
tr
|
||||
th URI
|
||||
th Options
|
||||
th Verification Code
|
||||
th Verified?
|
||||
th Public
|
||||
tbody
|
||||
= for link <- @user_links do
|
||||
tr
|
||||
td = link link.uri, to: link.uri
|
||||
td = link "View Details", to: Routes.user_link_path(@conn, :show, link)
|
||||
td = link.verification_code
|
||||
th = verified_as_string(link)
|
||||
th = public_as_string(link)
|
2
lib/philomena_web/templates/user_link/new.html.slime
Normal file
2
lib/philomena_web/templates/user_link/new.html.slime
Normal file
|
@ -0,0 +1,2 @@
|
|||
h1 Create Link
|
||||
= render PhilomenaWeb.UserLinkView, "_form.html", changeset: @changeset, action: Routes.user_link_path(@conn, :create)
|
59
lib/philomena_web/templates/user_link/show.html.slime
Normal file
59
lib/philomena_web/templates/user_link/show.html.slime
Normal file
|
@ -0,0 +1,59 @@
|
|||
h1
|
||||
' Link to
|
||||
= link @user_link.uri, to: @user_link.uri
|
||||
|
||||
h3 Status
|
||||
= cond do
|
||||
- verified?(@user_link) ->
|
||||
p This link has been verified by a member of the administration team.
|
||||
p You can now remove the verification text from your website if you have not done so already.
|
||||
|
||||
- contacted?(@user_link) ->
|
||||
p
|
||||
strong This link is awaiting your reply on the linked website in order to be verified.
|
||||
p
|
||||
' An administrator
|
||||
=> "(#{@user_link.contacted_by_user.name})"
|
||||
' has manually contacted you at the address above, as your verification code was not found on the website. Please respond to the message from the administrator to confirm your link.
|
||||
p The verification code is:
|
||||
p
|
||||
code
|
||||
h1 = @user_link.verification_code
|
||||
|
||||
- link_verified?(@user_link) ->
|
||||
p
|
||||
strong This link is pending verification by a member of the administration team.
|
||||
p We've now found the verification code on your website. An administrator still needs to check the tag list before verifying the link. Please leave the code on your website until verification is complete.
|
||||
p If you need it again, your verification code is:
|
||||
p
|
||||
code
|
||||
h1 = @user_link.verification_code
|
||||
|
||||
- unverified?(@user_link) ->
|
||||
p
|
||||
strong This link is pending verification by a member of the administration team.
|
||||
p
|
||||
h3 To have your link verified as fast as possible, please place this text somewhere on the page you are linking.
|
||||
p
|
||||
code
|
||||
h1 = @user_link.verification_code
|
||||
p Otherwise, an administrator will have to contact you to verify your identity.
|
||||
p Once the link has been verified you can remove the text; the text simply allows the team to directly check with your website rather than messaging you and waiting for a reply.
|
||||
|
||||
- rejected?(@user_link) ->
|
||||
p This link has been rejected by a member of the administration team; this is probably because you were not reachable in a timely manner (~1 week) to verify the link.
|
||||
|
||||
h3 Visibility
|
||||
= if public?(@user_link) do
|
||||
p This link is public, and will be shown around the site.
|
||||
- else
|
||||
p This link is not public, and will only be shown to administrators.
|
||||
|
||||
h3 Associated tag
|
||||
= if @user_link.tag do
|
||||
p
|
||||
= render PhilomenaWeb.TagView, "_tag.html", tag: @user_link.tag
|
||||
- else
|
||||
p There is no tag associated with this link.
|
||||
|
||||
= link "Back", to: Routes.user_link_path(@conn, :index)
|
17
lib/philomena_web/views/user_link_view.ex
Normal file
17
lib/philomena_web/views/user_link_view.ex
Normal file
|
@ -0,0 +1,17 @@
|
|||
defmodule PhilomenaWeb.UserLinkView do
|
||||
use PhilomenaWeb, :view
|
||||
|
||||
def verified?(%{aasm_state: state}), do: state == "verified"
|
||||
def contacted?(%{aasm_state: state}), do: state == "contacted"
|
||||
def link_verified?(%{aasm_state: state}), do: state == "link_verified"
|
||||
def unverified?(%{aasm_state: state}), do: state == "unverified"
|
||||
def rejected?(%{aasm_state: state}), do: state == "rejected"
|
||||
|
||||
def public?(%{public: public}), do: !!public
|
||||
|
||||
def verified_as_string(%{aasm_state: "verified"}), do: "Yes"
|
||||
def verified_as_string(_user_link), do: "No"
|
||||
|
||||
def public_as_string(%{public: true}), do: "Yes"
|
||||
def public_as_string(_user_link), do: "No"
|
||||
end
|
88
test/philomena_web/controllers/user_link_controller_test.exs
Normal file
88
test/philomena_web/controllers/user_link_controller_test.exs
Normal file
|
@ -0,0 +1,88 @@
|
|||
defmodule PhilomenaWeb.UserLinkControllerTest do
|
||||
use PhilomenaWeb.ConnCase
|
||||
|
||||
alias Philomena.UserLinks
|
||||
|
||||
@create_attrs %{}
|
||||
@update_attrs %{}
|
||||
@invalid_attrs %{}
|
||||
|
||||
def fixture(:user_link) do
|
||||
{:ok, user_link} = UserLinks.create_user_link(@create_attrs)
|
||||
user_link
|
||||
end
|
||||
|
||||
describe "index" do
|
||||
test "lists all user_links", %{conn: conn} do
|
||||
conn = get(conn, Routes.user_link_path(conn, :index))
|
||||
assert html_response(conn, 200) =~ "Listing User links"
|
||||
end
|
||||
end
|
||||
|
||||
describe "new user_link" do
|
||||
test "renders form", %{conn: conn} do
|
||||
conn = get(conn, Routes.user_link_path(conn, :new))
|
||||
assert html_response(conn, 200) =~ "New User link"
|
||||
end
|
||||
end
|
||||
|
||||
describe "create user_link" do
|
||||
test "redirects to show when data is valid", %{conn: conn} do
|
||||
conn = post(conn, Routes.user_link_path(conn, :create), user_link: @create_attrs)
|
||||
|
||||
assert %{id: id} = redirected_params(conn)
|
||||
assert redirected_to(conn) == Routes.user_link_path(conn, :show, id)
|
||||
|
||||
conn = get(conn, Routes.user_link_path(conn, :show, id))
|
||||
assert html_response(conn, 200) =~ "Show User link"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn} do
|
||||
conn = post(conn, Routes.user_link_path(conn, :create), user_link: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "New User link"
|
||||
end
|
||||
end
|
||||
|
||||
describe "edit user_link" do
|
||||
setup [:create_user_link]
|
||||
|
||||
test "renders form for editing chosen user_link", %{conn: conn, user_link: user_link} do
|
||||
conn = get(conn, Routes.user_link_path(conn, :edit, user_link))
|
||||
assert html_response(conn, 200) =~ "Edit User link"
|
||||
end
|
||||
end
|
||||
|
||||
describe "update user_link" do
|
||||
setup [:create_user_link]
|
||||
|
||||
test "redirects when data is valid", %{conn: conn, user_link: user_link} do
|
||||
conn = put(conn, Routes.user_link_path(conn, :update, user_link), user_link: @update_attrs)
|
||||
assert redirected_to(conn) == Routes.user_link_path(conn, :show, user_link)
|
||||
|
||||
conn = get(conn, Routes.user_link_path(conn, :show, user_link))
|
||||
assert html_response(conn, 200)
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn, user_link: user_link} do
|
||||
conn = put(conn, Routes.user_link_path(conn, :update, user_link), user_link: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "Edit User link"
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete user_link" do
|
||||
setup [:create_user_link]
|
||||
|
||||
test "deletes chosen user_link", %{conn: conn, user_link: user_link} do
|
||||
conn = delete(conn, Routes.user_link_path(conn, :delete, user_link))
|
||||
assert redirected_to(conn) == Routes.user_link_path(conn, :index)
|
||||
assert_error_sent 404, fn ->
|
||||
get(conn, Routes.user_link_path(conn, :show, user_link))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp create_user_link(_) do
|
||||
user_link = fixture(:user_link)
|
||||
{:ok, user_link: user_link}
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue