Rename MobilizonWeb to Mobilizon.Web
This commit is contained in:
118
lib/web/views/activity_pub/actor_view.ex
Normal file
118
lib/web/views/activity_pub/actor_view.ex
Normal file
@@ -0,0 +1,118 @@
|
||||
defmodule Mobilizon.Web.ActivityPub.ActorView do
|
||||
use Mobilizon.Web, :view
|
||||
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.Actors.Actor
|
||||
|
||||
alias Mobilizon.Federation.ActivityPub
|
||||
alias Mobilizon.Federation.ActivityPub.{Activity, Utils}
|
||||
alias Mobilizon.Federation.ActivityStream.Convertible
|
||||
|
||||
@private_visibility_empty_collection %{elements: [], total: 0}
|
||||
|
||||
def render("actor.json", %{actor: actor}) do
|
||||
actor
|
||||
|> Convertible.model_to_as()
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("following.json", %{actor: actor, page: page}) do
|
||||
%{total: total, elements: following} =
|
||||
if Actor.is_public_visibility(actor),
|
||||
do: Actors.build_followings_for_actor(actor, page),
|
||||
else: @private_visibility_empty_collection
|
||||
|
||||
following
|
||||
|> collection(actor.preferred_username, :following, page, total)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("following.json", %{actor: actor}) do
|
||||
%{total: total, elements: following} =
|
||||
if Actor.is_public_visibility(actor),
|
||||
do: Actors.build_followings_for_actor(actor),
|
||||
else: @private_visibility_empty_collection
|
||||
|
||||
%{
|
||||
"id" => Actor.build_url(actor.preferred_username, :following),
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => total,
|
||||
"first" => collection(following, actor.preferred_username, :following, 1, total)
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("followers.json", %{actor: actor, page: page}) do
|
||||
%{total: total, elements: followers} =
|
||||
if Actor.is_public_visibility(actor),
|
||||
do: Actors.build_followers_for_actor(actor, page),
|
||||
else: @private_visibility_empty_collection
|
||||
|
||||
followers
|
||||
|> collection(actor.preferred_username, :followers, page, total)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("followers.json", %{actor: actor}) do
|
||||
%{total: total, elements: followers} =
|
||||
if Actor.is_public_visibility(actor),
|
||||
do: Actors.build_followers_for_actor(actor),
|
||||
else: @private_visibility_empty_collection
|
||||
|
||||
%{
|
||||
"id" => Actor.build_url(actor.preferred_username, :followers),
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => total,
|
||||
"first" => collection(followers, actor.preferred_username, :followers, 1, total)
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("outbox.json", %{actor: actor, page: page}) do
|
||||
%{total: total, elements: followers} =
|
||||
if Actor.is_public_visibility(actor),
|
||||
do: ActivityPub.fetch_public_activities_for_actor(actor, page),
|
||||
else: @private_visibility_empty_collection
|
||||
|
||||
followers
|
||||
|> collection(actor.preferred_username, :outbox, page, total)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("outbox.json", %{actor: actor}) do
|
||||
%{total: total, elements: followers} =
|
||||
if Actor.is_public_visibility(actor),
|
||||
do: ActivityPub.fetch_public_activities_for_actor(actor),
|
||||
else: @private_visibility_empty_collection
|
||||
|
||||
%{
|
||||
"id" => Actor.build_url(actor.preferred_username, :outbox),
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => total,
|
||||
"first" => collection(followers, actor.preferred_username, :outbox, 1, total)
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
@spec collection(list(), String.t(), atom(), integer(), integer()) :: map()
|
||||
defp collection(collection, preferred_username, endpoint, page, total)
|
||||
when endpoint in [:followers, :following, :outbox] do
|
||||
offset = (page - 1) * 10
|
||||
|
||||
map = %{
|
||||
"id" => Actor.build_url(preferred_username, endpoint, page: page),
|
||||
"type" => "OrderedCollectionPage",
|
||||
"partOf" => Actor.build_url(preferred_username, endpoint),
|
||||
"orderedItems" => Enum.map(collection, &item/1)
|
||||
}
|
||||
|
||||
if offset < total do
|
||||
Map.put(map, "next", Actor.build_url(preferred_username, endpoint, page: page + 1))
|
||||
end
|
||||
|
||||
map
|
||||
end
|
||||
|
||||
def item(%Activity{data: %{"id" => id}}), do: id
|
||||
def item(%Actor{url: url}), do: url
|
||||
end
|
||||
30
lib/web/views/activity_pub/object_view.ex
Normal file
30
lib/web/views/activity_pub/object_view.ex
Normal file
@@ -0,0 +1,30 @@
|
||||
defmodule Mobilizon.Web.ActivityPub.ObjectView do
|
||||
use Mobilizon.Web, :view
|
||||
|
||||
alias Mobilizon.Federation.ActivityPub.{Activity, Utils}
|
||||
|
||||
def render("activity.json", %{activity: %Activity{local: local, data: data} = activity}) do
|
||||
%{
|
||||
"id" => data["id"],
|
||||
"type" =>
|
||||
if local do
|
||||
"Create"
|
||||
else
|
||||
"Announce"
|
||||
end,
|
||||
"actor" => activity.actor,
|
||||
# Not sure if needed since this is used into outbox
|
||||
"published" => Timex.now(),
|
||||
"to" => activity.recipients,
|
||||
"object" =>
|
||||
case data["type"] do
|
||||
"Event" ->
|
||||
render_one(data, ObjectView, "event.json", as: :event)
|
||||
|
||||
"Note" ->
|
||||
render_one(data, ObjectView, "comment.json", as: :comment)
|
||||
end
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
end
|
||||
22
lib/web/views/changeset_view.ex
Normal file
22
lib/web/views/changeset_view.ex
Normal file
@@ -0,0 +1,22 @@
|
||||
defmodule Mobilizon.Web.ChangesetView do
|
||||
@moduledoc """
|
||||
View for changesets in case of errors
|
||||
"""
|
||||
use Mobilizon.Web, :view
|
||||
|
||||
@doc """
|
||||
Traverses and translates changeset errors.
|
||||
|
||||
See `Ecto.Changeset.traverse_errors/2` and
|
||||
`Mobilizon.Web.ErrorHelpers.translate_error/1` for more details.
|
||||
"""
|
||||
def translate_errors(changeset) do
|
||||
Ecto.Changeset.traverse_errors(changeset, &translate_error/1)
|
||||
end
|
||||
|
||||
def render("error.json", %{changeset: changeset}) do
|
||||
# When encoded, the changeset returns its errors
|
||||
# as a JSON object. So we just pass it forward.
|
||||
%{errors: translate_errors(changeset)}
|
||||
end
|
||||
end
|
||||
12
lib/web/views/email_view.ex
Normal file
12
lib/web/views/email_view.ex
Normal file
@@ -0,0 +1,12 @@
|
||||
defmodule Mobilizon.Web.EmailView do
|
||||
use Mobilizon.Web, :view
|
||||
|
||||
import Mobilizon.Web.Gettext
|
||||
|
||||
def datetime_to_string(%DateTime{} = datetime, locale \\ "en") do
|
||||
with {:ok, string} <-
|
||||
Cldr.DateTime.to_string(datetime, Mobilizon.Cldr, format: :medium, locale: locale) do
|
||||
string
|
||||
end
|
||||
end
|
||||
end
|
||||
40
lib/web/views/error_helpers.ex
Normal file
40
lib/web/views/error_helpers.ex
Normal file
@@ -0,0 +1,40 @@
|
||||
defmodule Mobilizon.Web.ErrorHelpers do
|
||||
@moduledoc """
|
||||
Conveniences for translating and building error messages.
|
||||
"""
|
||||
|
||||
use Phoenix.HTML
|
||||
|
||||
@doc """
|
||||
Generates tag for inlined form input errors.
|
||||
"""
|
||||
def error_tag(form, field) do
|
||||
Enum.map(Keyword.get_values(form.errors, field), fn error ->
|
||||
content_tag(:span, translate_error(error), class: "help-block")
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Translates an error message using gettext.
|
||||
"""
|
||||
def translate_error({msg, opts}) do
|
||||
# Because error messages were defined within Ecto, we must
|
||||
# call the Gettext module passing our Gettext backend. We
|
||||
# also use the "errors" domain as translations are placed
|
||||
# in the errors.po file.
|
||||
# Ecto will pass the :count keyword if the error message is
|
||||
# meant to be pluralized.
|
||||
# On your own code and templates, depending on whether you
|
||||
# need the message to be pluralized or not, this could be
|
||||
# written simply as:
|
||||
#
|
||||
# dngettext "errors", "1 file", "%{count} files", count
|
||||
# dgettext "errors", "is invalid"
|
||||
#
|
||||
if count = opts[:count] do
|
||||
Gettext.dngettext(Mobilizon.Web.Gettext, "errors", msg, msg, count, opts)
|
||||
else
|
||||
Gettext.dgettext(Mobilizon.Web.Gettext, "errors", msg, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
55
lib/web/views/error_view.ex
Normal file
55
lib/web/views/error_view.ex
Normal file
@@ -0,0 +1,55 @@
|
||||
defmodule Mobilizon.Web.ErrorView do
|
||||
@moduledoc """
|
||||
View for errors
|
||||
"""
|
||||
use Mobilizon.Web, :view
|
||||
|
||||
def render("404.html", _assigns) do
|
||||
with {:ok, index_content} <- File.read(index_file_path()) do
|
||||
{:safe, index_content}
|
||||
end
|
||||
end
|
||||
|
||||
def render("404.json", _assigns) do
|
||||
%{msg: "Resource not found"}
|
||||
end
|
||||
|
||||
def render("404.activity-json", _assigns) do
|
||||
%{msg: "Resource not found"}
|
||||
end
|
||||
|
||||
def render("404.ics", _assigns) do
|
||||
"Bad feed"
|
||||
end
|
||||
|
||||
def render("404.atom", _assigns) do
|
||||
"Bad feed"
|
||||
end
|
||||
|
||||
def render("invalid_request.json", _assigns) do
|
||||
%{errors: "Invalid request"}
|
||||
end
|
||||
|
||||
def render("not_found.json", %{details: details}) do
|
||||
%{
|
||||
msg: "Resource not found",
|
||||
details: details
|
||||
}
|
||||
end
|
||||
|
||||
def render("500.html", _assigns) do
|
||||
"Internal server error"
|
||||
end
|
||||
|
||||
# In case no render clause matches or no
|
||||
# template is found, let's render it as 500
|
||||
def template_not_found(template, assigns) do
|
||||
require Logger
|
||||
Logger.warn("Template #{inspect(template)} not found")
|
||||
render("500.html", assigns)
|
||||
end
|
||||
|
||||
defp index_file_path() do
|
||||
Path.join(Application.app_dir(:mobilizon, "priv/static"), "index.html")
|
||||
end
|
||||
end
|
||||
65
lib/web/views/json_ld/object_view.ex
Normal file
65
lib/web/views/json_ld/object_view.ex
Normal file
@@ -0,0 +1,65 @@
|
||||
defmodule Mobilizon.Web.JsonLD.ObjectView do
|
||||
use Mobilizon.Web, :view
|
||||
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Addresses.Address
|
||||
alias Mobilizon.Events.Event
|
||||
|
||||
alias Mobilizon.Web.JsonLD.ObjectView
|
||||
alias Mobilizon.Web.MediaProxy
|
||||
|
||||
def render("event.json", %{event: %Event{} = event}) do
|
||||
# TODO: event.description is actually markdown!
|
||||
|
||||
json_ld = %{
|
||||
"@context" => "https://schema.org",
|
||||
"@type" => "Event",
|
||||
"name" => event.title,
|
||||
"description" => event.description,
|
||||
"performer" => %{
|
||||
"@type" =>
|
||||
if(event.organizer_actor.type == :Group, do: "PerformingGroup", else: "Person"),
|
||||
"name" => Actor.display_name(event.organizer_actor)
|
||||
},
|
||||
"location" => render_one(event.physical_address, ObjectView, "place.json", as: :address)
|
||||
}
|
||||
|
||||
json_ld =
|
||||
if event.picture do
|
||||
Map.put(json_ld, "image", [
|
||||
event.picture.file.url |> MediaProxy.url()
|
||||
])
|
||||
else
|
||||
json_ld
|
||||
end
|
||||
|
||||
json_ld =
|
||||
if event.begins_on,
|
||||
do: Map.put(json_ld, "startDate", DateTime.to_iso8601(event.begins_on)),
|
||||
else: json_ld
|
||||
|
||||
json_ld =
|
||||
if event.ends_on,
|
||||
do: Map.put(json_ld, "endDate", DateTime.to_iso8601(event.ends_on)),
|
||||
else: json_ld
|
||||
|
||||
json_ld
|
||||
end
|
||||
|
||||
def render("place.json", %{address: %Address{} = address}) do
|
||||
%{
|
||||
"@type" => "Place",
|
||||
"name" => address.description,
|
||||
"address" => %{
|
||||
"@type" => "PostalAddress",
|
||||
"streetAddress" => address.street,
|
||||
"addressLocality" => address.locality,
|
||||
"postalCode" => address.postal_code,
|
||||
"addressRegion" => address.region,
|
||||
"addressCountry" => address.country
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def render("place.json", nil), do: %{}
|
||||
end
|
||||
3
lib/web/views/layout_view.ex
Normal file
3
lib/web/views/layout_view.ex
Normal file
@@ -0,0 +1,3 @@
|
||||
defmodule Mobilizon.Web.LayoutView do
|
||||
use Mobilizon.Web, :view
|
||||
end
|
||||
74
lib/web/views/page_view.ex
Normal file
74
lib/web/views/page_view.ex
Normal file
@@ -0,0 +1,74 @@
|
||||
defmodule Mobilizon.Web.PageView do
|
||||
@moduledoc """
|
||||
View for our webapp
|
||||
"""
|
||||
|
||||
use Mobilizon.Web, :view
|
||||
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events.{Comment, Event}
|
||||
alias Mobilizon.Tombstone
|
||||
|
||||
alias Mobilizon.Service.Metadata
|
||||
alias Mobilizon.Service.Metadata.Instance
|
||||
alias Mobilizon.Service.Metadata.Utils, as: MetadataUtils
|
||||
|
||||
alias Mobilizon.Federation.ActivityPub.Utils
|
||||
alias Mobilizon.Federation.ActivityStream.Convertible
|
||||
|
||||
def render("actor.activity-json", %{conn: %{assigns: %{object: %Actor{} = actor}}}) do
|
||||
actor
|
||||
|> Convertible.model_to_as()
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("event.activity-json", %{conn: %{assigns: %{object: %Event{} = event}}}) do
|
||||
event
|
||||
|> Convertible.model_to_as()
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("event.activity-json", %{conn: %{assigns: %{object: %Tombstone{} = event}}}) do
|
||||
event
|
||||
|> Convertible.model_to_as()
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render("comment.activity-json", %{conn: %{assigns: %{object: %Comment{} = comment}}}) do
|
||||
comment
|
||||
|> Convertible.model_to_as()
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
def render(page, %{object: object} = _assigns)
|
||||
when page in ["actor.html", "event.html", "comment.html"] do
|
||||
with {:ok, index_content} <- File.read(index_file_path()) do
|
||||
tags = object |> Metadata.build_tags() |> MetadataUtils.stringify_tags()
|
||||
|
||||
index_content = replace_meta(index_content, tags)
|
||||
|
||||
{:safe, index_content}
|
||||
end
|
||||
end
|
||||
|
||||
def render("index.html", _assigns) do
|
||||
with {:ok, index_content} <- File.read(index_file_path()) do
|
||||
tags = Instance.build_tags() |> MetadataUtils.stringify_tags()
|
||||
|
||||
index_content = replace_meta(index_content, tags)
|
||||
|
||||
{:safe, index_content}
|
||||
end
|
||||
end
|
||||
|
||||
defp index_file_path do
|
||||
Path.join(Application.app_dir(:mobilizon, "priv/static"), "index.html")
|
||||
end
|
||||
|
||||
# TODO: Find why it's different in dev/prod and during tests
|
||||
defp replace_meta(index_content, tags) do
|
||||
index_content
|
||||
|> String.replace("<meta name=\"server-injected-data\" />", tags)
|
||||
|> String.replace("<meta name=server-injected-data>", tags)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user