Add cached RSS feeds for actors endpoints
This commit is contained in:
parent
976186a18d
commit
02d1cea2d7
|
@ -354,6 +354,9 @@ defmodule Mobilizon.Actors.Actor do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Return the preferred_username with the eventual @domain suffix if it's a distant actor
|
||||
"""
|
||||
@spec actor_acct_from_actor(struct()) :: String.t()
|
||||
def actor_acct_from_actor(%Actor{preferred_username: preferred_username, domain: domain}) do
|
||||
if is_nil(domain) do
|
||||
|
@ -362,4 +365,16 @@ defmodule Mobilizon.Actors.Actor do
|
|||
"#{preferred_username}@#{domain}"
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the display name if available, or the preferred_username (with the eventual @domain suffix if it's a distant actor).
|
||||
"""
|
||||
@spec display_name(struct()) :: String.t()
|
||||
def display_name(%Actor{name: name} = actor) do
|
||||
case name do
|
||||
nil -> actor_acct_from_actor(actor)
|
||||
"" -> actor_acct_from_actor(actor)
|
||||
name -> name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -612,67 +612,9 @@ defmodule Mobilizon.Actors do
|
|||
with {:ok, %User{} = user} <-
|
||||
%User{} |> User.registration_changeset(args) |> Mobilizon.Repo.insert() do
|
||||
{:ok, user}
|
||||
# else
|
||||
# {:error, %Ecto.Changeset{} = changeset} ->
|
||||
# {:error, Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
|
||||
# Enum.reduce(opts, msg, fn {key, value}, acc ->
|
||||
# String.replace(acc, "%{#{key}}", to_string(value))
|
||||
# end)
|
||||
# end)}
|
||||
end
|
||||
end
|
||||
|
||||
# @spec register(map()) :: {:ok, Actor.t()} | {:error, String.t()}
|
||||
# def register(%{email: email, password: password, username: username}) do
|
||||
# with avatar <- gravatar(email),
|
||||
# user_changeset <-
|
||||
# User.registration_changeset(%User{}, %{
|
||||
# email: email,
|
||||
# password: password,
|
||||
# default_actor: %{
|
||||
# preferred_username: username,
|
||||
# domain: nil,
|
||||
# keys: create_keys(),
|
||||
# avatar_url: avatar
|
||||
# }
|
||||
# }),
|
||||
# {:ok, %User{default_actor: %Actor{} = actor, id: user_id} = user} <-
|
||||
# Mobilizon.Repo.insert(user_changeset),
|
||||
# {:ok, %Actor{} = _actor} <- update_actor(actor, %{user_id: user_id}) do
|
||||
# {:ok, Repo.preload(user, [:actors])}
|
||||
# else
|
||||
# {:error, %Ecto.Changeset{} = changeset} ->
|
||||
# handle_actor_user_changeset(changeset)
|
||||
# end
|
||||
# end
|
||||
|
||||
# @spec handle_actor_user_changeset(Ecto.Changeset.t()) :: {:error, String.t()}
|
||||
# defp handle_actor_user_changeset(changeset) do
|
||||
# changeset =
|
||||
# Ecto.Changeset.traverse_errors(changeset, fn
|
||||
# {msg, _opts} -> msg
|
||||
# msg -> msg
|
||||
# end)
|
||||
|
||||
# email_msg = Map.get(changeset, :email) || [:empty_email]
|
||||
# {:error, hd(email_msg)}
|
||||
# end
|
||||
|
||||
# @spec gravatar(String.t()) :: String.t() | nil
|
||||
# defp gravatar(nil), do: nil
|
||||
|
||||
# defp gravatar(email) do
|
||||
# avatar_url = gravatar_url(email, default: "404")
|
||||
|
||||
# case HTTPoison.get(avatar_url) do
|
||||
# {:ok, %HTTPoison.Response{status_code: 200}} ->
|
||||
# avatar_url
|
||||
|
||||
# _ ->
|
||||
# nil
|
||||
# end
|
||||
# end
|
||||
|
||||
@doc """
|
||||
Create a new person actor
|
||||
"""
|
||||
|
|
|
@ -17,6 +17,7 @@ defmodule Mobilizon.Application do
|
|||
supervisor(MobilizonWeb.Endpoint, []),
|
||||
# Start your own worker by calling: Mobilizon.Worker.start_link(arg1, arg2, arg3)
|
||||
# worker(Mobilizon.Worker, [arg1, arg2, arg3]),
|
||||
worker(Cachex, [:mobilizon, []]),
|
||||
worker(Guardian.DB.Token.SweeperServer, []),
|
||||
worker(Mobilizon.Service.Federator, [])
|
||||
]
|
||||
|
|
|
@ -21,6 +21,10 @@ defmodule MobilizonWeb.ActivityPubController do
|
|||
"application/activity+json, application/ld+json"
|
||||
]
|
||||
|
||||
def actor(conn, %{"name" => _name, "_format" => "atom"} = params) do
|
||||
MobilizonWeb.FeedController.actor(conn, params)
|
||||
end
|
||||
|
||||
def actor(conn, %{"name" => name}) do
|
||||
with %Actor{} = actor <- Actors.get_local_actor_by_name(name) do
|
||||
if conn |> get_req_header("accept") |> is_ap_header() do
|
||||
|
|
105
lib/mobilizon_web/controllers/feed_controller.ex
Normal file
105
lib/mobilizon_web/controllers/feed_controller.ex
Normal file
|
@ -0,0 +1,105 @@
|
|||
defmodule MobilizonWeb.FeedController do
|
||||
@moduledoc """
|
||||
Controller to serve RSS, ATOM and iCal Feeds
|
||||
"""
|
||||
use MobilizonWeb, :controller
|
||||
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events
|
||||
alias Mobilizon.Events.Event
|
||||
alias Atomex.{Feed, Entry}
|
||||
import MobilizonWeb.Gettext
|
||||
|
||||
@version Mix.Project.config()[:version]
|
||||
def version(), do: @version
|
||||
|
||||
def actor(conn, %{"name" => name, "_format" => format}) when format in ["atom"] do
|
||||
name = String.replace_suffix(name, ".atom", "")
|
||||
|
||||
with {status, data} when status in [:ok, :commit] <-
|
||||
Cachex.fetch(:mobilizon, "actor_" <> format <> "_" <> name, &create_cache/1) do
|
||||
conn
|
||||
|> put_resp_content_type("application/atom+xml")
|
||||
|> send_resp(200, data)
|
||||
else
|
||||
_err ->
|
||||
send_resp(conn, 404, "Not found")
|
||||
end
|
||||
end
|
||||
|
||||
@spec create_cache(String.t()) :: {:commit, String.t()} | {:ignore, any()}
|
||||
defp create_cache(key) do
|
||||
with ["actor", type, name] <- String.split(key, "_", parts: 3),
|
||||
{:ok, res} <- fetch_actor_event_feed(type, name) do
|
||||
{:commit, res}
|
||||
else
|
||||
err ->
|
||||
{:ignore, err}
|
||||
end
|
||||
end
|
||||
|
||||
@spec fetch_actor_event_feed(String.t(), String.t()) :: String.t()
|
||||
defp fetch_actor_event_feed(type, name) do
|
||||
with %Actor{} = actor <- Actors.get_local_actor_by_name(name),
|
||||
{:ok, events, _count} <- Events.get_public_events_for_actor(actor) do
|
||||
{:ok, build_actor_feed(actor, events, type)}
|
||||
else
|
||||
err ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
@spec build_actor_feed(Actor.t(), list(), String.t()) :: String.t()
|
||||
defp build_actor_feed(%Actor{} = actor, events, type) do
|
||||
display_name = Actor.display_name(actor)
|
||||
|
||||
# Title uses default instance language
|
||||
feed =
|
||||
Feed.new(
|
||||
actor.url <> ".rss",
|
||||
DateTime.utc_now(),
|
||||
gettext("%{actor}'s public events feed", actor: display_name)
|
||||
)
|
||||
|> Feed.author(display_name, uri: actor.url)
|
||||
|> Feed.link(actor.url <> "." <> type, rel: "self")
|
||||
|> Feed.link(actor.url, rel: "alternate")
|
||||
|> Feed.generator("Mobilizon", uri: "https://joinmobilizon.org", version: version())
|
||||
|> Feed.entries(Enum.map(events, &get_entry/1))
|
||||
|
||||
feed = if actor.avatar_url, do: Feed.icon(feed, actor.avatar_url), else: feed
|
||||
|
||||
feed =
|
||||
if actor.banner_url,
|
||||
do: Feed.logo(feed, actor.banner_url),
|
||||
else: feed
|
||||
|
||||
feed
|
||||
|> Feed.build()
|
||||
|> Atomex.generate_document()
|
||||
end
|
||||
|
||||
defp get_entry(%Event{} = event) do
|
||||
with {:ok, html, []} <- Earmark.as_html(event.description) do
|
||||
entry =
|
||||
Entry.new(event.url, event.inserted_at, event.title)
|
||||
|> Entry.link(event.url, rel: "alternate", type: "text/html")
|
||||
|> Entry.content({:cdata, html}, type: "html")
|
||||
|
||||
entry = if event.publish_at, do: Entry.published(entry, event.publish_at), else: entry
|
||||
|
||||
# Add tags
|
||||
entry =
|
||||
event.tags
|
||||
|> Enum.map(& &1.title)
|
||||
|> Enum.uniq()
|
||||
|> Enum.reduce(entry, fn tag, acc -> Entry.category(acc, tag) end)
|
||||
|
||||
Entry.build(entry)
|
||||
else
|
||||
{:error, _html, error_messages} ->
|
||||
require Logger
|
||||
Logger.error("Unable to produce HTML for Markdown", details: inspect(error_messages))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,6 +22,11 @@ defmodule MobilizonWeb.Router do
|
|||
plug(:accepts, ["activity-json", "html"])
|
||||
end
|
||||
|
||||
pipeline :activity_pub_rss do
|
||||
plug(TrailingFormatPlug)
|
||||
plug(:accepts, ["activity-json", "html", "atom"])
|
||||
end
|
||||
|
||||
pipeline :browser do
|
||||
plug(:accepts, ["html"])
|
||||
plug(:fetch_session)
|
||||
|
@ -52,9 +57,14 @@ defmodule MobilizonWeb.Router do
|
|||
end
|
||||
|
||||
scope "/", MobilizonWeb do
|
||||
pipe_through(:activity_pub)
|
||||
pipe_through(:activity_pub_rss)
|
||||
|
||||
get("/@:name", ActivityPubController, :actor)
|
||||
end
|
||||
|
||||
scope "/", MobilizonWeb do
|
||||
pipe_through(:activity_pub)
|
||||
|
||||
get("/@:name/outbox", ActivityPubController, :outbox)
|
||||
get("/@:name/following", ActivityPubController, :following)
|
||||
get("/@:name/followers", ActivityPubController, :followers)
|
||||
|
|
9
mix.exs
9
mix.exs
|
@ -35,7 +35,7 @@ defmodule Mobilizon.Mixfile do
|
|||
def application do
|
||||
[
|
||||
mod: {Mobilizon.Application, []},
|
||||
extra_applications: [:logger, :runtime_tools, :guardian, :bamboo, :geolix, :crypto]
|
||||
extra_applications: [:logger, :runtime_tools, :guardian, :bamboo, :geolix, :crypto, :cachex]
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -85,6 +85,10 @@ defmodule Mobilizon.Mixfile do
|
|||
{:arc, "~> 0.11.0"},
|
||||
{:arc_ecto, "~> 0.11.0"},
|
||||
{:plug_cowboy, "~> 2.0"},
|
||||
{:atomex, "0.3.0"},
|
||||
{:cachex, "~> 3.1"},
|
||||
{:trailing_format_plug, "~> 0.0.5"},
|
||||
{:earmark, "~> 1.3.1"},
|
||||
# Dev and test dependencies
|
||||
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
||||
{:ex_machina, "~> 2.2", only: [:dev, :test]},
|
||||
|
@ -95,7 +99,8 @@ defmodule Mobilizon.Mixfile do
|
|||
{:dialyxir, "~> 1.0.0-rc.4", only: [:dev], runtime: false},
|
||||
{:exvcr, "~> 0.10", only: :test},
|
||||
{:credo, "~> 1.0.0", only: [:dev, :test], runtime: false},
|
||||
{:mock, "~> 0.3.0", only: :test}
|
||||
{:mock, "~> 0.3.0", only: :test},
|
||||
{:feeder_ex, "~> 1.1", only: :test}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
10
mix.lock
10
mix.lock
|
@ -6,10 +6,12 @@
|
|||
"arc": {:hex, :arc, "0.11.0", "ac7a0cc03035317b6fef9fe94c97d7d9bd183a3e7ce1606aa0c175cfa8d1ba6d", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:ex_aws_s3, "~> 2.0", [hex: :ex_aws_s3, repo: "hexpm", optional: true]}, {:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"arc_ecto": {:hex, :arc_ecto, "0.11.1", "27aedf8c236b2097eed09d96f4ae73b43eb4c042a0e2ae42d44bf644cf16115c", [:mix], [{:arc, "~> 0.11.0", [hex: :arc, repo: "hexpm", optional: false]}, {:ecto, "~> 2.1 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"argon2_elixir": {:hex, :argon2_elixir, "2.0.0", "e3539f441930d4c8296e36024168526626351c1f2c2df97cfd50f4e90b15386a", [:make, :mix], [{:comeonin, "~> 5.0", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"atomex": {:hex, :atomex, "0.3.0", "19b5d1a2aef8706dbd307385f7d5d9f6f273869226d317492c396c7bacf26402", [:mix], [{:xml_builder, "~> 2.0.0", [hex: :xml_builder, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"bamboo": {:hex, :bamboo, "1.2.0", "8aebd24f7c606c32d0163c398004a11608ca1028182a169b2e527793bfab7561", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"bamboo_smtp": {:hex, :bamboo_smtp, "1.6.0", "0a3607b77f22554af58c547350c1c73ebba6f4fb2c4bd0b11713ab5b4081588f", [:mix], [{:bamboo, "~> 1.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 0.12.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||
"cachex": {:hex, :cachex, "3.1.3", "86ed0669ea4b2f3e3982dbb5c6ca9e0964e46738e572c9156f22ceb75f57c336", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"},
|
||||
"comeonin": {:hex, :comeonin, "5.0.0", "e87716d3b1c31e56312f6a1545a5548cdc80376cff5025fe3b12be2046934837", [:mix], [], "hexpm"},
|
||||
|
@ -29,6 +31,7 @@
|
|||
"ecto_sql": {:hex, :ecto_sql, "3.0.5", "7e44172b4f7aca4469f38d7f6a3da394dbf43a1bcf0ca975e958cb957becd74e", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0.6", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.3.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.5.2", "96a28c79f5b8d34879cd95ebc04d2a0d678cfbbd3e74c43cb63a76adf0ee8054", [:mix], [], "hexpm"},
|
||||
"erlex": {:hex, :erlex, "0.2.1", "cee02918660807cbba9a7229cae9b42d1c6143b768c781fa6cee1eaf03ad860b", [:mix], [], "hexpm"},
|
||||
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
|
||||
"ex_crypto": {:hex, :ex_crypto, "0.10.0", "af600a89b784b36613a989da6e998c1b200ff1214c3cfbaf8deca4aa2f0a1739", [:mix], [{:poison, ">= 2.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_ical": {:hex, :ex_ical, "0.2.0", "4b928b554614704016cc0c9ee226eb854da9327a1cc460457621ceacb1ac29a6", [:mix], [{:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -39,6 +42,8 @@
|
|||
"exgravatar": {:hex, :exgravatar, "2.0.1", "66d595c7d63dd6bbac442c5542a724375ae29144059c6fe093e61553850aace4", [:mix], [], "hexpm"},
|
||||
"exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"exvcr": {:hex, :exvcr, "0.10.3", "1ae3b97560430acfa88ebc737c85b2b7a9dbacd8a2b26789a19718b51ae3522c", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"feeder": {:hex, :feeder, "2.2.4", "56ec535cf2f79719bc53b5c2abe5f6cf481fc01e5ae6229ab7cc829644f039ec", [:make], [], "hexpm"},
|
||||
"feeder_ex": {:hex, :feeder_ex, "1.1.0", "0be3732255cdb45dec949e0ede6852b5261c9ff173360e8274a6ac65183b2b55", [:mix], [{:feeder, "~> 2.2", [hex: :feeder, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"},
|
||||
"gen_smtp": {:hex, :gen_smtp, "0.12.0", "97d44903f5ca18ca85cb39aee7d9c77e98d79804bbdef56078adcf905cb2ef00", [:rebar3], [], "hexpm"},
|
||||
"geo": {:hex, :geo, "3.1.0", "727e005262430d037e870ff364e65d80ca5ca21d5ac8eddd57a1ada72c3f83b0", [:mix], [], "hexpm"},
|
||||
|
@ -56,6 +61,7 @@
|
|||
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"json_ld": {:hex, :json_ld, "0.3.0", "92f508ca831b9e4530e3e6c950976fdafcf26323e6817c325b3e1ee78affc4bd", [:mix], [{:jason, "~> 1.1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:rdf, "~> 0.5", [hex: :rdf, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"},
|
||||
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm"},
|
||||
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"},
|
||||
|
@ -81,10 +87,14 @@
|
|||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||
"rdf": {:hex, :rdf, "0.5.4", "57e09d4adfe7646fe0c3514b703b76eaf29d537b250b36abae75e66d7e5920cf", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"rsa_ex": {:hex, :rsa_ex, "0.4.0", "e28dd7dc5236e156df434af0e4aa822384c8866c928e17b785d4edb7c253b558", [:mix], [], "hexpm"},
|
||||
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm"},
|
||||
"slugger": {:hex, :slugger, "0.3.0", "efc667ab99eee19a48913ccf3d038b1fb9f165fa4fbf093be898b8099e61b6ed", [:mix], [], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"telemetry": {:hex, :telemetry, "0.3.0", "099a7f3ce31e4780f971b4630a3c22ec66d22208bc090fe33a2a3a6a67754a73", [:rebar3], [], "hexpm"},
|
||||
"timex": {:hex, :timex, "3.5.0", "b0a23167da02d0fe4f1a4e104d1f929a00d348502b52432c05de875d0b9cffa5", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
|
||||
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm"},
|
||||
"xml_builder": {:hex, :xml_builder, "2.0.0", "371ed27bb63bf0598dbaf3f0c466e5dc7d16cb4ecb68f06a67f953654062e21b", [:mix], [], "hexpm"},
|
||||
}
|
||||
|
|
59
priv/gettext/default.pot
Normal file
59
priv/gettext/default.pot
Normal file
|
@ -0,0 +1,59 @@
|
|||
## This file is a PO Template file.
|
||||
##
|
||||
## `msgid`s here are often extracted from source code.
|
||||
## Add new translations manually only if they're dynamic
|
||||
## translations that can't be statically extracted.
|
||||
##
|
||||
## Run `mix gettext.extract` to bring this file up to
|
||||
## date. Leave `msgstr`s empty as changing them here as no
|
||||
## effect: edit them in PO (`.po`) files instead.
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/controllers/feed_controller.ex:59
|
||||
msgid "%{actor}'s public events feed"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/email.html.eex:8
|
||||
#: lib/mobilizon_web/templates/email/email.text.eex:3
|
||||
msgid "An email sent by Mobilizon on %{instance}."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.html.eex:1
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.text.eex:1
|
||||
msgid "Confirm the email address"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:3
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:7
|
||||
msgid "If you didn't request this, please ignore this email. Your password won't change until you access the link below and create a new one."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon/email/user.ex:19
|
||||
msgid "Mobilizon: Confirmation instructions for %{instance}"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon/email/user.ex:34
|
||||
msgid "Mobilizon: Reset your password on %{instance} instructions"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:1
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:1
|
||||
msgid "Password reset"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.html.eex:2
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.text.eex:5
|
||||
msgid "You created an account on %{host} with this email address. You are one click away from activating it. If this wasn't you, please ignore this email."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:2
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:5
|
||||
msgid "You requested a new password for your account on %{host}."
|
||||
msgstr ""
|
63
priv/gettext/en/LC_MESSAGES/default.po
Normal file
63
priv/gettext/en/LC_MESSAGES/default.po
Normal file
|
@ -0,0 +1,63 @@
|
|||
## `msgid`s in this file come from POT (.pot) files.
|
||||
##
|
||||
## Do not add, change, or remove `msgid`s manually here as
|
||||
## they're tied to the ones in the corresponding POT file
|
||||
## (with the same domain).
|
||||
##
|
||||
## Use `mix gettext.extract --merge` or `mix gettext.merge`
|
||||
## to merge POT files into PO files.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language: en\n"
|
||||
"Plural-Forms: nplurals=2\n"
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/controllers/feed_controller.ex:59
|
||||
msgid "%{actor}'s public events feed"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/email.html.eex:8
|
||||
#: lib/mobilizon_web/templates/email/email.text.eex:3
|
||||
msgid "An email sent by Mobilizon on %{instance}."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.html.eex:1
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.text.eex:1
|
||||
msgid "Confirm the email address"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:3
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:7
|
||||
msgid "If you didn't request this, please ignore this email. Your password won't change until you access the link below and create a new one."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon/email/user.ex:19
|
||||
msgid "Mobilizon: Confirmation instructions for %{instance}"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon/email/user.ex:34
|
||||
msgid "Mobilizon: Reset your password on %{instance} instructions"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:1
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:1
|
||||
msgid "Password reset"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.html.eex:2
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.text.eex:5
|
||||
msgid "You created an account on %{host} with this email address. You are one click away from activating it. If this wasn't you, please ignore this email."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:2
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:5
|
||||
msgid "You requested a new password for your account on %{host}."
|
||||
msgstr ""
|
|
@ -7,7 +7,6 @@
|
|||
## Run `mix gettext.extract` to bring this file up to
|
||||
## date. Leave `msgstr`s empty as changing them here as no
|
||||
## effect: edit them in PO (`.po`) files instead.
|
||||
|
||||
## From Ecto.Changeset.cast/4
|
||||
msgid "can't be blank"
|
||||
msgstr ""
|
||||
|
|
63
priv/gettext/fr_FR/LC_MESSAGES/default.po
Normal file
63
priv/gettext/fr_FR/LC_MESSAGES/default.po
Normal file
|
@ -0,0 +1,63 @@
|
|||
## `msgid`s in this file come from POT (.pot) files.
|
||||
##
|
||||
## Do not add, change, or remove `msgid`s manually here as
|
||||
## they're tied to the ones in the corresponding POT file
|
||||
## (with the same domain).
|
||||
##
|
||||
## Use `mix gettext.extract --merge` or `mix gettext.merge`
|
||||
## to merge POT files into PO files.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language: fr_FR\n"
|
||||
"Plural-Forms: nplurals=2\n"
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/controllers/feed_controller.ex:59
|
||||
msgid "%{actor}'s public events feed"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/email.html.eex:8
|
||||
#: lib/mobilizon_web/templates/email/email.text.eex:3
|
||||
msgid "An email sent by Mobilizon on %{instance}."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.html.eex:1
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.text.eex:1
|
||||
msgid "Confirm the email address"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:3
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:7
|
||||
msgid "If you didn't request this, please ignore this email. Your password won't change until you access the link below and create a new one."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon/email/user.ex:19
|
||||
msgid "Mobilizon: Confirmation instructions for %{instance}"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon/email/user.ex:34
|
||||
msgid "Mobilizon: Reset your password on %{instance} instructions"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:1
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:1
|
||||
msgid "Password reset"
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.html.eex:2
|
||||
#: lib/mobilizon_web/templates/email/registration_confirmation.text.eex:5
|
||||
msgid "You created an account on %{host} with this email address. You are one click away from activating it. If this wasn't you, please ignore this email."
|
||||
msgstr ""
|
||||
|
||||
#, elixir-format
|
||||
#: lib/mobilizon_web/templates/email/password_reset.html.eex:2
|
||||
#: lib/mobilizon_web/templates/email/password_reset.text.eex:5
|
||||
msgid "You requested a new password for your account on %{host}."
|
||||
msgstr ""
|
87
priv/gettext/fr_FR/LC_MESSAGES/errors.po
Normal file
87
priv/gettext/fr_FR/LC_MESSAGES/errors.po
Normal file
|
@ -0,0 +1,87 @@
|
|||
## `msgid`s in this file come from POT (.pot) files.
|
||||
##
|
||||
## Do not add, change, or remove `msgid`s manually here as
|
||||
## they're tied to the ones in the corresponding POT file
|
||||
## (with the same domain).
|
||||
##
|
||||
## Use `mix gettext.extract --merge` or `mix gettext.merge`
|
||||
## to merge POT files into PO files.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language: fr_FR\n"
|
||||
"Plural-Forms: nplurals=2\n"
|
||||
|
||||
msgid "can't be blank"
|
||||
msgstr ""
|
||||
|
||||
msgid "has already been taken"
|
||||
msgstr ""
|
||||
|
||||
msgid "is invalid"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be accepted"
|
||||
msgstr ""
|
||||
|
||||
msgid "has invalid format"
|
||||
msgstr ""
|
||||
|
||||
msgid "has an invalid entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "is reserved"
|
||||
msgstr ""
|
||||
|
||||
msgid "does not match confirmation"
|
||||
msgstr ""
|
||||
|
||||
msgid "is still associated with this entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "are still associated with this entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "should be %{count} character(s)"
|
||||
msgid_plural "should be %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have %{count} item(s)"
|
||||
msgid_plural "should have %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should be at least %{count} character(s)"
|
||||
msgid_plural "should be at least %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have at least %{count} item(s)"
|
||||
msgid_plural "should have at least %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should be at most %{count} character(s)"
|
||||
msgid_plural "should be at most %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have at most %{count} item(s)"
|
||||
msgid_plural "should have at most %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "must be less than %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be greater than %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be less than or equal to %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be greater than or equal to %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be equal to %{number}"
|
||||
msgstr ""
|
37
test/mobilizon_web/controllers/feed_controller_test.exs
Normal file
37
test/mobilizon_web/controllers/feed_controller_test.exs
Normal file
|
@ -0,0 +1,37 @@
|
|||
defmodule MobilizonWeb.FeedControllerTest do
|
||||
use MobilizonWeb.ConnCase
|
||||
import Mobilizon.Factory
|
||||
|
||||
describe "/@:preferred_username.atom" do
|
||||
test "it returns an RSS representation of the actor's public events", %{conn: conn} do
|
||||
actor = insert(:actor)
|
||||
event1 = insert(:event, organizer_actor: actor)
|
||||
event2 = insert(:event, organizer_actor: actor)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/atom+xml")
|
||||
|> get("/@#{actor.preferred_username}.atom")
|
||||
|
||||
assert response(conn, 200) =~ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
assert response_content_type(conn, :xml) =~ "charset=utf-8"
|
||||
|
||||
{:ok, feed, _} = FeederEx.parse(conn.resp_body)
|
||||
|
||||
assert feed.title == actor.preferred_username <> "'s public events feed"
|
||||
|
||||
Enum.each(feed.entries, fn entry ->
|
||||
assert entry.title in [event1.title, event2.title]
|
||||
end)
|
||||
end
|
||||
|
||||
test "it doesn't return anything for an not existing actor", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/atom+xml")
|
||||
|> get("/@notexistent.atom")
|
||||
|
||||
assert response(conn, 404) == "Not found"
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue