Add basic metadata to opengraph preview

Also refactor datetime & address utils

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2021-06-22 13:15:21 +02:00
parent eee2d63309
commit 8caf1e302b
9 changed files with 376 additions and 53 deletions

View File

@@ -5,6 +5,7 @@ defmodule Mobilizon.Cldr do
use Cldr,
locales: Application.get_env(:mobilizon, :cldr)[:locales],
add_fallback_locales: true,
gettext:
if(Application.fetch_env!(:mobilizon, :env) == :prod,
do: Mobilizon.Web.Gettext,

View File

@@ -0,0 +1,95 @@
defmodule Mobilizon.Service.Address do
@moduledoc """
Module to render an `Mobilizon.Addresses.Address` struct to a string
"""
alias Mobilizon.Addresses.Address, as: AddressModel
@type address :: %{name: String.t(), alternative_name: String.t()}
def render_address(%AddressModel{} = address) do
%{name: name, alternative_name: alternative_name} = render_names(address)
cond do
defined?(alternative_name) && defined?(name) ->
"#{name}, #{alternative_name}"
defined?(name) ->
name
defined?(alternative_name) ->
alternative_name
true ->
raise ArgumentError, message: "Invalid address"
end
end
@spec render_names(AddressModel.t()) :: address()
def render_names(%AddressModel{type: nil} = address) do
render_names(%AddressModel{address | type: "house"})
end
def render_names(%AddressModel{
type: type,
description: description,
postal_code: postal_code,
locality: locality,
country: country
})
when type in ["house", "street", "secondary"] do
%{
name: description,
alternative_name: [postal_code, locality, country] |> Enum.filter(& &1) |> Enum.join(", ")
}
end
def render_names(%AddressModel{
type: type,
description: description,
postal_code: postal_code,
locality: locality,
country: country
})
when type in ["zone", "city", "administrative"] do
%{
name: if(defined?(postal_code), do: "#{description} (#{postal_code})", else: description),
alternative_name:
[locality, country]
|> Enum.filter(& &1)
|> Enum.filter(&(&1 != description))
|> Enum.join(", ")
}
end
def render_names(%AddressModel{
description: description,
street: street,
region: region,
locality: locality,
country: country
}) do
alternative_name =
cond do
defined?(street) ->
if defined?(locality), do: "#{street} (#{locality})", else: street
defined?(locality) ->
"#{locality}, #{region}, #{country}"
defined?(region) ->
"#{region}, #{country}"
defined?(country) ->
country
true ->
nil
end
%{name: description, alternative_name: alternative_name}
end
defp defined?(string) when is_binary(string), do: String.trim(string) != ""
defp defined?(_), do: false
end

View File

@@ -0,0 +1,41 @@
defmodule Mobilizon.Service.DateTime do
@moduledoc """
Module to represent a datetime in a given locale
"""
alias Cldr.DateTime.Relative
def datetime_to_string(%DateTime{} = datetime, locale \\ "en", format \\ :medium) do
Mobilizon.Cldr.DateTime.to_string!(datetime, format: format, locale: locale_or_default(locale))
end
def datetime_to_time_string(%DateTime{} = datetime, locale \\ "en", format \\ :short) do
Mobilizon.Cldr.Time.to_string!(datetime, format: format, locale: locale_or_default(locale))
end
@spec datetime_tz_convert(DateTime.t(), String.t()) :: DateTime.t()
def datetime_tz_convert(%DateTime{} = datetime, timezone) do
case DateTime.shift_zone(datetime, timezone) do
{:ok, datetime_with_user_tz} ->
datetime_with_user_tz
_ ->
datetime
end
end
@spec datetime_relative(DateTime.t(), String.t()) :: String.t()
def datetime_relative(%DateTime{} = datetime, locale \\ "en") do
Relative.to_string!(datetime, Mobilizon.Cldr,
relative_to: DateTime.utc_now(),
locale: locale_or_default(locale)
)
end
defp locale_or_default(locale) do
if Mobilizon.Cldr.known_locale_name(locale) do
locale
else
"en"
end
end
end

View File

@@ -1,19 +1,22 @@
defimpl Mobilizon.Service.Metadata, for: Mobilizon.Events.Event do
alias Phoenix.HTML
alias Phoenix.HTML.Tag
alias Mobilizon.Addresses.Address
alias Mobilizon.Events.Event
alias Mobilizon.Web.JsonLD.ObjectView
import Mobilizon.Service.Metadata.Utils, only: [process_description: 2, strip_tags: 1]
import Mobilizon.Service.Metadata.Utils,
only: [process_description: 2, strip_tags: 1, datetime_to_string: 2, render_address: 1]
def build_tags(%Event{} = event, locale \\ "en") do
event = Map.put(event, :description, process_description(event.description, locale))
formatted_description = description(event, locale)
tags = [
Tag.content_tag(:title, event.title <> " - Mobilizon"),
Tag.tag(:meta, name: "description", content: event.description),
Tag.tag(:meta, name: "description", content: process_description(event.description, locale)),
Tag.tag(:meta, property: "og:title", content: event.title),
Tag.tag(:meta, property: "og:url", content: event.url),
Tag.tag(:meta, property: "og:description", content: event.description),
Tag.tag(:meta, property: "og:description", content: formatted_description),
Tag.tag(:meta, property: "og:type", content: "website"),
# Tell Search Engines what's the origin
Tag.tag(:link, rel: "canonical", href: event.url)
@@ -45,4 +48,25 @@ defimpl Mobilizon.Service.Metadata, for: Mobilizon.Events.Event do
|> ObjectView.render(%{event: %{event | title: strip_tags(title)}})
|> Jason.encode!()
end
defp description(
%Event{
description: description,
begins_on: begins_on,
physical_address: %Address{} = address
},
locale
) do
"#{datetime_to_string(begins_on, locale)} - #{render_address(address)} - #{process_description(description, locale)}"
end
defp description(
%Event{
description: description,
begins_on: begins_on
},
locale
) do
"#{datetime_to_string(begins_on, locale)} - #{process_description(description, locale)}"
end
end

View File

@@ -3,6 +3,7 @@ defmodule Mobilizon.Service.Metadata.Utils do
Tools to convert tags to string.
"""
alias Mobilizon.Service.{Address, DateTime}
alias Mobilizon.Service.Formatter.HTML, as: HTMLFormatter
alias Phoenix.HTML
import Mobilizon.Web.Gettext
@@ -52,6 +53,9 @@ defmodule Mobilizon.Service.Metadata.Utils do
gettext("The event organizer didn't add any description.")
end
defdelegate datetime_to_string(datetime, locale \\ "en", format \\ :medium), to: DateTime
defdelegate render_address(address), to: Address
defp maybe_slice(description, limit) do
if String.length(description) > limit do
description

View File

@@ -1,39 +1,15 @@
defmodule Mobilizon.Web.EmailView do
use Mobilizon.Web, :view
alias Cldr.DateTime.Relative
alias Mobilizon.Service.DateTime, as: DateTimeRenderer
import Mobilizon.Web.Gettext
def datetime_to_string(%DateTime{} = datetime, locale \\ "en", format \\ :medium) do
with {:ok, string} <-
Mobilizon.Cldr.DateTime.to_string(datetime, format: format, locale: locale) do
string
end
end
defdelegate datetime_to_string(datetime, locale \\ "en", format \\ :medium),
to: DateTimeRenderer
def datetime_to_time_string(%DateTime{} = datetime, locale \\ "en", format \\ :hm) do
with {:ok, string} <-
Mobilizon.Cldr.DateTime.to_string(datetime, format: format, locale: locale) do
string
end
end
defdelegate datetime_to_time_string(datetime, locale \\ "en", format \\ :short),
to: DateTimeRenderer
@spec datetime_tz_convert(DateTime.t(), String.t()) :: DateTime.t()
def datetime_tz_convert(%DateTime{} = datetime, timezone) do
case DateTime.shift_zone(datetime, timezone) do
{:ok, datetime_with_user_tz} ->
datetime_with_user_tz
_ ->
datetime
end
end
@spec datetime_relative(DateTime.t(), String.t()) :: String.t()
def datetime_relative(%DateTime{} = datetime, locale \\ "en") do
Relative.to_string!(datetime, Mobilizon.Cldr,
relative_to: DateTime.utc_now(),
locale: locale
)
end
defdelegate datetime_tz_convert(datetime, timezone), to: DateTimeRenderer
defdelegate datetime_relative(datetime, locale \\ "en"), to: DateTimeRenderer
end